OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / libhb / muxogm.c
1 /* $Id: muxogm.c,v 1.4 2005/02/20 00:41:56 titer Exp $
2
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. */
6
7 #include "hb.h"
8
9 #include <ogg/ogg.h>
10
11 struct hb_mux_object_s
12 {
13     HB_MUX_COMMON;
14
15     hb_job_t * job;
16
17     FILE * file;
18 };
19
20 struct hb_mux_data_s
21 {
22     int              codec;
23     ogg_stream_state os;
24     int              i_packet_no;
25 };
26
27 typedef struct __attribute__((__packed__))
28 {
29     uint8_t i_packet_type;
30
31     char stream_type[8];
32     char sub_type[4];
33
34     int32_t i_size;
35
36     int64_t i_time_unit;
37     int64_t i_samples_per_unit;
38     int32_t i_default_len;
39
40     int32_t i_buffer_size;
41     int16_t i_bits_per_sample;
42     int16_t i_padding_0;            // hum hum
43     union
44     {
45         struct
46         {
47             int32_t i_width;
48             int32_t i_height;
49
50         } video;
51         struct
52         {
53             int16_t i_channels;
54             int16_t i_block_align;
55             int32_t i_avgbytespersec;
56         } audio;
57     } header;
58
59 } ogg_stream_header_t;
60
61 #define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
62 static void _SetWLE( uint8_t *p, uint16_t i_dw )
63 {
64     p[1] = ( i_dw >>  8 )&0xff;
65     p[0] = ( i_dw       )&0xff;
66 }
67
68 #define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
69 static void _SetDWLE( uint8_t *p, uint32_t i_dw )
70 {
71     p[3] = ( i_dw >> 24 )&0xff;
72     p[2] = ( i_dw >> 16 )&0xff;
73     p[1] = ( i_dw >>  8 )&0xff;
74     p[0] = ( i_dw       )&0xff;
75 }
76 #define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
77 static void _SetQWLE( uint8_t *p, uint64_t i_qw )
78 {
79     SetDWLE( p,   i_qw&0xffffffff );
80     SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
81 }
82
83 static int OGMFlush( hb_mux_object_t * m, hb_mux_data_t * mux_data )
84 {
85     for( ;; )
86     {
87         ogg_page og;
88         if( ogg_stream_flush( &mux_data->os, &og ) == 0 )
89         {
90             break;
91         }
92         if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
93             fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
94         {
95             return -1;
96         }
97     }
98     return 0;
99 }
100
101 /**********************************************************************
102  * OGMInit
103  **********************************************************************
104  * Allocates hb_mux_data_t structures, create file and write headers
105  *********************************************************************/
106 static int OGMInit( hb_mux_object_t * m )
107 {
108     hb_job_t   * job   = m->job;
109     hb_title_t * title = job->title;
110
111     hb_audio_t    * audio;
112     hb_mux_data_t * mux_data;
113     int i;
114
115     ogg_packet          op;
116     ogg_stream_header_t h;
117
118     /* Open output file */
119     if( ( m->file = fopen( job->file, "wb" ) ) == NULL )
120     {
121         hb_log( "muxogm: failed to open `%s'", job->file );
122         return -1;
123     }
124     hb_log( "muxogm: `%s' opened", job->file );
125
126     /* Video track */
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 );
132
133     /* Audio */
134     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
135     {
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 );
142     }
143
144
145     /* First pass: all b_o_s packets */
146     hb_log("muxogm: Writing b_o_s header packets");
147     /* Video */
148     mux_data = job->mux_data;
149     switch( job->vcodec )
150     {
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 );
155             break;
156         case HB_VCODEC_X264:
157         case HB_VCODEC_FFMPEG:
158         {
159                 memset( &h, 0, sizeof( ogg_stream_header_t ) );
160                 h.i_packet_type = 0x01;
161                 memcpy( h.stream_type, "video    ", 8 );
162                 if( mux_data->codec == HB_VCODEC_X264 )
163                 {
164                     memcpy( h.sub_type, "H264", 4 );
165                 }
166                 else
167                 {
168                     memcpy( h.sub_type, "DX50", 4 );
169                 }
170                 SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
171                 SetQWLE( &h.i_time_unit, (int64_t) 10 * 1000 * 1000 *
172                          (int64_t) job->vrate_base / (int64_t) job->vrate );
173                 SetQWLE( &h.i_samples_per_unit, 1 );
174                 SetDWLE( &h.i_default_len, 0 );
175                 SetDWLE( &h.i_buffer_size, 1024*1024 );
176                 SetWLE ( &h.i_bits_per_sample, 0 );
177                 SetDWLE( &h.header.video.i_width,  job->width );
178                 SetDWLE( &h.header.video.i_height, job->height );
179                 op.packet   = (unsigned char*)&h;
180                 op.bytes    = sizeof( ogg_stream_header_t );
181                 op.b_o_s    = 1;
182                 op.e_o_s    = 0;
183                 op.granulepos = 0;
184                 op.packetno = mux_data->i_packet_no++;
185                 ogg_stream_packetin( &mux_data->os, &op );
186                 break;
187             }
188         default:
189             hb_error( "muxogm: unhandled video codec" );
190             *job->die = 1;
191     }
192     OGMFlush( m, mux_data );
193
194     /* Audio */
195     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
196     {
197         audio   = hb_list_item( title->list_audio, i );
198         mux_data = audio->priv.mux_data;
199         memset( &h, 0, sizeof( ogg_stream_header_t ) );
200         switch( audio->config.out.codec )
201         {
202             case HB_ACODEC_LAME:
203             {
204                 h.i_packet_type = 0x01;
205                 memcpy( h.stream_type, "audio    ", 8 );
206                 memcpy( h.sub_type, "55  ", 4 );
207
208                 SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
209                 SetQWLE( &h.i_time_unit, 0 );
210                 SetQWLE( &h.i_samples_per_unit, audio->config.out.samplerate );
211                 SetDWLE( &h.i_default_len, 1 );
212                 SetDWLE( &h.i_buffer_size, 30 * 1024 );
213                 SetWLE ( &h.i_bits_per_sample, 0 );
214
215                 SetDWLE( &h.header.audio.i_channels, 2 );
216                 SetDWLE( &h.header.audio.i_block_align, 0 );
217                 SetDWLE( &h.header.audio.i_avgbytespersec,
218                          audio->config.out.bitrate / 8 );
219
220                 op.packet   = (unsigned char*) &h;
221                 op.bytes    = sizeof( ogg_stream_header_t );
222                 op.b_o_s    = 1;
223                 op.e_o_s    = 0;
224                 op.granulepos = 0;
225                 op.packetno = mux_data->i_packet_no++;
226                 ogg_stream_packetin( &mux_data->os, &op );
227                 break;
228             }
229             case HB_ACODEC_VORBIS:
230             {
231                 memcpy( &op, audio->priv.config.vorbis.headers[0],
232                         sizeof( ogg_packet ) );
233                 op.packet = audio->priv.config.vorbis.headers[0] +
234                                 sizeof( ogg_packet );
235                 ogg_stream_packetin( &mux_data->os, &op );
236                 break;
237             }
238             default:
239                 hb_log( "muxogm: unhandled codec" );
240                 break;
241         }
242         OGMFlush( m, mux_data );
243     }
244
245     /* second pass: all non b_o_s packets */
246     hb_log("muxogm: Writing non b_o_s header packets");
247     /* Video */
248     mux_data = job->mux_data;
249     switch( job->vcodec )
250     {
251         case HB_VCODEC_THEORA:
252             for (i = 1; i < 3; i++)
253             {
254                 memcpy(&op, job->config.theora.headers[i], sizeof(op));
255                 op.packet = job->config.theora.headers[i] + sizeof(op);
256                 ogg_stream_packetin( &mux_data->os, &op );
257                 OGMFlush( m, mux_data );
258             }
259             break;
260         case HB_VCODEC_X264:
261         case HB_VCODEC_FFMPEG:
262             break;
263         default:
264             hb_error( "muxogm: unhandled video codec" );
265             *job->die = 1;
266     }
267
268     /* Audio */
269     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
270     {
271         audio = hb_list_item( title->list_audio, i );
272         if( audio->config.out.codec == HB_ACODEC_VORBIS )
273         {
274             int       j;
275             mux_data = audio->priv.mux_data;
276
277             for( j = 1; j < 3; j++ )
278             {
279                 memcpy( &op, audio->priv.config.vorbis.headers[j],
280                         sizeof( ogg_packet ) );
281                 op.packet = audio->priv.config.vorbis.headers[j] +
282                                 sizeof( ogg_packet );
283                 ogg_stream_packetin( &mux_data->os, &op );
284
285                 OGMFlush( m, mux_data );
286             }
287         }
288     }
289     hb_log( "muxogm: headers written" );
290
291     return 0;
292 }
293
294 static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
295                    hb_buffer_t * buf )
296 {
297     ogg_packet   op;
298
299     switch( mux_data->codec )
300     {
301         case HB_VCODEC_THEORA:
302             memcpy( &op, buf->data, sizeof( ogg_packet ) );
303             op.packet = malloc( op.bytes );
304             memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes );
305             break;
306         case HB_VCODEC_FFMPEG:
307         case HB_VCODEC_X264:
308             op.bytes  = buf->size + 1;
309             op.packet = malloc( op.bytes );
310             op.packet[0] = (buf->frametype & HB_FRAME_KEY) ? 0x08 : 0x00;
311             memcpy( &op.packet[1], buf->data, buf->size );
312             op.b_o_s       = 0;
313             op.e_o_s       = 0;
314             op.granulepos  = mux_data->i_packet_no;
315             op.packetno    = mux_data->i_packet_no++;
316             break;
317         case HB_ACODEC_LAME:
318             op.bytes  = buf->size + 1;
319             op.packet = malloc( op.bytes );
320             op.packet[0] = 0x08;
321             memcpy( &op.packet[1], buf->data, buf->size );
322             op.b_o_s       = 0;
323             op.e_o_s       = 0;
324             op.granulepos  = mux_data->i_packet_no * 1152;
325             op.packetno    = mux_data->i_packet_no++;
326             break;
327         case HB_ACODEC_VORBIS:
328             memcpy( &op, buf->data, sizeof( ogg_packet ) );
329             op.packet = malloc( op.bytes );
330             memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes );
331             break;
332
333         default:
334             hb_log( "muxogm: unhandled codec" );
335             op.bytes = 0;
336             op.packet = NULL;
337             break;
338     }
339
340     if( op.packet )
341     {
342         ogg_stream_packetin( &mux_data->os, &op );
343
344         for( ;; )
345         {
346             ogg_page og;
347             if( ogg_stream_pageout( &mux_data->os, &og ) == 0 )
348             {
349                 break;
350             }
351
352             if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
353                 fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
354             {
355                 hb_log( "muxogm: write failed" );
356                 break;
357             }
358         }
359         free( op.packet );
360     }
361     return 0;
362 }
363
364 static int OGMEnd( hb_mux_object_t * m )
365 {
366     hb_job_t * job = m->job;
367
368     hb_title_t * title = job->title;
369     hb_audio_t    * audio;
370     hb_mux_data_t * mux_data;
371     int          i;
372
373     mux_data = job->mux_data;
374     if( OGMFlush( m, mux_data ) < 0 )
375     {
376         return -1;
377     }
378     ogg_stream_clear( &mux_data->os );
379
380     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
381     {
382         audio = hb_list_item( title->list_audio, i );
383         mux_data = audio->priv.mux_data;
384         if( OGMFlush( m, mux_data ) < 0 )
385         {
386             return -1;
387         }
388         ogg_stream_clear( &mux_data->os );
389     }
390
391     fclose( m->file );
392     hb_log( "muxogm: `%s' closed", job->file );
393
394     return 0;
395 }
396
397 hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job )
398 {
399     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
400     m->init      = OGMInit;
401     m->mux       = OGMMux;
402     m->end       = OGMEnd;
403     m->job       = job;
404     return m;
405 }
406