/* * Copyright (C) 2003-2010 August Black * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "ReadMedia.h" #include #include //usleep static void *the_thread_dispatcher(void * xp); static void *the_thread_opener(void * xp); static void *the_audiofifo_filler(void * xp); static void *the_videofifo_filler(void * xp); ReadMedia::ReadMedia( ) { m_state = STATE_EMPTY; m_audio_frame = NULL; m_video_frame = NULL; m_aeof = true; m_veof = true; //m_atime = 0.0; //m_vtime = 0.0; m_video_stream_count =0; m_audio_stream_count =0; m_pcm_seek = SEEK_NOTHING; m_frame_seek = SEEK_NOTHING; m_length_in_seconds=0.0; m_length_in_gavltime = 0; m_num_frames=0; m_num_samples=0; m_video_format.frame_width=0; m_video_format.frame_height=0; m_video_format.image_width=0; m_video_format.image_height=0; m_video_format.pixel_width=0; m_video_format.pixel_height=0; m_video_format.pixelformat = GAVL_PIXELFORMAT_NONE ; m_video_format.frame_duration=0; m_video_format.timescale=0; m_video_format.framerate_mode=GAVL_FRAMERATE_CONSTANT; m_video_format.chroma_placement=GAVL_CHROMA_PLACEMENT_DEFAULT; m_video_format.interlace_mode=GAVL_INTERLACE_NONE; m_video_format.timecode_format.int_framerate =0; m_video_format.timecode_format.flags =0; m_audio_format.samples_per_frame = 0; m_audio_format.samplerate = 0; m_audio_format.num_channels = 0; m_audio_format.sample_format = GAVL_SAMPLE_NONE ; m_audio_format.interleave_mode = GAVL_INTERLEAVE_NONE; m_audio_format.center_level = 1.0; m_audio_format.rear_level = 1.0; m_audio_format.channel_locations[0] = GAVL_CHID_NONE; // Reset m_fifoaudio= NULL; // new FifoAudioFrames( frames_in_fifo , &tmp_audio_lormat); m_fifovideo= NULL; // new FifoVideoFrames( 12 , &output_video_format); m_audio_thread_ret = -1; m_video_thread_ret = -1; m_open_callback = NULL; m_open_callback_data = NULL; m_buffer_callback = NULL; m_buffer_callback_data = NULL; quit_av_threads = false; m_loop = false; sprintf(m_filename, "seinettbitte!"); //bgav stuff m_file = NULL; m_opt = bgav_options_create(); bgav_options_set_connect_timeout(m_opt, 5000); bgav_options_set_read_timeout(m_opt, 5000); bgav_options_set_network_bandwidth(m_opt, 524300); bgav_options_set_network_buffer_size(m_opt, 1024*12); bgav_options_set_http_shoutcast_metadata (m_opt, 1); // set up the reading so that we can seek sample accurately bgav_options_set_sample_accurate (m_opt, 1 ); pthread_cond_init(&m_cond_dispatch, 0); pthread_mutex_init(&m_condmut_dispatch, 0); pthread_cond_init(&m_cond_a, 0); pthread_cond_init(&m_cond_v, 0); pthread_mutex_init(&m_condmut_a, 0); pthread_mutex_init(&m_condmut_v, 0); pthread_mutex_init(&m_av_mut, 0); pthread_mutex_init(&m_state_mut, 0); // start the dispatcher thread m_cmd = CMD_START; m_dispatcher_thread_ret = pthread_create(&m_thread_dispatch, NULL, the_thread_dispatcher, (void *)this) ; if (m_dispatcher_thread_ret != 0 ) printf("error starting the readmedia dispatcher thread.\n"); while( getCommand() != CMD_NULL) signalDispatcher(); //printf("dispatcher ready ...\n"); } ReadMedia::~ReadMedia() { printf("killing the media..\n"); setCommand( CMD_QUIT ); signalDispatcher(); // signal dispatcher joins the opener and AV threads pthread_join( m_thread_dispatch, NULL); //printf("joined dispatcher\n"); if (m_audio_frame != NULL) { gavl_audio_frame_destroy(m_audio_frame); } if (m_video_frame != NULL) { gavl_video_frame_destroy(m_video_frame); } if (m_file != NULL) { bgav_close(m_file); } //printf("now, on to deleting fifo...\n"); if( m_fifoaudio != NULL) delete m_fifoaudio; if( m_fifovideo != NULL) delete m_fifovideo; // these are created only once bgav_options_destroy(m_opt); pthread_cond_destroy(&m_cond_dispatch); pthread_mutex_destroy(&m_condmut_dispatch); pthread_cond_destroy(&m_cond_a); pthread_cond_destroy(&m_cond_v); pthread_mutex_destroy(&m_condmut_a); pthread_mutex_destroy(&m_condmut_v); pthread_mutex_destroy(&m_av_mut); pthread_mutex_destroy(&m_state_mut); printf("killed the media..\n"); } int ReadMedia::decodeVideo( gavl_video_frame_t * vf ) { // check state first, if state is ready, we can check the other vars without locking lockState(); if (m_state != STATE_READY || m_video_stream_count < 1 || m_fifovideo == NULL ) { unlockState(); return -1; } if (!m_fifovideo->Get( vf ) ) { if ( m_veof ) { m_frame_seek = SEEK_NOTHING; unlockState(); signalV(); return 0; } else { //printf("Couldn't get a video frame, videofifo is %f full\n", m_fifovideo->getSizePercentage()); // this can only happen if the fifo is empty unlockState(); signalV(); return -1; // return with error } } //m_vtime = vf->timestamp / (double)m_video_format.timescale; unlockState(); signalV(); return 1 ; } int ReadMedia::decodeAudio( gavl_audio_frame_t * af ) { lockState(); if (m_state != STATE_READY || m_audio_stream_count < 1 || m_fifoaudio == NULL ) { unlockState(); return -1; } if ( !m_fifoaudio->Get( af ) ) { if ( m_aeof ) { m_pcm_seek = SEEK_NOTHING; unlockState(); signalA(); return 0; } else { printf("Couldn't get an audio frame, audiofifo is %f full.\n", m_fifoaudio->getSizePercentage()); // this can only happen if the fifo is empty unlockState(); signalA(); return -1; } } //m_atime = af->timestamp / (double)m_audio_format.samplerate; unlockState(); signalA(); return 1 ; } bool ReadMedia::rewind() { // NOTE!! Always check for stream count before setting aeof or veof lockState(); if ( m_state == STATE_READY && m_file != NULL) { //printf("ReadMedia::rewind(), valid_samples=%d\n", m_audio_frame->valid_samples); m_pcm_seek = SEEK_REWIND; if(m_audio_stream_count) m_aeof = false; //m_atime = 0; m_frame_seek=SEEK_REWIND; if (m_video_stream_count) m_veof = false; //m_vtime=0; unlockState(); if (m_audio_stream_count) { signalA(); // only signal one or the other here //printf("ReadMedia::rewind(), signaled audio\n"); // we are gong to flush here even though it is flushed // during the audio or video thread. This will ensure our fifo // is clean and empty. Otherwise, the audio thread may be waiting // at the bottom of its loop and not make it to the flush part even // though we signalled it. if (m_fifoaudio) m_fifoaudio->Flush(); } else if (m_video_stream_count) { signalV(); if (m_fifovideo) m_fifovideo->Flush(); } return true; } unlockState(); return false; } gavl_time_t ReadMedia::getLengthInGavlTime() { gavl_time_t time = 0; lockState(); time = m_length_in_gavltime; unlockState(); return time; } double ReadMedia::getLengthInSeconds() { double secs = 0.0; lockState(); secs = m_length_in_seconds; unlockState(); return secs; } int64_t ReadMedia::getLengthInAudioSamples() { int64_t samples = 0; lockState(); samples = m_num_samples; unlockState(); return samples; } int64_t ReadMedia::getLengthInVideoFrames() { int64_t frames = 0; lockState(); frames = m_num_frames; unlockState(); return frames; } bool ReadMedia::frameSeek( int64_t frames ) { lockState(); if (m_state == STATE_READY && m_file && bgav_can_seek( m_file ) && frames >= 0 && frames < m_num_frames ) { m_frame_seek = frames; unlockState(); signalAV(); return true; } else { m_frame_seek = SEEK_NOTHING; unlockState(); return false; } } // NOT PUBLIC int64_t ReadMedia::frameSeek() { int64_t tmp=SEEK_NOTHING; lockState(); tmp = m_frame_seek; m_frame_seek = SEEK_NOTHING; unlockState(); return tmp; } bool ReadMedia::pcmSeek( int64_t samples ) { lockState(); if (m_state == STATE_READY && m_file && bgav_can_seek( m_file) && samples >= 0 && samples < m_num_samples ) { m_pcm_seek = samples; unlockState(); signalAV(); return true; } else { m_pcm_seek = SEEK_NOTHING; unlockState(); return false; } } // NOT PUBLIC int64_t ReadMedia::pcmSeek() { int64_t tmp=SEEK_NOTHING; lockState(); tmp = m_pcm_seek; m_pcm_seek = SEEK_NOTHING; unlockState(); return tmp; } bool ReadMedia::timeSeek(double seconds) { gavl_time_t gt = gavl_seconds_to_time( seconds ) ; lockState(); if (m_state == STATE_READY && m_file && bgav_can_seek( m_file) && seconds >= 0.0 && seconds < m_length_in_seconds ) { if (m_audio_stream_count) { m_pcm_seek = gavl_time_to_samples(m_audio_format.samplerate, gt ); if (m_pcm_seek >= m_num_samples || m_pcm_seek < 0) m_pcm_seek = SEEK_NOTHING; unlockState(); signalAV(); return true; } else if ( m_video_stream_count && m_video_format.framerate_mode == GAVL_FRAMERATE_CONSTANT ) { m_frame_seek = gavl_time_to_frames( m_video_format.timescale, m_video_format.frame_duration, gt ); if (m_frame_seek >= m_num_frames || m_frame_seek < 0 ) m_frame_seek = SEEK_NOTHING; unlockState(); signalAV(); return true; } } unlockState(); return false; } bool ReadMedia::quitAVThreads() { bool b =false; lockState(); b = quit_av_threads; unlockState(); return b; } void ReadMedia::openFile( char * fn, int vsize, int asize, int spf) { lockState(); /* if ( strcmp(m_filename, fn) == 0 && m_state == STATE_READY) { printf("%s is already open for action. \n", m_filename); unlockState(); callOpenCallback(); return; } */ // signal the dispatcher that we want an new file m_audio_format.samples_per_frame = spf ; m_afifosize = asize; m_vfifosize = vsize; sprintf(m_filename, "%s", fn); m_cmd = CMD_OPEN ; unlockState(); signalDispatcher(); } void ReadMedia::copyAudioFormat(gavl_audio_format_t * dst ){ lockState(); //if (m_state == STATE_READY) gavl_audio_format_copy(dst, &m_audio_format); unlockState(); } void ReadMedia::copyVideoFormat(gavl_video_format_t * dst ){ lockState(); //if (m_state == STATE_READY) gavl_video_format_copy( dst, &m_video_format); unlockState(); } int ReadMedia::getAudioSamplerate() { int sr=0; lockState(); //if (m_state == STATE_READY ) sr = m_audio_format.samplerate; unlockState(); return sr; } int ReadMedia::getAudioChannelCount() { int ch=0; lockState(); //if (m_state == STATE_READY ) ch = m_audio_format.num_channels; unlockState(); return ch; } int ReadMedia::getVideoTimescale() { int t=0; lockState(); t = m_video_format.timescale; unlockState(); return t; } char * ReadMedia::getFilename() { return m_filename; } bgav_t * ReadMedia::getFile() {return m_file;} FifoAudioFrames * ReadMedia::getAudioFifo() { return m_fifoaudio; } FifoVideoFrames * ReadMedia::getVideoFifo() { return m_fifovideo; } gavl_audio_frame_t * ReadMedia::getAudioFrame() { return m_audio_frame;} gavl_video_frame_t * ReadMedia::getVideoFrame() { return m_video_frame;} void ReadMedia::setState(int b) { lockState(); m_state = b; unlockState(); } int ReadMedia::getState() { int s=STATE_EMPTY; lockState(); s = m_state; unlockState(); return s; } bool ReadMedia::isReady() { if ( getState() == STATE_READY) return true; else return false;} // no need to lock on these //double ReadMedia::getATimeInSeconds() { return m_atime;}; //double ReadMedia::getVTimeInSeconds() { return m_vtime;}; /* float ReadMedia::getTimeInSeconds() { lockState(); if (m_audio_stream_count > 0 ) { unlockState(); return m_atime; } else { // FIXME : see if the following is really true unlockState(); return m_vtime; } }; */ float ReadMedia::getAudioFifoSizePercentage() { float f=0.0; lockState(); if (m_fifoaudio) f = m_fifoaudio->getSizePercentage(); unlockState(); return f; } void ReadMedia::pealOffVideoFrames( int howmany) { lockAV(); if (m_fifovideo == NULL) { unlockAV(); return; } int max = howmany > m_fifovideo->getSize() ? m_fifovideo->getSize() : howmany; for (int i=0;i< max; i++) { m_fifovideo->Get(); //printf("pealing of a video frame size = %d\n", m_fifovideo->getSize()); } unlockAV(); } void ReadMedia::setAEOF(bool b) { lockState(); m_aeof = b; m_pcm_seek = SEEK_NOTHING; unlockState(); } void ReadMedia::setVEOF(bool b) { lockState(); m_veof = b; m_frame_seek = SEEK_NOTHING; unlockState(); } bool ReadMedia::getEOF() { bool tmp = true; lockState(); if (m_state == STATE_READY) tmp = (m_aeof && m_veof); unlockState(); //printf("ReadMedia:getEOF=%d, aeof=%d, veof=%d\n", tmp, m_aeof, m_veof); return tmp; } void ReadMedia::setLoop( bool b) { lockState(); m_loop = b; unlockState(); } bool ReadMedia::getLoop() { bool tmp = true; lockState(); if (m_file && bgav_can_seek( m_file) ) { tmp = m_loop; } else { tmp = false; // we can't loop on file, return false // but leave the loop var untouched } unlockState(); return tmp; } // NOT PUBILC // only used in fifo filler bool ReadMedia::getAEOF() { bool tmp = false; lockState(); tmp = m_aeof; unlockState(); return tmp; } // NOT PUBLIC bool ReadMedia::getVEOF() { bool tmp = false; lockState(); tmp = m_veof; unlockState(); return tmp; } int ReadMedia::getSamplesPerFrame() { int spf=0; lockState(); spf = m_audio_format.samples_per_frame; unlockState(); return spf; } int ReadMedia::lockAV() { //printf("locking AV\n"); return pthread_mutex_lock(&m_av_mut); } int ReadMedia::unlockAV() { //printf("unlocking AV\n"); return pthread_mutex_unlock(&m_av_mut); } int ReadMedia::lockState() { // printf("locking state.\n"); return pthread_mutex_lock(&m_state_mut); } int ReadMedia::unlockState() { //printf("unlocking state.\n"); return pthread_mutex_unlock(&m_state_mut); } void ReadMedia::waitA() { pthread_cond_wait( &m_cond_a, &m_condmut_a); } void ReadMedia::waitV() { pthread_cond_wait( &m_cond_v, &m_condmut_v); } void ReadMedia::signalAV() { signalA(); signalV(); } void ReadMedia::signalA() { pthread_cond_signal( &m_cond_a); } void ReadMedia::signalV() { pthread_cond_signal( &m_cond_v); } void ReadMedia::signalDispatcher() { pthread_cond_signal( &m_cond_dispatch); } void ReadMedia::waitDispatch() { pthread_cond_wait( &m_cond_dispatch, &m_condmut_dispatch); } void ReadMedia::setOpenCallback(void (*oc)(void *), void *v ) { lockState();// do we need this? m_open_callback = oc; m_open_callback_data = v; unlockState(); } void ReadMedia::callOpenCallback() { // do NOT lock on the callback, user must do call public functions // that also lock if(m_open_callback != NULL) m_open_callback( m_open_callback_data); }; void ReadMedia::setBufferCallback(bgav_buffer_callback bc, void *v ) { lockState(); m_buffer_callback = bc; m_buffer_callback_data = v; // set up callbacks. if (m_buffer_callback) { bgav_options_set_buffer_callback( m_opt, m_buffer_callback, m_buffer_callback_data); } unlockState(); }; int ReadMedia::getAudioStreamCount() { int asc=0; lockState(); asc = m_audio_stream_count; unlockState(); return asc; } int ReadMedia::getVideoStreamCount() { int vsc=0; lockState(); vsc = m_video_stream_count; unlockState(); return vsc; } // NOT PUBLIC void ReadMedia::setAudioStreamCount(int s){ lockState(); m_audio_stream_count=s; if (s == 0) // no audio streams, we are already at audio eof m_aeof = true; else m_aeof = false; unlockState(); } // NOT PUBLIC void ReadMedia::setVideoStreamCount(int s){ lockState(); m_video_stream_count=s; if (s==0) // if there are no video streams, video is at eof m_veof = true; else m_veof =false; unlockState(); } // NOT PUBLIC void ReadMedia::setCommand(int s){ lockState(); m_cmd = s; unlockState(); } // NOT PUBLIC int ReadMedia::getCommand() { int cmd= CMD_NULL; lockState(); cmd = m_cmd; unlockState(); return cmd; } // NOT PUBLIC // Clears the bgav_t struct, destroying and then creating it // NO NEED TO DO AV LOCK, only called in the opener thread void ReadMedia::clearFile() { if (m_file != NULL) bgav_close( m_file ); m_file = bgav_create(); bgav_options_copy( bgav_get_options( m_file ) , m_opt); m_aeof = true; m_veof = true; m_pcm_seek = SEEK_NOTHING; m_frame_seek = SEEK_NOTHING; } // NOT PUBLIC // Closes the bgav_t struct, destroying it and setting to NULL // NO NEED TO DO AV LOCK, only called in the opener thread void ReadMedia::closeFile() { if (m_file != NULL) bgav_close( m_file ); m_file = NULL; m_aeof = true; m_veof = true; m_pcm_seek = SEEK_NOTHING; m_frame_seek = SEEK_NOTHING; sprintf(m_filename, "seinettbitte!"); } // NOT PUBLIC // killAVThreads is only called from the opener thread // and from the destructor // THIS IS NOT REALLY A PUBLIC function void ReadMedia::killAVThreads() { lockState(); m_state = STATE_EMPTY; quit_av_threads = true; unlockState(); signalAV(); signalAV(); // FIRST need to join the threads that exist and are running if (m_audio_thread_ret == 0) { pthread_join( m_thread_fillaudiofifo, NULL); //pthread_detach( m_thread_fillaudiofifo); } if (m_video_thread_ret == 0){ pthread_join( m_thread_fillvideofifo, NULL); //pthread_detach( m_thread_fillvideofifo); } m_audio_thread_ret = -1; m_video_thread_ret = -1; // no need to lock here quit_av_threads = false; } // NOT PUBLIC // Only called from the opener thread bool ReadMedia::startAVThreads() { if (m_audio_thread_ret == 0 || m_video_thread_ret == 0 ) { // ouch!, we have running AV threads, this is not good return false; } if (m_audio_stream_count > 0) { m_audio_thread_ret = pthread_create(&m_thread_fillaudiofifo, NULL, the_audiofifo_filler, (void *)this); if (m_audio_thread_ret != 0 ) { printf("ReadMedia:: problem starting the audio thread\n"); return false; } } if (m_video_stream_count > 0) { m_video_thread_ret = pthread_create(&m_thread_fillvideofifo, NULL, the_videofifo_filler, (void *)this); if (m_video_thread_ret != 0 ) { printf("ReadMedia:: problem starting the video thread\n"); return false; } } return true; } bool ReadMedia::initFormat() { const gavl_audio_format_t * open_audio_format; const gavl_video_format_t * open_video_format; // we use the m_vfifosize to see if the user app wants video or not // then, we set m_video_stream_count to 0 if he doesn't want video if (m_video_stream_count > 0 && m_vfifosize > 0) { open_video_format = bgav_get_video_format(m_file, 0); if (open_video_format->pixelformat == GAVL_PIXELFORMAT_NONE) { printf("!!!sorry, pixelformat is not recognized.\n"); return false; } // let's check to see if the formats are the same, if they are the same // there is no reason to recreate the fifo or frames if ( gavl_video_formats_equal( &m_video_format, open_video_format) == 0 ) { // the formats are different gavl_video_format_copy (&m_video_format, open_video_format); if (m_video_frame != NULL) gavl_video_frame_destroy(m_video_frame); m_video_frame = gavl_video_frame_create(&m_video_format); gavl_video_frame_clear( m_video_frame, &m_video_format); if (m_fifovideo != NULL) delete m_fifovideo; m_fifovideo= new FifoVideoFrames( m_vfifosize , &m_video_format); } } else { m_video_stream_count = 0; m_veof = true; } // we use the m_afifosize to see if the user app wants audio or not // then, we set m_audio_stream_count to 0 if he doesn't want audio if (m_audio_stream_count > 0 && m_afifosize > 0) { open_audio_format = bgav_get_audio_format(m_file, 0); // we can get audio formats that are unkown if ( open_audio_format->sample_format == GAVL_SAMPLE_NONE) { printf("sorry, this file has unsupported audio.\n"); return false; } if ( gavl_audio_formats_equal(&m_audio_format, open_audio_format) == 0 ) { // audio formats are different // save the old spf int spf = m_audio_format.samples_per_frame; gavl_audio_format_copy(&m_audio_format, open_audio_format); if (m_audio_frame != NULL) { gavl_audio_frame_destroy(m_audio_frame); } // set it back to original m_audio_format.samples_per_frame = spf ; m_audio_frame = gavl_audio_frame_create(&m_audio_format); gavl_audio_frame_mute( m_audio_frame, &m_audio_format); if( m_fifoaudio != NULL ) delete m_fifoaudio; m_fifoaudio = new FifoAudioFrames( m_afifosize , &m_audio_format); } } else { // user doesn't want audio m_audio_stream_count = 0; m_aeof=true; } m_length_in_gavltime = bgav_get_duration ( m_file, 0);; m_length_in_seconds = gavl_time_to_seconds( m_length_in_gavltime ); m_num_samples = 0; m_num_frames = 0; if (m_audio_stream_count) { if ( bgav_can_seek_sample(m_file) == 1 ) { m_num_samples= bgav_audio_duration ( m_file, 0) ; } else { m_num_samples= gavl_time_to_samples( m_audio_format.samplerate , bgav_get_duration ( m_file, 0) ); } } // set frames WE NEED TO take care here for non-constant frame-rates if(m_video_stream_count) { if ( bgav_can_seek_sample(m_file) == 1 && m_video_format.framerate_mode == GAVL_FRAMERATE_CONSTANT) { m_num_frames = bgav_video_duration ( m_file, 0)/ m_video_format.frame_duration; } else if ( bgav_can_seek_sample(m_file) == 1 && m_video_format.framerate_mode == GAVL_FRAMERATE_VARIABLE ) { // FIXME what to do with non constant frame rates? m_num_frames=0; } else { m_num_frames = gavl_time_to_frames( m_video_format.timescale, m_video_format.frame_duration , bgav_get_duration ( m_file, 0) ); } } // printf("m_num_frames =%lld, duration = %lld , vid_duration=%lld\n", // m_num_frames, bgav_get_duration ( m_file, 0), bgav_video_duration ( m_file, 0) ); // set seconds if ( bgav_can_seek_sample(m_file) == 1) { gavl_time_t atime=0,vtime=0; if ( m_audio_stream_count ) atime = gavl_samples_to_time( m_audio_format.samplerate, m_num_samples ); if (m_video_stream_count && m_video_format.frame_duration > 0) { vtime = gavl_frames_to_time( m_video_format.timescale, m_video_format.frame_duration, m_num_frames ); } else if ( m_video_stream_count ) { // non constant framerate vtime = bgav_video_duration( m_file, 0); } // else rely on audio time m_length_in_gavltime = atime > vtime ? atime :vtime; m_length_in_seconds = gavl_time_to_seconds( m_length_in_gavltime ); //printf("atime=%ld, vtime=%ld, l_in_sec=%f\n", atime, vtime, m_length_in_seconds); } m_pcm_seek = SEEK_NOTHING; m_frame_seek = SEEK_NOTHING; return true; } void *the_thread_dispatcher(void *xp) { ReadMedia *rm = (ReadMedia *)xp; int cmd = CMD_NULL; pthread_t thread_open; int start_thread_ret = -1; cmd = rm->getCommand(); while ( cmd != CMD_QUIT ) { if (cmd == CMD_OPEN) { // We already check in the openFile function if the user is trying to open // a file that is already open. // join the opener thread, this will protect again any other calls to open if (start_thread_ret == 0 ) pthread_join( thread_open, NULL ); // we join the AV threads // the opener thread will start the AV threads anew upon success rm->killAVThreads(); start_thread_ret = pthread_create(&thread_open, NULL, the_thread_opener, (void *)rm) ; if (start_thread_ret != 0 ) printf( "Failed to create m_thread_open thread.\n"); } if (rm->getCommand() == CMD_QUIT) break; rm->setCommand( CMD_NULL); rm->waitDispatch(); cmd = rm->getCommand(); } //printf("dispatcher: joining thread open\n"); if( start_thread_ret == 0 ) pthread_join( thread_open , NULL); //printf("dispatcher: joined thread open, joining AV's\n"); rm->killAVThreads(); //printf("dispatcher: joined AV's\n"); pthread_exit(NULL); }; void *the_thread_opener(void *xp) { ReadMedia *rm = NULL; int num_urls=0, num_tracks=0, audio_stream_count=0, video_stream_count =0; rm = (ReadMedia *)xp; rm->setState(STATE_OPENING); // AFTER WE KILL THE THREADS, THERE IS NO NEED TO LOCK AV mutex // ALL functions that want to get info on the file, seek, // or decode a frame, MUST first check the state. If the // state is STATE_READY, then it can perform it's function, // locking on the necessary variables that might conflict with // the AV. // clearFile deletes old file and creates new File rm->clearFile(); if(!bgav_open(rm->getFile(), rm->getFilename())) { printf( "Could not open file %s\n", rm->getFilename()); rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL); //return NULL; } else { printf("opened %s\n", rm->getFilename()); } // check to see if it is a redirector if(bgav_is_redirector( rm->getFile() )) { num_urls = bgav_redirector_get_num_urls( rm->getFile() ); printf( "Found redirector with %d urls inside, we will try to use the first one.\n", num_urls); printf( "Name %d: %s\n", 1, bgav_redirector_get_name(rm->getFile() , 0)); printf("URL %d: %s\n", 1, bgav_redirector_get_url(rm->getFile(), 0)); sprintf(rm->getFilename(), "%s", bgav_redirector_get_url(rm->getFile(), 0) ); rm->clearFile(); if (!bgav_open( rm->getFile(), rm->getFilename() )) { printf("Could not open redirector\n"); rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL); //return NULL; } else { printf("opened redirector %s\n", rm->getFilename()); } } num_tracks = bgav_num_tracks(rm->getFile()); if ( num_tracks ) { bgav_select_track(rm->getFile(), 0); } else { printf("No tracks associated with file:%s\n", rm->getFilename() ); rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL); } audio_stream_count = bgav_num_audio_streams(rm->getFile(), 0); if( audio_stream_count ) bgav_set_audio_stream(rm->getFile(), 0, BGAV_STREAM_DECODE); video_stream_count = bgav_num_video_streams(rm->getFile(), 0); if( video_stream_count ) bgav_set_video_stream(rm->getFile(), 0, BGAV_STREAM_DECODE); rm->setVideoStreamCount(video_stream_count); rm->setAudioStreamCount(audio_stream_count); //printf("astream_count = %d, vstream_count=%d\n", audio_stream_count, video_stream_count); if(!bgav_start(rm->getFile())) { printf( "failed to start file\n"); rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL) ; //return NULL; } if( !rm->initFormat() ){ rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL) ; } if( !rm->startAVThreads() ){ rm->setState( STATE_EMPTY ); rm->closeFile(); rm->callOpenCallback(); pthread_exit(NULL) ; } // AV threads are now running, blocking will be necessary // STATE_READY and callOpenCallback is set/called in the // fifo fill callbacks rm->signalAV(); rm->signalAV(); //extra signal for second thread pthread_exit(NULL); //return NULL; } void *the_audiofifo_filler( void * xp) { int samples_returned=0; ReadMedia *rm = (ReadMedia *)xp; int first = true; int dovideo = rm->getVideoStreamCount(); int spf = rm->getSamplesPerFrame(); int samplerate = rm->getAudioSamplerate(); int64_t seekto = SEEK_NOTHING; int can_seek = bgav_can_seek ( rm->getFile() ); int can_seek_sample = bgav_can_seek_sample ( rm->getFile() ); while ( !rm->quitAVThreads() ) { //while ( rm->getAudioFifo() != NULL && rm->getAudioFifo()->FreeSpace() && !rm->getAEOF() ) { while ( rm->getAudioFifo()->FreeSpace() && !rm->getAEOF() ) { if (rm->quitAVThreads() ) pthread_exit(NULL) ; //return NULL; rm->lockAV(); // check to see if we need to seek // if this is set, we already know we can seek on this file // and don't need to check with bgav_can_seek if ( can_seek ) { seekto = rm->pcmSeek(); if ( seekto != SEEK_NOTHING ) { if ( seekto == SEEK_REWIND) { // bgav_seek_audio ONLY seeks on the audio stream seekto = 0; // bgav_seek_scaled(rm->getFile(), &seekto, samplerate); } //else { bgav_seek_scaled(rm->getFile(), &seekto, samplerate); //} rm->getAudioFifo()->Flush(); if (dovideo && rm->getVideoFifo() ) { rm->getVideoFifo()->Flush(); rm->signalV(); } } } samples_returned = bgav_read_audio(rm->getFile(), rm->getAudioFrame(), 0, spf ); //rm->unlockAV(); if (samples_returned == 0 ) { if( rm->getLoop() ) { if ( can_seek ) { // Now, rewind the file, don't flush the fifo's if (can_seek_sample) { // only seek on audio stream bgav_seek_audio(rm->getFile(), 0, 0); } else { seekto =0; bgav_seek_scaled(rm->getFile(), &seekto, samplerate); if (dovideo && rm->getVideoFifo() ) { rm->setVEOF(false); rm->signalV(); } } // SAVE THIS FOR ANOTHER TIME, OVERLAPPED SMOOTH LOOPING //if ( rm->getLoop() > 1 && bgav_read_audio(rm->getFile(), rm->getAudioFrame(), 0, spf ) ) { // add to the fifo, overlapping // rm->getAudioFifo()->AppendOverlap( rm->getAudioFrame(), 5 ); // } } else { // this file is not seekable, what do we do? printf("cannot seek on file, but we want to loop. setting end of file.\n"); rm->setAEOF(true); } } else { rm->setAEOF(true); } rm->unlockAV(); break; } rm->unlockAV(); if( !rm->getAudioFifo()->Append( rm->getAudioFrame() )) printf("problem with appending Audio Frame\n"); } if (first && !dovideo) { rm->setState( STATE_READY ); rm->callOpenCallback(); first = false; } if (rm->quitAVThreads() ) pthread_exit(NULL); //return NULL; rm->waitA(); } pthread_exit(NULL);//return NULL; } void *the_videofifo_filler( void * xp) { ReadMedia *rm = (ReadMedia *)xp; int ret = 0; int first = true; int doaudio = rm->getAudioStreamCount(); int64_t seekto =SEEK_NOTHING; int can_seek = bgav_can_seek ( rm->getFile() ); int can_seek_sample = bgav_can_seek_sample ( rm->getFile() ); int timescale = rm->getVideoTimescale(); while (!rm->quitAVThreads() ) { while ( rm->getVideoFifo() !=NULL && rm->getVideoFifo()->FreeSpace() && !rm->getVEOF() ) { if (rm->quitAVThreads() ) pthread_exit(NULL);//return NULL; rm->lockAV(); // check to see if we need to seek // if this is set, we already know we can seek on this file // and don't need to check with bgav_can_seek if (can_seek) { seekto = rm->frameSeek(); if ( seekto >= 0 ) { if (doaudio && rm->getAudioFifo() ) rm->getAudioFifo()->Flush(); rm->getVideoFifo()->Flush(); bgav_seek_scaled(rm->getFile(), &seekto, timescale); } else if (seekto == SEEK_REWIND && !doaudio) { // rewind is a special case for Video. rm->getVideoFifo()->Flush(); seekto =0; bgav_seek_scaled(rm->getFile(), &seekto, timescale); } } ret = bgav_read_video(rm->getFile(), rm->getVideoFrame(), 0 ); if ( !ret ) { // only loop from video if there is no audio // audio controls loop timing if ( rm->getLoop() ){ if ( can_seek ) { if (doaudio && can_seek_sample) { // seek on video stream only if we have audio // audio and video seeking are separate in sample accurate // seeking. If no sample accurate seeking, then let audio // handle seeking bgav_seek_video(rm->getFile(), 0,0); } else if (!doaudio) { // if we don't have audio...just seek to 0 seekto=0; bgav_seek_scaled(rm->getFile(), &seekto, timescale); } } else { // if (can_seek) printf ("We want to loop video, but we cannot seek on this video stream,setting VEOF\n"); rm->setVEOF(true); } } else { rm->setVEOF(true); } rm->unlockAV(); break; } rm->unlockAV(); if( !rm->getVideoFifo()->Append( rm->getVideoFrame() )) printf("problem with appending VideoFrame\n"); } // on the first time 'round we will call the open callback // if there is no video in the file, the audio will handle it. if (first) { rm->setState( STATE_READY ); rm->callOpenCallback(); first = false; } if (rm->quitAVThreads() ) pthread_exit(NULL); //return NULL; rm->waitV(); } pthread_exit(NULL); //return NULL; }