1 /* $Id: muxogm.c,v 1.4 2005/02/20 00:41:56 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
11 struct hb_mux_object_s
27 typedef struct __attribute__((__packed__))
29 uint8_t i_packet_type;
37 int64_t i_samples_per_unit;
38 int32_t i_default_len;
40 int32_t i_buffer_size;
41 int16_t i_bits_per_sample;
42 int16_t i_padding_0; // hum hum
54 int16_t i_block_align;
55 int32_t i_avgbytespersec;
59 } ogg_stream_header_t;
61 #define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
62 static void _SetWLE( uint8_t *p, uint16_t i_dw )
64 p[1] = ( i_dw >> 8 )&0xff;
68 #define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
69 static void _SetDWLE( uint8_t *p, uint32_t i_dw )
71 p[3] = ( i_dw >> 24 )&0xff;
72 p[2] = ( i_dw >> 16 )&0xff;
73 p[1] = ( i_dw >> 8 )&0xff;
76 #define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
77 static void _SetQWLE( uint8_t *p, uint64_t i_qw )
79 SetDWLE( p, i_qw&0xffffffff );
80 SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
83 static int OGMFlush( hb_mux_object_t * m, hb_mux_data_t * mux_data )
88 if( ogg_stream_flush( &mux_data->os, &og ) == 0 )
92 if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
93 fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
101 /**********************************************************************
103 **********************************************************************
104 * Allocates hb_mux_data_t structures, create file and write headers
105 *********************************************************************/
106 static int OGMInit( hb_mux_object_t * m )
108 hb_job_t * job = m->job;
109 hb_title_t * title = job->title;
112 hb_mux_data_t * mux_data;
116 ogg_stream_header_t h;
118 /* Open output file */
119 if( ( m->file = fopen( job->file, "wb" ) ) == NULL )
121 hb_log( "muxogm: failed to open `%s'", job->file );
124 hb_log( "muxogm: `%s' opened", job->file );
127 mux_data = malloc( sizeof( hb_mux_data_t ) );
128 mux_data->codec = job->vcodec;
129 mux_data->i_packet_no = 0;
130 job->mux_data = mux_data;
131 ogg_stream_init( &mux_data->os, 0 );
134 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
136 audio = hb_list_item( title->list_audio, i );
137 mux_data = malloc( sizeof( hb_mux_data_t ) );
138 mux_data->codec = audio->config.out.codec;
139 mux_data->i_packet_no = 0;
140 audio->priv.mux_data = mux_data;
141 ogg_stream_init( &mux_data->os, i + 1 );
145 /* First pass: all b_o_s packets */
146 hb_log("muxogm: Writing b_o_s header packets");
148 mux_data = job->mux_data;
149 switch( job->vcodec )
151 case HB_VCODEC_THEORA:
152 memcpy(&op, job->config.theora.headers[0], sizeof(op));
153 op.packet = job->config.theora.headers[0] + sizeof(op);
154 ogg_stream_packetin( &mux_data->os, &op );
158 case HB_VCODEC_FFMPEG:
160 memset( &h, 0, sizeof( ogg_stream_header_t ) );
161 h.i_packet_type = 0x01;
162 memcpy( h.stream_type, "video ", 8 );
163 if( mux_data->codec == HB_VCODEC_X264 )
165 memcpy( h.sub_type, "H264", 4 );
167 else if( mux_data->codec == HB_VCODEC_XVID )
169 memcpy( h.sub_type, "XVID", 4 );
173 memcpy( h.sub_type, "DX50", 4 );
175 SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
176 SetQWLE( &h.i_time_unit, (int64_t) 10 * 1000 * 1000 *
177 (int64_t) job->vrate_base / (int64_t) job->vrate );
178 SetQWLE( &h.i_samples_per_unit, 1 );
179 SetDWLE( &h.i_default_len, 0 );
180 SetDWLE( &h.i_buffer_size, 1024*1024 );
181 SetWLE ( &h.i_bits_per_sample, 0 );
182 SetDWLE( &h.header.video.i_width, job->width );
183 SetDWLE( &h.header.video.i_height, job->height );
184 op.packet = (unsigned char*)&h;
185 op.bytes = sizeof( ogg_stream_header_t );
189 op.packetno = mux_data->i_packet_no++;
190 ogg_stream_packetin( &mux_data->os, &op );
194 hb_error( "muxogm: unhandled video codec" );
197 OGMFlush( m, mux_data );
200 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
202 audio = hb_list_item( title->list_audio, i );
203 mux_data = audio->priv.mux_data;
204 memset( &h, 0, sizeof( ogg_stream_header_t ) );
205 switch( audio->config.out.codec )
209 h.i_packet_type = 0x01;
210 memcpy( h.stream_type, "audio ", 8 );
211 memcpy( h.sub_type, "55 ", 4 );
213 SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
214 SetQWLE( &h.i_time_unit, 0 );
215 SetQWLE( &h.i_samples_per_unit, audio->config.out.samplerate );
216 SetDWLE( &h.i_default_len, 1 );
217 SetDWLE( &h.i_buffer_size, 30 * 1024 );
218 SetWLE ( &h.i_bits_per_sample, 0 );
220 SetDWLE( &h.header.audio.i_channels, 2 );
221 SetDWLE( &h.header.audio.i_block_align, 0 );
222 SetDWLE( &h.header.audio.i_avgbytespersec,
223 audio->config.out.bitrate / 8 );
225 op.packet = (unsigned char*) &h;
226 op.bytes = sizeof( ogg_stream_header_t );
230 op.packetno = mux_data->i_packet_no++;
231 ogg_stream_packetin( &mux_data->os, &op );
234 case HB_ACODEC_VORBIS:
236 memcpy( &op, audio->priv.config.vorbis.headers[0],
237 sizeof( ogg_packet ) );
238 op.packet = audio->priv.config.vorbis.headers[0] +
239 sizeof( ogg_packet );
240 ogg_stream_packetin( &mux_data->os, &op );
244 hb_log( "muxogm: unhandled codec" );
247 OGMFlush( m, mux_data );
250 /* second pass: all non b_o_s packets */
251 hb_log("muxogm: Writing non b_o_s header packets");
253 mux_data = job->mux_data;
254 switch( job->vcodec )
256 case HB_VCODEC_THEORA:
257 for (i = 1; i < 3; i++)
259 memcpy(&op, job->config.theora.headers[i], sizeof(op));
260 op.packet = job->config.theora.headers[i] + sizeof(op);
261 ogg_stream_packetin( &mux_data->os, &op );
262 OGMFlush( m, mux_data );
267 case HB_VCODEC_FFMPEG:
270 hb_error( "muxogm: unhandled video codec" );
275 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
277 audio = hb_list_item( title->list_audio, i );
278 if( audio->config.out.codec == HB_ACODEC_VORBIS )
281 mux_data = audio->priv.mux_data;
283 for( j = 1; j < 3; j++ )
285 memcpy( &op, audio->priv.config.vorbis.headers[j],
286 sizeof( ogg_packet ) );
287 op.packet = audio->priv.config.vorbis.headers[j] +
288 sizeof( ogg_packet );
289 ogg_stream_packetin( &mux_data->os, &op );
291 OGMFlush( m, mux_data );
295 hb_log( "muxogm: headers written" );
300 static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
305 switch( mux_data->codec )
307 case HB_VCODEC_THEORA:
308 memcpy( &op, buf->data, sizeof( ogg_packet ) );
309 op.packet = malloc( op.bytes );
310 memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes );
312 case HB_VCODEC_FFMPEG:
315 op.bytes = buf->size + 1;
316 op.packet = malloc( op.bytes );
317 op.packet[0] = (buf->frametype & HB_FRAME_KEY) ? 0x08 : 0x00;
318 memcpy( &op.packet[1], buf->data, buf->size );
321 op.granulepos = mux_data->i_packet_no;
322 op.packetno = mux_data->i_packet_no++;
325 op.bytes = buf->size + 1;
326 op.packet = malloc( op.bytes );
328 memcpy( &op.packet[1], buf->data, buf->size );
331 op.granulepos = mux_data->i_packet_no * 1152;
332 op.packetno = mux_data->i_packet_no++;
334 case HB_ACODEC_VORBIS:
335 memcpy( &op, buf->data, sizeof( ogg_packet ) );
336 op.packet = malloc( op.bytes );
337 memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes );
341 hb_log( "muxogm: unhandled codec" );
349 ogg_stream_packetin( &mux_data->os, &op );
354 if( ogg_stream_pageout( &mux_data->os, &og ) == 0 )
359 if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
360 fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
362 hb_log( "muxogm: write failed" );
371 static int OGMEnd( hb_mux_object_t * m )
373 hb_job_t * job = m->job;
375 hb_title_t * title = job->title;
377 hb_mux_data_t * mux_data;
380 mux_data = job->mux_data;
381 if( OGMFlush( m, mux_data ) < 0 )
385 ogg_stream_clear( &mux_data->os );
387 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
389 audio = hb_list_item( title->list_audio, i );
390 mux_data = audio->priv.mux_data;
391 if( OGMFlush( m, mux_data ) < 0 )
395 ogg_stream_clear( &mux_data->os );
399 hb_log( "muxogm: `%s' closed", job->file );
404 hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job )
406 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );