OSDN Git Service

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