/****************************************************************************** * recordMyDesktop * ******************************************************************************* * * * Copyright (C) 2006,2007,2008 John Varouhakis * * * * * * 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 * * * * * * * * For further information contact me at johnvarouhakis@gmail.com * ******************************************************************************/ #include "config.h" #include "rmd_flush_to_ogg.h" #include "rmd_encode_audio_buffers.h" #include "rmd_encode_image_buffers.h" #include "rmd_threads.h" #include "rmd_types.h" #include #include #include #include #include #include //we copy the page because the next call to ogg_stream_pageout //will invalidate it. But we must have pages from //both streams at every time in //order to do correct multiplexing static void page_copy(ogg_page *new, ogg_page *old) { new->header_len = old->header_len; new->header = malloc(new->header_len); new->body_len = old->body_len; new->body = malloc(new->body_len); memcpy(new->header, old->header, new->header_len); memcpy(new->body, old->body, new->body_len); } //free our copy static void page_free(ogg_page *pg) { pg->header_len = pg->body_len = 0; free(pg->header); free(pg->body); } void *rmdFlushToOgg(ProgData *pdata) { int videoflag = 0, audioflag = 0; double video_bytesout = 0, audio_bytesout = 0; ogg_page videopage, //owned by libogg videopage_copy, //owned by the application audiopage, //owned by libogg audiopage_copy; //owned by the application double audiotime = 0; double videotime = 0; int th_st_fin = 0, v_st_fin = (pdata->args.nosound); rmdThreadsSetName("rmdFlushToOgg"); while (!(th_st_fin && v_st_fin)) { enum { FLUSH_AUDIO, FLUSH_VIDEO, } audio_or_video; if (pdata->running) { pthread_mutex_lock(&pdata->libogg_mutex); if (!videoflag) { videoflag = ogg_stream_pageout(&pdata->enc_data->m_ogg_ts, &videopage); videotime = videoflag ? theora_granule_time( &pdata->enc_data->m_th_st, ogg_page_granulepos(&videopage) ) : -1; if (videoflag) page_copy(&videopage_copy, &videopage); } if (!pdata->args.nosound && !audioflag) { audioflag = ogg_stream_pageout(&pdata->enc_data->m_ogg_vs, &audiopage); audiotime = audioflag ? vorbis_granule_time( &pdata->enc_data->m_vo_dsp, ogg_page_granulepos(&audiopage) ) : -1; if (audioflag) page_copy(&audiopage_copy, &audiopage); } pthread_mutex_unlock(&pdata->libogg_mutex); } else { if (!th_st_fin && !videoflag) { pthread_mutex_lock(&pdata->libogg_mutex); videoflag = ogg_stream_flush(&pdata->enc_data->m_ogg_ts, &videopage); videotime = videoflag ? theora_granule_time( &pdata->enc_data->m_th_st, ogg_page_granulepos(&videopage) ) : -1; if (videoflag) page_copy(&videopage_copy, &videopage); pthread_mutex_unlock(&pdata->libogg_mutex); //we need the last page to properly close the stream if (!videoflag) { pthread_mutex_lock(&pdata->theora_lib_mutex); while (!pdata->th_encoding_clean) pthread_cond_wait(&pdata->theora_lib_clean, &pdata->theora_lib_mutex); pthread_mutex_unlock(&pdata->theora_lib_mutex); rmdSyncEncodeImageBuffer(pdata, 1); } } if (!pdata->args.nosound && !v_st_fin && !audioflag) { pthread_mutex_lock(&pdata->libogg_mutex); audioflag = ogg_stream_flush(&pdata->enc_data->m_ogg_vs, &audiopage); audiotime = audioflag ? vorbis_granule_time( &pdata->enc_data->m_vo_dsp, ogg_page_granulepos(&audiopage) ) : -1; if (audioflag) page_copy(&audiopage_copy, &audiopage); pthread_mutex_unlock(&pdata->libogg_mutex); //we need the last page to properly close the stream if (!audioflag) { pthread_mutex_lock(&pdata->vorbis_lib_mutex); while (!pdata->v_encoding_clean) pthread_cond_wait(&pdata->vorbis_lib_clean, &pdata->vorbis_lib_mutex); pthread_mutex_unlock(&pdata->vorbis_lib_mutex); rmdSyncEncodeAudioBuffer(pdata, NULL); } } } #if 0 /* I don't understand what this is about, if these are finished we want to lose * their pages? */ if (th_st_fin) videoflag=0; if (v_st_fin) audioflag=0; #endif if ((!audioflag && !v_st_fin && !pdata->args.nosound) || (!videoflag && !th_st_fin)) { nanosleep(&(struct timespec){ .tv_nsec = 10000000 }, NULL); continue; } if (!audioflag) { audio_or_video = FLUSH_VIDEO; } else if (!videoflag) { audio_or_video = FLUSH_AUDIO; } else { if (audiotime < videotime) audio_or_video = FLUSH_AUDIO; else audio_or_video = FLUSH_VIDEO; } if (audio_or_video == FLUSH_VIDEO) { video_bytesout += fwrite( videopage_copy.header, 1, videopage_copy.header_len, pdata->enc_data->fp); video_bytesout += fwrite( videopage_copy.body, 1, videopage_copy.body_len, pdata->enc_data->fp); videoflag = 0; if (!pdata->running) { pthread_mutex_lock(&pdata->libogg_mutex); if (ogg_page_eos(&videopage_copy)) th_st_fin = 1; pthread_mutex_unlock(&pdata->libogg_mutex); } page_free(&videopage_copy); } else { audio_bytesout += fwrite( audiopage_copy.header, 1, audiopage_copy.header_len, pdata->enc_data->fp); audio_bytesout += fwrite( audiopage_copy.body, 1, audiopage_copy.body_len, pdata->enc_data->fp); audioflag = 0; if (!pdata->running) { pthread_mutex_lock(&pdata->libogg_mutex); if (ogg_page_eos(&audiopage_copy)) v_st_fin = 1; pthread_mutex_unlock(&pdata->libogg_mutex); } page_free(&audiopage_copy); } } pthread_mutex_lock(&pdata->libogg_mutex); ogg_stream_clear(&pdata->enc_data->m_ogg_ts); if (!pdata->args.nosound) ogg_stream_clear(&pdata->enc_data->m_ogg_vs); pthread_mutex_unlock(&pdata->libogg_mutex); theora_clear(&pdata->enc_data->m_th_st); if (pdata->enc_data->fp) fclose(pdata->enc_data->fp); fprintf(stderr, "\r \nDone.\nWritten %.0f bytes\n" "(%.0f of which were video data and %.0f audio data)\n\n", video_bytesout + audio_bytesout, video_bytesout, audio_bytesout); pthread_exit(&errno); }