OSDN Git Service

6a3b1ec5d4de9305a604e0a570f48e170dc6187e
[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.m0k.org/>.
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       = job->acodec;
139         mux_data->i_packet_no = 0;
140         audio->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
147     /* Video */
148     mux_data = job->mux_data;
149     memset( &h, 0, sizeof( ogg_stream_header_t ) );
150     h.i_packet_type = 0x01;
151     memcpy( h.stream_type, "video    ", 8 );
152     if( mux_data->codec == HB_VCODEC_X264 )
153     {
154         memcpy( h.sub_type, "H264", 4 );
155     }
156     else if( mux_data->codec == HB_VCODEC_XVID )
157     {
158         memcpy( h.sub_type, "XVID", 4 );
159     }
160     else
161     {
162         memcpy( h.sub_type, "DX50", 4 );
163     }
164     SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
165     SetQWLE( &h.i_time_unit, (int64_t) 10 * 1000 * 1000 *
166              (int64_t) job->vrate_base / (int64_t) job->vrate );
167     SetQWLE( &h.i_samples_per_unit, 1 );
168     SetDWLE( &h.i_default_len, 0 );
169     SetDWLE( &h.i_buffer_size, 1024*1024 );
170     SetWLE ( &h.i_bits_per_sample, 0 );
171     SetDWLE( &h.header.video.i_width,  job->width );
172     SetDWLE( &h.header.video.i_height, job->height );
173     op.packet   = (unsigned char*)&h;
174     op.bytes    = sizeof( ogg_stream_header_t );
175     op.b_o_s    = 1;
176     op.e_o_s    = 0;
177     op.granulepos = 0;
178     op.packetno = mux_data->i_packet_no++;
179     ogg_stream_packetin( &mux_data->os, &op );
180     OGMFlush( m, mux_data );
181
182     /* Audio */
183     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
184     {
185         audio   = hb_list_item( title->list_audio, i );
186         mux_data = audio->mux_data;
187         memset( &h, 0, sizeof( ogg_stream_header_t ) );
188         switch( job->acodec )
189         {
190             case HB_ACODEC_LAME:
191             {
192                 h.i_packet_type = 0x01;
193                 memcpy( h.stream_type, "audio    ", 8 );
194                 memcpy( h.sub_type, "55  ", 4 );
195
196                 SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1);
197                 SetQWLE( &h.i_time_unit, 0 );
198                 SetQWLE( &h.i_samples_per_unit, job->arate );
199                 SetDWLE( &h.i_default_len, 1 );
200                 SetDWLE( &h.i_buffer_size, 30 * 1024 );
201                 SetWLE ( &h.i_bits_per_sample, 0 );
202
203                 SetDWLE( &h.header.audio.i_channels, 2 ); 
204                 SetDWLE( &h.header.audio.i_block_align, 0 );
205                 SetDWLE( &h.header.audio.i_avgbytespersec,
206                          job->abitrate / 8 );
207
208                 op.packet   = (char*) &h;
209                 op.bytes    = sizeof( ogg_stream_header_t );
210                 op.b_o_s    = 1;
211                 op.e_o_s    = 0;
212                 op.granulepos = 0;
213                 op.packetno = mux_data->i_packet_no++;
214                 ogg_stream_packetin( &mux_data->os, &op );
215                 break;
216             }
217             case HB_ACODEC_VORBIS:
218             {
219                 memcpy( &op, audio->config.vorbis.headers[0],
220                         sizeof( ogg_packet ) );
221                 op.packet = audio->config.vorbis.headers[0] +
222                                 sizeof( ogg_packet );
223                 ogg_stream_packetin( &mux_data->os, &op );
224                 break;
225             }
226             default:
227                 hb_log( "muxogm: unhandled codec" );
228                 break;
229         }
230         OGMFlush( m, mux_data );
231     }
232
233     /* second pass: all non b_o_s packets */
234     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
235     {
236         audio = hb_list_item( title->list_audio, i );
237         if( job->acodec == HB_ACODEC_VORBIS )
238         {
239             int       j;
240             mux_data = audio->mux_data;
241
242             for( j = 1; j < 3; j++ )
243             {
244                 memcpy( &op, audio->config.vorbis.headers[j],
245                         sizeof( ogg_packet ) );
246                 op.packet = audio->config.vorbis.headers[j] +
247                                 sizeof( ogg_packet );
248                 ogg_stream_packetin( &mux_data->os, &op );
249
250                 OGMFlush( m, mux_data );
251             }
252         }
253     }
254     hb_log( "muxogm: headers written" );
255
256     return 0;
257 }
258
259 static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
260                    hb_buffer_t * buf )
261 {
262     ogg_packet   op;
263
264     switch( mux_data->codec )
265     {
266         case HB_VCODEC_FFMPEG:
267         case HB_VCODEC_XVID:
268         case HB_VCODEC_X264:
269             op.bytes  = buf->size + 1;
270             op.packet = malloc( op.bytes );
271             op.packet[0] = buf->key ? 0x08 : 0x00;
272             memcpy( &op.packet[1], buf->data, buf->size );
273             op.b_o_s       = 0;
274             op.e_o_s       = 0;
275             op.granulepos  = mux_data->i_packet_no;
276             op.packetno    = mux_data->i_packet_no++;
277             break;
278         case HB_ACODEC_LAME:
279             op.bytes  = buf->size + 1;
280             op.packet = malloc( op.bytes );
281             op.packet[0] = 0x08;
282             memcpy( &op.packet[1], buf->data, buf->size );
283             op.b_o_s       = 0;
284             op.e_o_s       = 0;
285             op.granulepos  = mux_data->i_packet_no * 1152;
286             op.packetno    = mux_data->i_packet_no++;
287             break;
288         case HB_ACODEC_VORBIS:
289             memcpy( &op, buf->data, sizeof( ogg_packet ) );
290             op.packet = malloc( op.bytes );
291             memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes );
292             break;
293
294         default:
295             hb_log( "muxogm: unhandled codec" );
296             op.bytes = 0;
297             op.packet = NULL;
298             break;
299     }
300
301     if( op.packet )
302     {
303         ogg_stream_packetin( &mux_data->os, &op );
304
305         for( ;; )
306         {
307             ogg_page og;
308             if( ogg_stream_pageout( &mux_data->os, &og ) == 0 )
309             {
310                 break;
311             }
312
313             if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 ||
314                 fwrite( og.body, og.body_len, 1, m->file ) <= 0 )
315             {
316                 hb_log( "muxogm: write failed" );
317                 break;
318             }
319         }
320         free( op.packet );
321     }
322     return 0; 
323 }
324
325 static int OGMEnd( hb_mux_object_t * m )
326 {
327     hb_job_t * job = m->job;
328
329     hb_title_t * title = job->title;
330     hb_audio_t    * audio;
331     hb_mux_data_t * mux_data;
332     int          i;
333
334     mux_data = job->mux_data;
335     if( OGMFlush( m, mux_data ) < 0 )
336     {
337         return -1;
338     }
339     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
340     {
341         audio = hb_list_item( title->list_audio, i );
342         mux_data = audio->mux_data;
343         if( OGMFlush( m, mux_data ) < 0 )
344         {
345             return -1;
346         }
347     }
348
349     fclose( m->file );
350     hb_log( "muxogm: `%s' closed", job->file );
351     
352     return 0;
353 }
354
355 hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job )
356 {
357     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
358     m->init      = OGMInit;
359     m->mux       = OGMMux;
360     m->end       = OGMEnd;
361     m->job       = job;
362     return m;
363 }
364