diff options
Diffstat (limited to 'libhb/muxogm.c')
-rw-r--r-- | libhb/muxogm.c | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/libhb/muxogm.c b/libhb/muxogm.c new file mode 100644 index 000000000..d326c9828 --- /dev/null +++ b/libhb/muxogm.c @@ -0,0 +1,364 @@ +/* $Id: muxogm.c,v 1.4 2005/02/20 00:41:56 titer Exp $ + + This file is part of the HandBrake source code. + Homepage: <http://handbrake.m0k.org/>. + It may be used under the terms of the GNU General Public License. */ + +#include "hb.h" + +#include <ogg/ogg.h> + +struct hb_mux_object_s +{ + HB_MUX_COMMON; + + hb_job_t * job; + + FILE * file; +}; + +struct hb_mux_data_s +{ + int codec; + ogg_stream_state os; + int i_packet_no; +}; + +typedef struct __attribute__((__packed__)) +{ + uint8_t i_packet_type; + + char stream_type[8]; + char sub_type[4]; + + int32_t i_size; + + int64_t i_time_unit; + int64_t i_samples_per_unit; + int32_t i_default_len; + + int32_t i_buffer_size; + int16_t i_bits_per_sample; + int16_t i_padding_0; // hum hum + union + { + struct + { + int32_t i_width; + int32_t i_height; + + } video; + struct + { + int16_t i_channels; + int16_t i_block_align; + int32_t i_avgbytespersec; + } audio; + } header; + +} ogg_stream_header_t; + +#define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v) +static void _SetWLE( uint8_t *p, uint16_t i_dw ) +{ + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} + +#define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v) +static void _SetDWLE( uint8_t *p, uint32_t i_dw ) +{ + p[3] = ( i_dw >> 24 )&0xff; + p[2] = ( i_dw >> 16 )&0xff; + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} +#define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v) +static void _SetQWLE( uint8_t *p, uint64_t i_qw ) +{ + SetDWLE( p, i_qw&0xffffffff ); + SetDWLE( p+4, ( i_qw >> 32)&0xffffffff ); +} + +static int OGMFlush( hb_mux_object_t * m, hb_mux_data_t * mux_data ) +{ + for( ;; ) + { + ogg_page og; + if( ogg_stream_flush( &mux_data->os, &og ) == 0 ) + { + break; + } + if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 || + fwrite( og.body, og.body_len, 1, m->file ) <= 0 ) + { + return -1; + } + } + return 0; +} + +/********************************************************************** + * OGMInit + ********************************************************************** + * Allocates hb_mux_data_t structures, create file and write headers + *********************************************************************/ +static int OGMInit( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + hb_title_t * title = job->title; + + hb_audio_t * audio; + hb_mux_data_t * mux_data; + int i; + + ogg_packet op; + ogg_stream_header_t h; + + /* Open output file */ + if( ( m->file = fopen( job->file, "wb" ) ) == NULL ) + { + hb_log( "muxogm: failed to open `%s'", job->file ); + return -1; + } + hb_log( "muxogm: `%s' opened", job->file ); + + /* Video track */ + mux_data = malloc( sizeof( hb_mux_data_t ) ); + mux_data->codec = job->vcodec; + mux_data->i_packet_no = 0; + job->mux_data = mux_data; + ogg_stream_init( &mux_data->os, 0 ); + + /* Audio */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = malloc( sizeof( hb_mux_data_t ) ); + mux_data->codec = job->acodec; + mux_data->i_packet_no = 0; + audio->mux_data = mux_data; + ogg_stream_init( &mux_data->os, i + 1 ); + } + + + /* First pass: all b_o_s packets */ + + /* Video */ + mux_data = job->mux_data; + memset( &h, 0, sizeof( ogg_stream_header_t ) ); + h.i_packet_type = 0x01; + memcpy( h.stream_type, "video ", 8 ); + if( mux_data->codec == HB_VCODEC_X264 ) + { + memcpy( h.sub_type, "H264", 4 ); + } + else if( mux_data->codec == HB_VCODEC_XVID ) + { + memcpy( h.sub_type, "XVID", 4 ); + } + else + { + memcpy( h.sub_type, "DX50", 4 ); + } + SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1); + SetQWLE( &h.i_time_unit, (int64_t) 10 * 1000 * 1000 * + (int64_t) job->vrate_base / (int64_t) job->vrate ); + SetQWLE( &h.i_samples_per_unit, 1 ); + SetDWLE( &h.i_default_len, 0 ); + SetDWLE( &h.i_buffer_size, 1024*1024 ); + SetWLE ( &h.i_bits_per_sample, 0 ); + SetDWLE( &h.header.video.i_width, job->width ); + SetDWLE( &h.header.video.i_height, job->height ); + op.packet = (char*)&h; + op.bytes = sizeof( ogg_stream_header_t ); + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = mux_data->i_packet_no++; + ogg_stream_packetin( &mux_data->os, &op ); + OGMFlush( m, mux_data ); + + /* Audio */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = audio->mux_data; + memset( &h, 0, sizeof( ogg_stream_header_t ) ); + switch( job->acodec ) + { + case HB_ACODEC_LAME: + { + h.i_packet_type = 0x01; + memcpy( h.stream_type, "audio ", 8 ); + memcpy( h.sub_type, "55 ", 4 ); + + SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1); + SetQWLE( &h.i_time_unit, 0 ); + SetQWLE( &h.i_samples_per_unit, job->arate ); + SetDWLE( &h.i_default_len, 1 ); + SetDWLE( &h.i_buffer_size, 30 * 1024 ); + SetWLE ( &h.i_bits_per_sample, 0 ); + + SetDWLE( &h.header.audio.i_channels, 2 ); + SetDWLE( &h.header.audio.i_block_align, 0 ); + SetDWLE( &h.header.audio.i_avgbytespersec, + job->abitrate / 8 ); + + op.packet = (char*) &h; + op.bytes = sizeof( ogg_stream_header_t ); + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = mux_data->i_packet_no++; + ogg_stream_packetin( &mux_data->os, &op ); + break; + } + case HB_ACODEC_VORBIS: + { + memcpy( &op, audio->config.vorbis.headers[0], + sizeof( ogg_packet ) ); + op.packet = audio->config.vorbis.headers[0] + + sizeof( ogg_packet ); + ogg_stream_packetin( &mux_data->os, &op ); + break; + } + default: + hb_log( "muxogm: unhandled codec" ); + break; + } + OGMFlush( m, mux_data ); + } + + /* second pass: all non b_o_s packets */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( job->acodec == HB_ACODEC_VORBIS ) + { + int j; + mux_data = audio->mux_data; + + for( j = 1; j < 3; j++ ) + { + memcpy( &op, audio->config.vorbis.headers[j], + sizeof( ogg_packet ) ); + op.packet = audio->config.vorbis.headers[j] + + sizeof( ogg_packet ); + ogg_stream_packetin( &mux_data->os, &op ); + + OGMFlush( m, mux_data ); + } + } + } + hb_log( "muxogm: headers written" ); + + return 0; +} + +static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data, + hb_buffer_t * buf ) +{ + ogg_packet op; + + switch( mux_data->codec ) + { + case HB_VCODEC_FFMPEG: + case HB_VCODEC_XVID: + case HB_VCODEC_X264: + op.bytes = buf->size + 1; + op.packet = malloc( op.bytes ); + op.packet[0] = buf->key ? 0x08 : 0x00; + memcpy( &op.packet[1], buf->data, buf->size ); + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = mux_data->i_packet_no; + op.packetno = mux_data->i_packet_no++; + break; + case HB_ACODEC_LAME: + op.bytes = buf->size + 1; + op.packet = malloc( op.bytes ); + op.packet[0] = 0x08; + memcpy( &op.packet[1], buf->data, buf->size ); + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = mux_data->i_packet_no * 1152; + op.packetno = mux_data->i_packet_no++; + break; + case HB_ACODEC_VORBIS: + memcpy( &op, buf->data, sizeof( ogg_packet ) ); + op.packet = malloc( op.bytes ); + memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes ); + break; + + default: + hb_log( "muxogm: unhandled codec" ); + op.bytes = 0; + op.packet = NULL; + break; + } + + if( op.packet ) + { + ogg_stream_packetin( &mux_data->os, &op ); + + for( ;; ) + { + ogg_page og; + if( ogg_stream_pageout( &mux_data->os, &og ) == 0 ) + { + break; + } + + if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 || + fwrite( og.body, og.body_len, 1, m->file ) <= 0 ) + { + hb_log( "muxogm: write failed" ); + break; + } + } + free( op.packet ); + } + return 0; +} + +static int OGMEnd( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + + hb_title_t * title = job->title; + hb_audio_t * audio; + hb_mux_data_t * mux_data; + int i; + + mux_data = job->mux_data; + if( OGMFlush( m, mux_data ) < 0 ) + { + return -1; + } + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = audio->mux_data; + if( OGMFlush( m, mux_data ) < 0 ) + { + return -1; + } + } + + fclose( m->file ); + hb_log( "muxogm: `%s' closed", job->file ); + + return 0; +} + +hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job ) +{ + hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 ); + m->init = OGMInit; + m->mux = OGMMux; + m->end = OGMEnd; + m->job = job; + return m; +} + |