OSDN Git Service

f06527295db4623454e450270e820b8f9f56ef41
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
1 /* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 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 /* libmp4v2 header */
8 #include "mp4.h"
9
10 #include "hb.h"
11
12 void AddIPodUUID(MP4FileHandle, MP4TrackId);
13
14
15 struct hb_mux_object_s
16 {
17     HB_MUX_COMMON;
18
19     hb_job_t * job;
20
21     /* libmp4v2 handle */
22     MP4FileHandle file;
23
24     /* Cumulated durations so far, in timescale units (see MP4Mux) */
25     uint64_t sum_dur;
26         
27 };
28
29 struct hb_mux_data_s
30 {
31     MP4TrackId track;
32 };
33
34 /**********************************************************************
35  * MP4Init
36  **********************************************************************
37  * Allocates hb_mux_data_t structures, create file and write headers
38  *********************************************************************/
39 static int MP4Init( hb_mux_object_t * m )
40 {
41     hb_job_t   * job   = m->job;
42     hb_title_t * title = job->title;
43     
44     hb_audio_t    * audio;
45     hb_mux_data_t * mux_data;
46     int i;
47     u_int16_t language_code;
48
49     /* Create an empty mp4 file */
50     m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
51
52     /* Video track */
53     mux_data      = malloc( sizeof( hb_mux_data_t ) );
54     job->mux_data = mux_data;
55
56     /* When using the standard 90000 timescale, QuickTime tends to have
57        synchronization issues (audio not playing at the correct speed).
58        To workaround this, we use the audio samplerate as the
59        timescale */
60     MP4SetTimeScale( m->file, job->arate );
61
62     if( job->vcodec == HB_VCODEC_X264 )
63     {
64         /* Stolen from mp4creator */
65         MP4SetVideoProfileLevel( m->file, 0x7F );
66
67                 if (job->areBframes == 1)
68                 {
69                         hb_log("muxmp4: Adjusting duration for B-frames");
70                     mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
71                             MP4_INVALID_DURATION+1, job->width, job->height,
72                             job->config.h264.sps[1], /* AVCProfileIndication */
73                             job->config.h264.sps[2], /* profile_compat */
74                             job->config.h264.sps[3], /* AVCLevelIndication */
75                             3 );      /* 4 bytes length before each NAL unit */                 
76                 }
77                 else
78                 {
79                         hb_log("muxmp4: Using default duration as there are no B-frames");
80                 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
81                         MP4_INVALID_DURATION, job->width, job->height,
82                         job->config.h264.sps[1], /* AVCProfileIndication */
83                         job->config.h264.sps[2], /* profile_compat */
84                         job->config.h264.sps[3], /* AVCLevelIndication */
85                         3 );      /* 4 bytes length before each NAL unit */
86                 }
87
88         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
89                 job->config.h264.sps, job->config.h264.sps_length );
90         MP4AddH264PictureParameterSet( m->file, mux_data->track,
91                 job->config.h264.pps, job->config.h264.pps_length );
92
93                 if( job->h264_level == 30)
94                 {
95                         hb_log("About to add iPod atom");
96                         AddIPodUUID(m->file, mux_data->track);
97                 }
98
99     }
100     else /* FFmpeg or XviD */
101     {
102         MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
103         mux_data->track = MP4AddVideoTrack( m->file, job->arate,
104                 MP4_INVALID_DURATION, job->width, job->height,
105                 MP4_MPEG4_VIDEO_TYPE );
106
107         /* VOL from FFmpeg or XviD */
108         MP4SetTrackESConfiguration( m->file, mux_data->track,
109                 job->config.mpeg4.bytes, job->config.mpeg4.length );
110     }
111
112         /* apply the anamorphic transformation matrix if needed */
113
114         if( job->pixel_ratio ) {
115
116                 uint8_t* val;
117                 uint8_t nval[38];
118                 uint32_t *ptr32 = (uint32_t*) (nval + 2);
119                 uint32_t size;
120
121                 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
122
123                 if (size == 38) {
124
125                         memcpy(nval, val, size);
126
127                         float width, height;
128                         float widthRatio;
129                         width = job->pixel_aspect_width;
130                         height = job->pixel_aspect_height;
131                         widthRatio = (width / height) * 0x10000;
132
133                         uint32_t widthRatioInt;
134                         widthRatioInt = (uint32_t)widthRatio;
135
136 #ifdef WORDS_BIGENDIAN
137                         ptr32[0] = widthRatioInt;
138 #else
139                         /* we need to switch the endianness, as the file format expects big endian */
140                         ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
141 #endif
142
143                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
144                                 hb_log("Problem setting transform matrix");
145                         }
146                         
147                 }
148
149         }
150
151         /* end of transformation matrix */
152
153         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
154         MP4TrackId firstAudioTrack;
155
156         /* add the audio tracks */
157     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
158     {
159         audio = hb_list_item( title->list_audio, i );
160         mux_data = malloc( sizeof( hb_mux_data_t ) );
161         audio->mux_data = mux_data;
162
163         mux_data->track = MP4AddAudioTrack( m->file,
164                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
165         MP4SetAudioProfileLevel( m->file, 0x0F );
166         MP4SetTrackESConfiguration( m->file, mux_data->track,
167                 audio->config.aac.bytes, audio->config.aac.length );
168                 
169         /* Set the language for this track */
170         /* The language is stored as 5-bit text - 0x60 */
171         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
172         language_code |= audio->iso639_2[1] - 0x60;  language_code <<= 5;
173         language_code |= audio->iso639_2[2] - 0x60;
174         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
175                                 
176                 /* store a reference to the first audio track,
177                 so we can use it to feed the chapter text track's sample rate */
178                 if (i == 0) {
179                         firstAudioTrack = mux_data->track;
180                 }
181                 
182     }
183
184         if (job->chapter_markers) {
185
186                 /* add a text track for the chapters */
187                 MP4TrackId textTrack;
188
189                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
190
191                 /* write the chapter markers for each selected chapter */
192                 char markerBuf[13];
193                 hb_chapter_t  * chapter;
194                 MP4Duration chapterDuration;
195                 float fOrigDuration, fTimescale;
196                 float fTSDuration;
197
198                 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
199                 {
200                         chapter = hb_list_item( title->list_chapter, i );
201
202                         fOrigDuration = chapter->duration;
203                         fTimescale = job->arate;
204                         fTSDuration = (fOrigDuration / 90000) * fTimescale;
205                         chapterDuration = (MP4Duration)fTSDuration;
206                         
207                         sprintf(markerBuf, "  Chapter %03i", i + 1);
208                         markerBuf[0] = 0;
209                         markerBuf[1] = 11; // "Chapter xxx"
210                         MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
211
212                 }
213                 
214         }
215         
216     return 0;
217 }
218
219 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
220                    hb_buffer_t * buf )
221 {
222     hb_job_t * job = m->job;
223
224     uint64_t duration;
225
226     if( mux_data == job->mux_data )
227     {
228         /* Video */
229         /* Because we use the audio samplerate as the timescale,
230            we have to use potentially variable durations so the video
231            doesn't go out of sync */
232         duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
233         m->sum_dur += duration;
234     }
235     else
236     {
237         /* Audio */
238         duration = MP4_INVALID_DURATION;
239     }
240
241     MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
242                     duration, 0, buf->key );
243     return 0;
244 }
245
246 static int MP4End( hb_mux_object_t * m )
247 {
248 #if 0
249     hb_job_t * job = m->job;
250     char filename[1024]; memset( filename, 0, 1024 );
251 #endif
252
253     MP4Close( m->file );
254
255 #if 0
256     hb_log( "muxmp4: optimizing file" );
257     snprintf( filename, 1024, "%s.tmp", job->file );
258     MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
259     remove( job->file );
260     rename( filename, job->file );
261 #endif
262
263     return 0;
264 }
265
266 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
267 {
268     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
269     m->init      = MP4Init;
270     m->mux       = MP4Mux;
271     m->end       = MP4End;
272     m->job       = job;
273     return m;
274 }
275