diff options
-rw-r--r-- | recordmydesktop/include/Makefile.am | 2 | ||||
-rw-r--r-- | recordmydesktop/include/skeleton.h | 107 | ||||
-rw-r--r-- | recordmydesktop/src/Makefile.am | 3 | ||||
-rw-r--r-- | recordmydesktop/src/init_encoder.c | 115 | ||||
-rw-r--r-- | recordmydesktop/src/skeleton.c | 216 |
5 files changed, 432 insertions, 11 deletions
diff --git a/recordmydesktop/include/Makefile.am b/recordmydesktop/include/Makefile.am index fb8518c..1884a45 100644 --- a/recordmydesktop/include/Makefile.am +++ b/recordmydesktop/include/Makefile.am @@ -1 +1 @@ -noinst_HEADERS= recordmydesktop.h rmdfunc.h rmdtypes.h rmdmacro.h +noinst_HEADERS= recordmydesktop.h rmdfunc.h rmdtypes.h rmdmacro.h skeleton.h diff --git a/recordmydesktop/include/skeleton.h b/recordmydesktop/include/skeleton.h new file mode 100644 index 0000000..a563c1f --- /dev/null +++ b/recordmydesktop/include/skeleton.h @@ -0,0 +1,107 @@ +/****************************************************************************** +* skeleton.h * +* author: Tahseen Mohammad * +******************************************************************************* +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions * +* are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Redistributions in binary form must reproduce the above copyright * +* notice, this list of conditions and the following disclaimer in the * +* documentation and/or other materials provided with the distribution. * +* * +* - Neither the name of the Xiph.org Foundation nor the names of its * +* contributors may be used to endorse or promote products derived from * +* this software without specific prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR * +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +******************************************************************************/ + + + +#ifndef _SKELETON_H +#define _SKELETON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +#define snprintf _snprintf +#endif + +#include <ogg/ogg.h> + +#define SKELETON_VERSION_MAJOR 3 +#define SKELETON_VERSION_MINOR 0 +#define FISHEAD_IDENTIFIER "fishead\0" +#define FISBONE_IDENTIFIER "fisbone\0" +#define FISHEAD_SIZE 64 +#define FISBONE_SIZE 52 +#define FISBONE_MESSAGE_HEADER_OFFSET 44 + +/* fishead_packet holds a fishead header packet. */ +typedef struct { + ogg_uint16_t version_major; /* skeleton version major */ + ogg_uint16_t version_minor; /* skeleton version minor */ + /* Start time of the presentation + * For a new stream presentationtime & basetime is same. */ + ogg_int64_t ptime_n; /* presentation time numerator */ + ogg_int64_t ptime_d; /* presentation time denominator */ + ogg_int64_t btime_n; /* basetime numerator */ + ogg_int64_t btime_d; /* basetime denominator */ + /* will holds the time of origin of the stream, a 20 bit field. */ + unsigned char UTC[20]; +} fishead_packet; + +/* fisbone_packet holds a fisbone header packet. */ +typedef struct { + ogg_uint32_t serial_no; /* serial no of the corresponding stream */ + ogg_uint32_t nr_header_packet; /* number of header packets */ + /* granule rate is the temporal resolution of the logical bitstream */ + ogg_int64_t granule_rate_n; /* granule rate numerator */ + ogg_int64_t granule_rate_d; /* granule rate denominator */ + ogg_int64_t start_granule; /* start granule value */ + ogg_uint32_t preroll; /* preroll */ + unsigned char granule_shift; /* 1 byte value holding the granule shift */ + char *message_header_fields; /* holds all the message header fields */ + /* current total size of the message header fields, for realloc purpose, initially zero */ + ogg_uint32_t current_header_size; +} fisbone_packet; + +extern int write_ogg_page_to_file(ogg_page *og, FILE *out); +extern int add_message_header_field(fisbone_packet *fp, char *header_key, char *header_value); +/* remember to deallocate the returned ogg_packet properly */ +extern ogg_packet ogg_from_fishead(fishead_packet *fp); +extern ogg_packet ogg_from_fisbone(fisbone_packet *fp); +extern fishead_packet fishead_from_ogg(ogg_packet *op); +extern fisbone_packet fisbone_from_ogg(ogg_packet *op); +extern int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp); +extern int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp); +extern int add_eos_packet_to_stream(ogg_stream_state *os); +extern int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out); + +#ifdef __cplusplus +} +#endif + +#endif /* _SKELETON_H */ + + + + + + diff --git a/recordmydesktop/src/Makefile.am b/recordmydesktop/src/Makefile.am index 09b77b0..c3046b5 100644 --- a/recordmydesktop/src/Makefile.am +++ b/recordmydesktop/src/Makefile.am @@ -35,7 +35,8 @@ recordmydesktop_SOURCES= recordmydesktop.c\ rmd_error.c\ rmd_frame.c\ rmd_timer.c\ - rmd_rescue.c + rmd_rescue.c\ + skeleton.c INCLUDES= $(all_includes) -I$(top_srcdir)/include diff --git a/recordmydesktop/src/init_encoder.c b/recordmydesktop/src/init_encoder.c index 055a83f..d646c59 100644 --- a/recordmydesktop/src/init_encoder.c +++ b/recordmydesktop/src/init_encoder.c @@ -27,6 +27,20 @@ #include <recordmydesktop.h> #include <string.h> +#include <skeleton.h> + +void m_add_fishead_packet(ogg_stream_state *m_ogg_state){ + + fishead_packet skel_fp; + + skel_fp.ptime_n=skel_fp.btime_n=0; + skel_fp.ptime_d=skel_fp.btime_d=1000; + + add_fishead_to_stream(m_ogg_state,&skel_fp); + + +} + int IncrementalNaming(char **name){ struct stat buff; @@ -66,20 +80,20 @@ int IncrementalNaming(char **name){ } void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready){ - int y1, + + int y0, + y1, y2, fname_length; + ogg_stream_state m_ogg_skel; + ogg_page skel_og_pg; + fisbone_packet skel_fbv, //video fisbone packet + skel_fba ; //audio fisbone packet + (pdata)->enc_data=enc_data_t; - srand(time(NULL)); - y1=rand(); - y2=rand(); - y2+=(y1==y2); - ogg_stream_init(&enc_data_t->m_ogg_ts,y1); - if(!pdata->args.nosound) - ogg_stream_init(&enc_data_t->m_ogg_vs,y2); fname_length=strlen(pdata->args.filename); - if(!(fname_length>4 && + if(!(fname_length>4 && pdata->args.filename[fname_length-4] == '.' && (pdata->args.filename[fname_length-3] == 'o' || pdata->args.filename[fname_length-3] == 'O') && @@ -106,6 +120,34 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready){ (pdata)->args.filename); exit(13); } + + //each stream must have a unique + srand(time(NULL)); + y0=rand()+1; + y1=rand()+1; + y2=rand()+1; + y2+=(y1==y2); + y0=(((y0==y1)||(y0==y2))?(y1+y2):y0); + + //init ogg streams + //skeleton first + ogg_stream_init(&m_ogg_skel,y0); + m_add_fishead_packet(&m_ogg_skel); + if(ogg_stream_pageout(&m_ogg_skel,&skel_og_pg)!= 1){ + fprintf (stderr, "Internal Ogg library error.\n"); + exit (2); + } + fwrite(skel_og_pg.header,1,skel_og_pg.header_len,enc_data_t->fp); + fwrite(skel_og_pg.body,1,skel_og_pg.body_len,enc_data_t->fp); + + + + ogg_stream_init(&enc_data_t->m_ogg_ts,y1); + if(!pdata->args.nosound) + ogg_stream_init(&enc_data_t->m_ogg_vs,y2); + + + theora_info_init(&enc_data_t->m_th_inf); enc_data_t->m_th_inf.frame_width=(pdata)->brwin.rgeom.width; enc_data_t->m_th_inf.frame_height=(pdata)->brwin.rgeom.height; @@ -203,6 +245,51 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready){ ogg_stream_packetin(&enc_data_t->m_ogg_vs,&header_code); } + //fishbone packets go here + memset(&skel_fbv,0,sizeof(skel_fbv)); + skel_fbv.serial_no=enc_data_t->m_ogg_ts.serialno; + skel_fbv.nr_header_packet=2+(!pdata->args.nosound); + skel_fbv.granule_rate_n=enc_data_t->m_th_inf.fps_numerator; + skel_fbv.granule_rate_d=enc_data_t->m_th_inf.fps_denominator; + skel_fbv.start_granule=0; + skel_fbv.preroll=0; + skel_fbv.granule_shift=theora_granule_shift(&enc_data_t->m_th_inf); + add_message_header_field(&skel_fbv, + "Content-Type", + "video/x-theora"); + + add_fisbone_to_stream(&m_ogg_skel,&skel_fbv); + + if(!pdata->args.nosound){ + + memset(&skel_fba,0,sizeof(skel_fba)); + skel_fba.serial_no=enc_data_t->m_ogg_vs.serialno; + skel_fba.nr_header_packet=3; + skel_fba.granule_rate_n=pdata->args.frequency; + skel_fba.granule_rate_d=(ogg_int64_t)1; + skel_fba.start_granule=0; + skel_fba.preroll=2; + skel_fba.granule_shift=0; + add_message_header_field(&skel_fba, + "Content-Type", + "audio/x-vorbis"); + + add_fisbone_to_stream(&m_ogg_skel,&skel_fba); + + } + + while(1){ + int result = ogg_stream_flush(&m_ogg_skel, &skel_og_pg); + if(result<0){ + fprintf (stderr, "Internal Ogg library error.\n"); + exit(2); + } + if(result==0) + break; + fwrite(skel_og_pg.header,1,skel_og_pg.header_len,enc_data_t->fp); + fwrite(skel_og_pg.body,1,skel_og_pg.body_len,enc_data_t->fp); + } + while(1){ @@ -238,8 +325,18 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready){ enc_data_t->fp); } } + + //skeleton eos + add_eos_packet_to_stream(&m_ogg_skel); + if(ogg_stream_flush(&m_ogg_skel,&skel_og_pg)<0){ + fprintf(stderr,"Internal Ogg library error.\n"); + exit(2); + } + fwrite(skel_og_pg.header,1,skel_og_pg.header_len,enc_data_t->fp); + fwrite(skel_og_pg.body,1,skel_og_pg.body_len,enc_data_t->fp); + //theora buffer allocation, if any if(!buffer_ready){ enc_data_t->yuv.y=(unsigned char *)malloc(enc_data_t->m_th_inf.height* enc_data_t->m_th_inf.width); diff --git a/recordmydesktop/src/skeleton.c b/recordmydesktop/src/skeleton.c new file mode 100644 index 0000000..02dd6bd --- /dev/null +++ b/recordmydesktop/src/skeleton.c @@ -0,0 +1,216 @@ +/****************************************************************************** +* skeleton.c * +* author: Tahseen Mohammad * +******************************************************************************* +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions * +* are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Redistributions in binary form must reproduce the above copyright * +* notice, this list of conditions and the following disclaimer in the * +* documentation and/or other materials provided with the distribution. * +* * +* - Neither the name of the Xiph.org Foundation nor the names of its * +* contributors may be used to endorse or promote products derived from * +* this software without specific prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR * +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +******************************************************************************/ + + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <ogg/ogg.h> + +#include <skeleton.h> + +/* write an ogg_page to a file pointer */ +int write_ogg_page_to_file(ogg_page *og, FILE *out) { + int written; + + written = fwrite(og->header,1, og->header_len, out); + written += fwrite(og->body,1, og->body_len, out); + + return written; +} + +int add_message_header_field(fisbone_packet *fp, + char *header_key, + char *header_value) { + + /* size of both key and value + ': ' + CRLF */ + int this_message_size = strlen(header_key) + strlen(header_value) + 4; + if (fp->message_header_fields == NULL) { + fp->message_header_fields = _ogg_calloc(this_message_size, sizeof(char)); + } else { + int new_size = (fp->current_header_size + this_message_size) * sizeof(char); + fp->message_header_fields = _ogg_realloc(fp->message_header_fields, new_size); + } + snprintf(fp->message_header_fields + fp->current_header_size, + this_message_size+1, + "%s: %s\r\n", + header_key, + header_value); + fp->current_header_size += this_message_size; + + return 0; +} + +/* create a ogg_packet from a fishead_packet structure */ +ogg_packet ogg_from_fishead(fishead_packet *fp) { + + ogg_packet op; + + memset(&op, 0, sizeof(op)); + op.packet = _ogg_calloc(FISHEAD_SIZE, sizeof(unsigned char)); + memset(op.packet, 0, FISHEAD_SIZE); + + memcpy (op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */ + *((ogg_uint16_t*)(op.packet+8)) = SKELETON_VERSION_MAJOR; /* version major */ + *((ogg_uint16_t*)(op.packet+10)) = SKELETON_VERSION_MINOR; /* version minor */ + *((ogg_int64_t*)(op.packet+12)) = (ogg_int64_t)fp->ptime_n; /* presentationtime numerator */ + *((ogg_int64_t*)(op.packet+20)) = (ogg_int64_t)fp->ptime_d; /* presentationtime denominator */ + *((ogg_int64_t*)(op.packet+28)) = (ogg_int64_t)fp->btime_n; /* basetime numerator */ + *((ogg_int64_t*)(op.packet+36)) = (ogg_int64_t)fp->btime_d; /* basetime denominator */ + /* TODO: UTC time, set to zero for now */ + + op.b_o_s = 1; /* its the first packet of the stream */ + op.e_o_s = 0; /* its not the last packet of the stream */ + op.bytes = FISHEAD_SIZE; /* length of the packet in bytes */ + + return op; +} + +/* create a ogg_packet from a fisbone_packet structure. + * call this method after the fisbone_packet is filled and all message header fields are added + * by calling add_message_header_field method. + */ +ogg_packet ogg_from_fisbone(fisbone_packet *fp) { + + ogg_packet op; + int packet_size = FISBONE_SIZE + fp->current_header_size; + + memset (&op, 0, sizeof (op)); + op.packet = _ogg_calloc (packet_size, sizeof(unsigned char)); + memset (op.packet, 0, packet_size); + memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */ + *((ogg_uint32_t*)(op.packet+8)) = FISBONE_MESSAGE_HEADER_OFFSET; /* offset of the message header fields */ + *((ogg_uint32_t*)(op.packet+12)) = fp->serial_no; /* serialno of the respective stream */ + *((ogg_uint32_t*)(op.packet+16)) = fp->nr_header_packet; /* number of header packets */ + *((ogg_int64_t*)(op.packet+20)) = fp->granule_rate_n; /* granulrate numerator */ + *((ogg_int64_t*)(op.packet+28)) = fp->granule_rate_d; /* granulrate denominator */ + *((ogg_int64_t*)(op.packet+36)) = fp->start_granule; /* start granule */ + *((ogg_uint32_t*)(op.packet+44)) = fp->preroll; /* preroll, for theora its 0 */ + *(op.packet+48) = fp->granule_shift; /* granule shift */ + memcpy((op.packet+FISBONE_SIZE), fp->message_header_fields, fp->current_header_size); + + op.b_o_s = 0; + op.e_o_s = 0; + op.bytes = packet_size; /* size of the packet in bytes */ + + return op; +} + +/* fills up a fishead_packet from a fishead ogg_packet of a skeleton bistream */ +fishead_packet fishead_from_ogg(ogg_packet *op) { + + fishead_packet fp; + + if (memcmp(op->packet, FISHEAD_IDENTIFIER, 8)) + ; /* invalid packet what do we do? */ + + fp.version_major = *((ogg_uint16_t*)(op->packet+8)); /* version major */ + fp.version_minor = *((ogg_uint16_t*)(op->packet+10)); /* version minor */ + fp.ptime_n = *((ogg_int64_t*)(op->packet+12)); /* presentationtime numerator */ + fp.ptime_d = *((ogg_int64_t*)(op->packet+20)); /* presentationtime denominator */ + fp.btime_n = *((ogg_int64_t*)(op->packet+28)); /* basetime numerator */ + fp.btime_d = *((ogg_int64_t*)(op->packet+36)); /* basetime denominator */ + memcpy(fp.UTC, op->packet+44, 20); + + return fp; +} + +/* fills up a fisbone_packet from a fisbone ogg_packet of a skeleton bitstream */ +fisbone_packet fisbone_from_ogg(ogg_packet *op) { + + fisbone_packet fp; + + if (memcmp(op->packet, FISBONE_IDENTIFIER, 8)) + ; /* invalid value, what do we do? */ + fp.serial_no = *((ogg_uint32_t*)(op->packet+12)); /* serialno of the stream represented by this fisbone packet */ + fp.nr_header_packet = *((ogg_uint32_t*)(op->packet+16)); /* number of header packets */ + fp.granule_rate_n = *((ogg_int64_t*)(op->packet+20)); /* granulrate numerator */ + fp.granule_rate_d = *((ogg_int64_t*)(op->packet+28)); /* granulrate denominator */ + fp.start_granule = *((ogg_int64_t*)(op->packet+36)); /* start granule */ + fp.preroll = *((ogg_uint32_t*)(op->packet+44)); /* preroll, for theora its 0 */ + fp.granule_shift = *(op->packet+48); /* granule shift */ + fp.current_header_size = op->bytes - FISBONE_SIZE; + fp.message_header_fields = _ogg_calloc(fp.current_header_size+1, sizeof(char)); + memcpy(fp.message_header_fields, op->packet+FISBONE_SIZE, fp.current_header_size); + + return fp; +} + +int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp) { + + ogg_packet op; + + op = ogg_from_fishead(fp); + ogg_stream_packetin(os, &op); + _ogg_free(op.packet); + + return 0; +} + +int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp) { + + ogg_packet op; + + op = ogg_from_fisbone(fp); + ogg_stream_packetin(os, &op); + _ogg_free(op.packet); + + return 0; +} + +int add_eos_packet_to_stream(ogg_stream_state *os) { + + ogg_packet op; + + memset (&op, 0, sizeof(op)); + op.e_o_s = 1; + ogg_stream_packetin(os, &op); + + return 0; +} + +int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out) { + + ogg_page og; + int result; + + while((result = ogg_stream_flush(os, &og))) + { + if(!result) break; + result = write_ogg_page_to_file(&og, out); + if(result != og.header_len + og.body_len) + return 1; + } + + return 0; +} |