aboutsummaryrefslogtreecommitdiff
path: root/src/ReadMedia.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ReadMedia.cpp')
-rw-r--r--src/ReadMedia.cpp1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/src/ReadMedia.cpp b/src/ReadMedia.cpp
new file mode 100644
index 0000000..f109da7
--- /dev/null
+++ b/src/ReadMedia.cpp
@@ -0,0 +1,1132 @@
+#include "ReadMedia.h"
+#include <pthread.h>
+#include <unistd.h> //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 = false;
+ m_veof = false;
+ m_atime = 0.0;
+ m_vtime = 0.0;
+ m_video_stream_count =0;
+ m_audio_stream_count =0;
+
+ m_pcm_seek = -1;
+ m_frame_seek = -1;
+ m_length_in_seconds=0.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_UNKNOWN;
+ 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 ) {
+ 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 = -1;
+ 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() {
+ lockState();
+ if ( m_state == STATE_READY && m_file != NULL) {
+ if (m_audio_stream_count){
+ //m_audio_frame->valid_samples = 0;
+ m_pcm_seek = 0;
+ m_frame_seek = -1;
+ m_aeof = false;
+ m_atime = 0;
+ // need to signal after setting eof to false;
+ unlockState();
+ signalAV();
+ lockState();
+ }
+ if ( m_video_stream_count ) {
+ if (m_audio_stream_count == 0) {
+ m_frame_seek=0;
+ m_pcm_seek = -1;
+ }
+ m_veof = false;
+ m_vtime=0;
+ unlockState();
+ signalAV();
+ lockState();
+ }
+ unlockState();
+ // fifo flush happens when we seek
+ return true;
+ }
+ unlockState();
+ return false;
+}
+
+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 = -1;
+ unlockState();
+ return false;
+ }
+}
+
+// NOT PUBLIC
+int64_t ReadMedia::frameSeek() {
+ int64_t tmp=-1;
+ lockState();
+ tmp = m_frame_seek;
+ m_frame_seek = -1;
+ 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 = -1;
+ unlockState();
+ return false;
+ }
+}
+
+// NOT PUBLIC
+int64_t ReadMedia::pcmSeek() {
+ int64_t tmp=-1;
+ lockState();
+ tmp = m_pcm_seek;
+ m_pcm_seek = -1;
+ 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 = -1;
+ unlockState();
+ signalAV();
+ return true;
+ } else if ( m_video_stream_count ) {
+ 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 = -1;
+ 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();
+ 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;
+}
+
+int ReadMedia::getVideoFrameDuration() {
+ int t=0;
+ lockState();
+ t = m_video_format.frame_duration;
+ 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;
+ unlockState();
+}
+void ReadMedia::setVEOF(bool b) {
+ lockState();
+ m_veof = b;
+ unlockState();
+}
+
+
+bool ReadMedia::getEOF() {
+ bool tmp = true;
+ lockState();
+ if (m_state == STATE_READY)
+ tmp = (m_aeof && m_veof);
+ unlockState();
+ return tmp;
+}
+
+void ReadMedia::setLoop( bool b) {
+ lockState();
+ m_loop = b;
+ unlockState();
+}
+bool ReadMedia::getLoop() {
+ bool tmp = true;
+ lockState();
+ tmp = m_loop;
+ 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;
+ 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;
+ 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 = false;
+ m_veof = false;
+ m_pcm_seek = -1;
+ m_frame_seek = -1;
+}
+
+// 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 = false;
+ m_veof = false;
+ m_pcm_seek = -1;
+ m_frame_seek = -1;
+
+ 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;
+ }
+
+ // 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;
+ }
+
+ if (bgav_can_seek(m_file)) {
+ // set seconds
+ m_length_in_seconds = gavl_time_to_seconds( bgav_get_duration ( m_file, 0) );
+
+ // set samples
+ 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) );
+ else
+ m_num_samples=0;
+
+ // set frames
+ if(m_video_stream_count)
+ if ( bgav_can_seek_sample(m_file) == 1 )
+ m_num_frames = bgav_video_duration ( m_file, 0);
+ else
+ m_num_frames = gavl_time_to_frames( m_video_format.timescale, m_video_format.frame_duration , bgav_get_duration ( m_file, 0) );
+ else
+ m_num_frames=0;
+
+ } else { // no can seek;
+
+ m_length_in_seconds = 0.0;
+ m_num_samples = 0;
+ m_num_frames = 0;
+ }
+
+ m_pcm_seek = -1;
+ m_frame_seek = -1;
+
+ 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);
+
+ 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 =-1;
+ 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() ) >= 0 ) {
+ rm->getAudioFifo()->Flush();
+ if (dovideo && rm->getVideoFifo() ) rm->getVideoFifo()->Flush();
+ if ( can_seek_sample ) {
+ bgav_seek_audio(rm->getFile() , 0, seekto );
+ } else {
+ gavl_time_t gt = gavl_samples_to_time( samplerate, seekto ) ;
+ bgav_seek(rm->getFile(), &gt);
+ }
+ }
+
+ 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) {
+ bgav_seek_audio(rm->getFile() , 0, 0);
+ } else {
+ gavl_time_t gt = 0;
+ bgav_seek(rm->getFile(), &gt);
+ }
+ // 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);
+ rm->pcmSeek();// clear the seek var just in case
+ }
+ } else {
+ rm->setAEOF(true);
+ rm->pcmSeek(); // clear the seek var just in case
+ }
+ 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 =-1;
+ int can_seek = bgav_can_seek ( rm->getFile() );
+ int can_seek_sample = bgav_can_seek_sample ( rm->getFile() );
+ int timescale = rm->getVideoTimescale();
+ int frame_duration = rm->getVideoFrameDuration();
+
+ 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 ( (seekto = rm->frameSeek() ) >= 0 ) {
+ if (doaudio && rm->getAudioFifo()) rm->getAudioFifo()->Flush();
+ rm->getVideoFifo()->Flush();
+ gavl_time_t gt = gavl_frames_to_time (timescale, frame_duration, seekto );
+ if ( can_seek_sample ) {
+ bgav_seek_video(rm->getFile() , 0, gt );
+ } else {
+ bgav_seek(rm->getFile(), &gt);
+ }
+ }
+
+ 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 ( !doaudio && rm->getLoop() ) {
+
+ if ( can_seek ) {
+ // Now, rewind the file, don't flush fifos
+ if (can_seek_sample) {
+ bgav_seek_video(rm->getFile() , 0, 0);
+ } else {
+ gavl_time_t gt = 0;
+ bgav_seek(rm->getFile(), &gt);
+ }
+ } 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->setVEOF(true);
+ //rm->frameSeek();// clear the seek var just in case
+ }
+
+ } 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;
+}
+
+