OSDN Git Service

SunOS support fixed with new ffmpeg and x264
[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_XVID:
157         case HB_VCODEC_X264:
158         case HB_VCODEC_FFMPEG:
159         {
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 )
164                 {
165                     memcpy( h.sub_type, "H264", 4 );
166                 }
167                 else if( mux_data->codec == HB_VCODEC_XVID )
168                 {
169                     memcpy( h.sub_type, "XVID", 4 );
170                 }
171                 else
172                 {
173                     memcpy( h.sub_type, "DX50", 4 );
174                 }
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 );
186                 op.b_o_s    = 1;
187                 op.e_o_s    = 0;
188                 op.granulepos = 0;
189                 op.packetno = mux_data->i_packet_no++;
190                 ogg_stream_packetin( &mux_data->os, &op );
191                 break;
192             }
193         default:
194             hb_error( "muxogm: unhandled video codec" );
195             *job->die = 1;
196     }
197     OGMFlush( m, mux_data );
198
199     /* Audio */
200     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
201     {
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 )
206         {
207             case HB_ACODEC_LAME:
208             {
209                 h.i_packet_type = 0x01;
210                 memcpy( h.stream_type, "audio    ", 8 );
211                 memcpy( h.sub_type, "55  ", 4 );
212
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 );
219
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 );
224
225                 op.packet   = (unsigned char*) &h;
226                 op.bytes    = sizeof( ogg_stream_header_t );
227                 op.b_o_s    = 1;
228                 op.e_o_s    = 0;
229                 op.granulepos = 0;
230                 op.packetno = mux_data->i_packet_no++;
231                 ogg_stream_packetin( &mux_data->os, &op );
232                 break;
233             }
234             case HB_ACODEC_VORBIS:
235             {
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 );
241                 break;
242             }
243             default:
244                 hb_log( "muxogm: unhandled codec" );
245                 break;
246         }
247         OGMFlush( m, mux_data );
248     }
249
250     /* second pass: all non b_o_s packets */
251     hb_log("muxogm: Writing non b_o_s header packets");
252     /* Video */
253     mux_data = job->mux_data;
254     switch( job->vcodec )
255     {
256         case HB_VCODEC_THEORA:
257             for (i = 1; i < 3; i++)
258             {
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 );
263             }
264             break;
265         case HB_VCODEC_XVID:
266         case HB_VCODEC_X264:
267         case HB_VCODEC_FFMPEG:
268             break;
269         default:
270             hb_error( "muxogm: unhandled video codec" );
271             *job->die = 1;
272     }
273
274     /* Audio */
275     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
276     {
277         audio = hb_list_item( title->list_audio, i );
278         if( audio->config.out.codec == HB_ACODEC_VORBIS )
279         {
280             int       j;
281             mux_data = audio->priv.mux_data;
282
283             for( j = 1; j < 3; j++ )
284             {
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 );
290
291                 OGMFlush( m, mux_data );
292             }
293         }
294     }
295     hb_log( "muxogm: headers written" );
296
297     return 0;
298 }
299
300 static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
301                    hb_buffer_t * buf )
302 {
303     ogg_packet   op;
304
305     switch( mux_data->codec )
306     {
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 );
311             break;
312         case HB_VCODEC_FFMPEG:
313         case HB_VCODEC_XVID:
314         case HB_VCODEC_X264:
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 );
319             op.b_o_s       = 0;
320             op.e_o_s       = 0;
321             op.granulepos  = mux_data->i_packet_no;
322             op.packetno    = mux_data->i_packet_no++;
323             break;
324         case HB_ACODEC_LAME:
325             op.bytes  = buf->size + 1;
326             op.packet = malloc( op.bytes );
327             op.packet[0] = 0x08;
328             memcpy( &op.packet[1], buf->data, buf->size );
329             op.b_o_s       = 0;
330             op.e_o_s       = 0;
331             op.granulepos  = mux_data->i_packet_no * 1152;
332             op.packetno    = mux_data->i_packet_no++;
333             break;
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 );
338             break;
339
340         default:
341             hb_log( "muxogm: unhandled codec" );
342             op.bytes = 0;
343             op.packet = NULL;
344             break;
345     }
346
347     if( op.packet )
348     {
349         ogg_stream_packetin( &mux_data->os, &op );
350
351         for( ;; )
352         {
353             ogg_page og;
354             if( ogg_stream_pageout( &mux_data->os, &og ) == 0 )
355             {
356                 break;
357             }
358
359             if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
360                 fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
361             {
362                 hb_log( "muxogm: write failed" );
363                 break;
364             }
365         }
366         free( op.packet );
367     }
368     return 0;
369 }
370
371 static int OGMEnd( hb_mux_object_t * m )
372 {
373     hb_job_t * job = m->job;
374
375     hb_title_t * title = job->title;
376     hb_audio_t    * audio;
377     hb_mux_data_t * mux_data;
378     int          i;
379
380     mux_data = job->mux_data;
381     if( OGMFlush( m, mux_data ) < 0 )
382     {
383         return -1;
384     }
385     ogg_stream_clear( &mux_data->os );
386
387     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
388     {
389         audio = hb_list_item( title->list_audio, i );
390         mux_data = audio->priv.mux_data;
391         if( OGMFlush( m, mux_data ) < 0 )
392         {
393             return -1;
394         }
395         ogg_stream_clear( &mux_data->os );
396     }
397
398     fclose( m->file );
399     hb_log( "muxogm: `%s' closed", job->file );
400
401     return 0;
402 }
403
404 hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job )
405 {
406     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
407     m->init      = OGMInit;
408     m->mux       = OGMMux;
409     m->end       = OGMEnd;
410     m->job       = job;
411     return m;
412 }
413