Hi all.
I'm Mat & I'm new :)
I'm testing libtheora + libogg perhaps for a commercial product.
I started watching encoder_example.c ... I simplified it for testing
it easier.
It seems ok to me but I have no experience with theora so I would like
to know if my code is correct.
I tried to debug it with Valgrind and I found 4 possible memory leaks...
but I think they can be found in the original sample too (
encoder_example.c ) ... right?
( 1 about theora_encode_comment - row 195 )
( 3 about ogg_stream_init - row 133 )
Sorry for my english. Thanx in advance.
-Mat-
=== CODE ==
#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#ifndef _REENTRANT
# define _REENTRANT
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "theora/theora.h"
#define STREAM_EOP 1 // End-of-page
#define STREAM_EOD 2 // End-of-data
#define STREAM_EOS 3 // End-of-stream
#define SPIN "|/-\\"
int video_x = 0;
int video_y = 0;
int frame_x = 352;
int frame_y = 288;
int frame_x_offset = 0;
int frame_y_offset = 0;
int video_hzn = 25;
int video_hzd = 1;
int video_an = 1;
int video_ad = 1;
int video_r = 320000;
int video_q = 16;
int frame_counter = 250;
signed char *yuvframe;
int fetch_video_page( ogg_page *videopage, ogg_stream_state *to,
theora_state *td )
{
yuv_buffer yuv;
ogg_packet op;
yuv.y_width = video_x;
yuv.y_height = video_y;
yuv.y_stride = video_x;
yuv.uv_width = video_x / 2;
yuv.uv_height = video_y / 2;
yuv.uv_stride = video_x / 2;
yuv.y = yuvframe;
yuv.u = yuvframe + video_x * video_y;
yuv.v = yuvframe + video_x * video_y * 5 / 4;
/* is there a video page flushed? If not, work until there is. */
while( 1 )
{
frame_counter--;
fprintf( stderr, "\r%c", SPIN[frame_counter%4] );
/* fill the frame data */
memset( yuvframe, frame_counter % 255, video_x * video_y );
memset( yuvframe + video_x * video_y, frame_counter % 128,
video_x * video_y / 2 );
/* encode the frame */
theora_encode_YUVin( td, &yuv );
if( frame_counter < 0 )
theora_encode_packetout( td, 1, &op );
else
theora_encode_packetout( td, 0, &op );
ogg_stream_packetin( to, &op );
if( ogg_stream_pageout( to, videopage ) > 0 )
{
if( frame_counter < 0 )
return STREAM_EOD;
else
return STREAM_EOP;
}
if( ogg_stream_eos( to ) )
return STREAM_EOS;
}
/* unreachable */
return 0;
}
int main( int argc,char *argv[] )
{
ogg_stream_state to; /* take physical pages, weld into a logical
stream of packets */
ogg_stream_state vo; /* take physical pages, weld into a logical
stream of packets */
ogg_page og; /* one Ogg bitstream page. Vorbis packets
are inside */
ogg_packet op; /* one raw packet of data for decode */
theora_state td;
theora_info ti;
theora_comment tc;
int vkbps = 0;
int ret;
ogg_int64_t video_bytesout = 0;
double timebase;
FILE* outfile;
outfile = fopen( "test.ogg","wb" );
if( outfile == NULL )
{
fprintf( stderr, "Unable to open output file '%s'\n",
"test.ogg" );
exit( 1 );
}
srand( time( NULL ) );
{
/* need two inequal serial numbers */
int serial1, serial2;
serial1 = rand();
serial2 = rand();
if( serial1 == serial2 )
serial2++;
ogg_stream_init( &to,serial1 );
ogg_stream_init( &vo,serial2 );
}
/* Set up Theora encoder */
/* Theora has a divisible-by-sixteen restriction for the encoded video
size */
/* scale the frame size up to the nearest /16 and calculate offsets */
video_x = ( ( frame_x + 15 ) >> 4 ) << 4;
video_y = ( ( frame_y + 15 ) >> 4 ) << 4;
/* We force the offset to be even. This ensures that the chroma samples
align
properly with the luma samples. */
frame_x_offset = ( ( video_x - frame_x ) / 2 ) & ~1;
frame_y_offset = ( ( video_y - frame_y ) / 2 ) & ~1;
yuvframe = malloc( video_x * video_y * 3 / 2 );
theora_info_init( &ti );
ti.width = video_x;
ti.height = video_y;
ti.frame_width = frame_x;
ti.frame_height = frame_y;
ti.offset_x = frame_x_offset;
ti.offset_y = frame_y_offset;
ti.fps_numerator = video_hzn;
ti.fps_denominator = video_hzd;
ti.aspect_numerator = video_an;
ti.aspect_denominator = video_ad;
ti.colorspace = OC_CS_UNSPECIFIED;
ti.pixelformat = OC_PF_420;
ti.target_bitrate = video_r;
ti.quality = video_q;
ti.dropframes_p = 0;
ti.quick_p = 1;
ti.keyframe_auto_p = 1;
ti.keyframe_frequency = 64;
ti.keyframe_frequency_force = 64;
ti.keyframe_data_target_bitrate = video_r * 1.5;
ti.keyframe_auto_threshold = 80;
ti.keyframe_mindistance = 8;
ti.noise_sensitivity = 1;
theora_encode_init( &td, &ti );
theora_info_clear( &ti );
/* write the bitstream header packets with proper page interleave */
/* first packet will get its own page automatically */
theora_encode_header( &td, &op );
ogg_stream_packetin( &to, &op );
if( ogg_stream_pageout( &to, &og ) != 1 )
{
fprintf( stderr, "Internal Ogg library error.\n" );
exit( 1 );
}
fwrite( og.header, 1, og.header_len, outfile );
fwrite( og.body, 1, og.body_len, outfile );
/* create the remaining theora headers */
theora_comment_init( &tc );
theora_encode_comment( &tc, &op );
ogg_stream_packetin( &to, &op );
theora_encode_tables( &td, &op );
ogg_stream_packetin( &to, &op );
/* Flush the rest of our headers. This ensures the actual data in each
stream
will start on a new page, as per spec. */
while( 1 )
{
int result = ogg_stream_flush( &to, &og );
if( result < 0 )
{
fprintf( stderr, "Internal Ogg library error.\n" );
exit( 1 );
}
if( result == 0 )
break;
fwrite( og.header, 1, og.header_len, outfile );
fwrite( og.body, 1, og.body_len, outfile );
}
/* setup complete. Raw processing loop */
fprintf( stderr, "Compressing....\n" );
while( 1 )
{
double videotime;
ogg_page videopage;
/* is there a video page flushed? If not, fetch one if possible */
ret = fetch_video_page( &videopage, &to, &td );
/* no pages of either? Must be end of stream. */
if( ret == STREAM_EOS )
break;
/* Flush to stream */
videotime = theora_granule_time( &td,ogg_page_granulepos(
&videopage ) );
/* flush a video page */
video_bytesout += fwrite( videopage.header, 1,
videopage.header_len, outfile );
video_bytesout += fwrite( videopage.body, 1, videopage.body_len,
outfile );
timebase = videotime;
{
int hundredths = timebase * 100 - (long) timebase * 100;
int seconds = ( long) timebase % 60;
int minutes = ( (long) timebase / 60 ) % 60;
int hours = (long) timebase / 3600;
vkbps = rint( video_bytesout * 8. / timebase * .001 );
fprintf( stderr, "\r %d:%02d:%02d.%02d video:
%dkbps ", hours, minutes, seconds, hundredths, vkbps );
}
if( ret == STREAM_EOD )
break;
}
/* clear out state */
ogg_stream_clear( &to );
theora_clear( &td );
fclose( outfile );
free( yuvframe );
fprintf( stderr, "\r \ndone.\n\n" );
return 0;
}