1 /*****************************************************************************
2 * mp4_lsmash.c: mp4 muxer using L-SMASH
3 *****************************************************************************
4 * Copyright (C) 2003-2021 x264 project
5 *
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7 * Loren Merritt <lorenm@u.washington.edu>
8 * Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
9 * Takashi Hirata <silverfilain@gmail.com>
10 * golgol7777 <golgol7777@gmail.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
25 *
26 * This program is also available under a commercial proprietary license.
27 * For more information, contact us at licensing@x264.com.
28 *****************************************************************************/
29
30 #include "output.h"
31 #include <lsmash.h>
32
33 #define H264_NALU_LENGTH_SIZE 4
34
35 /*******************/
36
37 #define MP4_LOG_ERROR( ... ) x264_cli_log( "mp4", X264_LOG_ERROR, __VA_ARGS__ )
38 #define MP4_LOG_WARNING( ... ) x264_cli_log( "mp4", X264_LOG_WARNING, __VA_ARGS__ )
39 #define MP4_LOG_INFO( ... ) x264_cli_log( "mp4", X264_LOG_INFO, __VA_ARGS__ )
40 #define MP4_FAIL_IF_ERR( cond, ... ) FAIL_IF_ERR( cond, "mp4", __VA_ARGS__ )
41
42 /* For close_file() */
43 #define MP4_LOG_IF_ERR( cond, ... )\
44 do\
45 {\
46 if( cond )\
47 {\
48 MP4_LOG_ERROR( __VA_ARGS__ );\
49 }\
50 } while( 0 )
51
52 /* For open_file() */
53 #define MP4_FAIL_IF_ERR_EX( cond, ... )\
54 do\
55 {\
56 if( cond )\
57 {\
58 remove_mp4_hnd( p_mp4 );\
59 MP4_LOG_ERROR( __VA_ARGS__ );\
60 return -1;\
61 }\
62 } while( 0 )
63
64 /*******************/
65
66 typedef struct
67 {
68 lsmash_root_t *p_root;
69 lsmash_video_summary_t *summary;
70 int b_stdout;
71 uint32_t i_movie_timescale;
72 uint32_t i_video_timescale;
73 uint32_t i_track;
74 uint32_t i_sample_entry;
75 uint64_t i_time_inc;
76 int64_t i_start_offset;
77 uint64_t i_first_cts;
78 uint64_t i_prev_dts;
79 uint32_t i_sei_size;
80 uint8_t *p_sei_buffer;
81 int i_numframe;
82 int64_t i_init_delta;
83 int i_delay_frames;
84 int b_dts_compress;
85 int i_dts_compress_multiplier;
86 int b_use_recovery;
87 int b_fragments;
88 lsmash_file_parameters_t file_param;
89 } mp4_hnd_t;
90
91 /*******************/
92
remove_mp4_hnd(hnd_t handle)93 static void remove_mp4_hnd( hnd_t handle )
94 {
95 mp4_hnd_t *p_mp4 = handle;
96 if( !p_mp4 )
97 return;
98 lsmash_cleanup_summary( (lsmash_summary_t *)p_mp4->summary );
99 lsmash_close_file( &p_mp4->file_param );
100 lsmash_destroy_root( p_mp4->p_root );
101 free( p_mp4->p_sei_buffer );
102 free( p_mp4 );
103 }
104
105 /*******************/
106
close_file(hnd_t handle,int64_t largest_pts,int64_t second_largest_pts)107 static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts )
108 {
109 mp4_hnd_t *p_mp4 = handle;
110
111 if( !p_mp4 )
112 return 0;
113
114 if( p_mp4->p_root )
115 {
116 double actual_duration = 0;
117 if( p_mp4->i_track )
118 {
119 /* Flush the rest of samples and add the last sample_delta. */
120 uint32_t last_delta = largest_pts - second_largest_pts;
121 MP4_LOG_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, (last_delta ? last_delta : 1) * p_mp4->i_time_inc ),
122 "failed to flush the rest of samples.\n" );
123
124 if( p_mp4->i_movie_timescale != 0 && p_mp4->i_video_timescale != 0 ) /* avoid zero division */
125 actual_duration = ((double)((largest_pts + last_delta) * p_mp4->i_time_inc) / p_mp4->i_video_timescale) * p_mp4->i_movie_timescale;
126 else
127 MP4_LOG_ERROR( "timescale is broken.\n" );
128
129 /*
130 * Declare the explicit time-line mapping.
131 * A segment_duration is given by movie timescale, while a media_time that is the start time of this segment
132 * is given by not the movie timescale but rather the media timescale.
133 * The reason is that ISO media have two time-lines, presentation and media time-line,
134 * and an edit maps the presentation time-line to the media time-line.
135 * According to QuickTime file format specification and the actual playback in QuickTime Player,
136 * if the Edit Box doesn't exist in the track, the ratio of the summation of sample durations and track's duration becomes
137 * the track's media_rate so that the entire media can be used by the track.
138 * So, we add Edit Box here to avoid this implicit media_rate could distort track's presentation timestamps slightly.
139 * Note: Any demuxers should follow the Edit List Box if it exists.
140 */
141 lsmash_edit_t edit;
142 edit.duration = actual_duration;
143 edit.start_time = p_mp4->i_first_cts;
144 edit.rate = ISOM_EDIT_MODE_NORMAL;
145 if( !p_mp4->b_fragments )
146 {
147 MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, edit ),
148 "failed to set timeline map for video.\n" );
149 }
150 else if( !p_mp4->b_stdout )
151 MP4_LOG_IF_ERR( lsmash_modify_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, 1, edit ),
152 "failed to update timeline map for video.\n" );
153 }
154
155 MP4_LOG_IF_ERR( lsmash_finish_movie( p_mp4->p_root, NULL ), "failed to finish movie.\n" );
156 }
157
158 remove_mp4_hnd( p_mp4 ); /* including lsmash_destroy_root( p_mp4->p_root ); */
159
160 return 0;
161 }
162
open_file(char * psz_filename,hnd_t * p_handle,cli_output_opt_t * opt)163 static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
164 {
165 *p_handle = NULL;
166
167 int b_regular = strcmp( psz_filename, "-" );
168 b_regular = b_regular && x264_is_regular_file_path( psz_filename );
169 if( b_regular )
170 {
171 FILE *fh = x264_fopen( psz_filename, "wb" );
172 MP4_FAIL_IF_ERR( !fh, "cannot open output file `%s'.\n", psz_filename );
173 b_regular = x264_is_regular_file( fh );
174 fclose( fh );
175 }
176
177 mp4_hnd_t *p_mp4 = calloc( 1, sizeof(mp4_hnd_t) );
178 MP4_FAIL_IF_ERR( !p_mp4, "failed to allocate memory for muxer information.\n" );
179
180 p_mp4->b_dts_compress = opt->use_dts_compress;
181 p_mp4->b_use_recovery = 0; // we don't really support recovery
182 p_mp4->b_fragments = !b_regular;
183 p_mp4->b_stdout = !strcmp( psz_filename, "-" );
184
185 p_mp4->p_root = lsmash_create_root();
186 MP4_FAIL_IF_ERR_EX( !p_mp4->p_root, "failed to create root.\n" );
187
188 MP4_FAIL_IF_ERR_EX( lsmash_open_file( psz_filename, 0, &p_mp4->file_param ) < 0, "failed to open an output file.\n" );
189 if( p_mp4->b_fragments )
190 p_mp4->file_param.mode |= LSMASH_FILE_MODE_FRAGMENTED;
191
192 p_mp4->summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO );
193 MP4_FAIL_IF_ERR_EX( !p_mp4->summary,
194 "failed to allocate memory for summary information of video.\n" );
195 p_mp4->summary->sample_type = ISOM_CODEC_TYPE_AVC1_VIDEO;
196
197 *p_handle = p_mp4;
198
199 return 0;
200 }
201
set_param(hnd_t handle,x264_param_t * p_param)202 static int set_param( hnd_t handle, x264_param_t *p_param )
203 {
204 mp4_hnd_t *p_mp4 = handle;
205 uint64_t i_media_timescale;
206
207 p_mp4->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
208 p_mp4->i_dts_compress_multiplier = p_mp4->b_dts_compress * p_mp4->i_delay_frames + 1;
209
210 i_media_timescale = (uint64_t)p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier;
211 p_mp4->i_time_inc = (uint64_t)p_param->i_timebase_num * p_mp4->i_dts_compress_multiplier;
212 MP4_FAIL_IF_ERR( i_media_timescale > UINT32_MAX, "MP4 media timescale %"PRIu64" exceeds maximum\n", i_media_timescale );
213
214 /* Select brands. */
215 lsmash_brand_type brands[6] = { 0 };
216 uint32_t brand_count = 0;
217 brands[brand_count++] = ISOM_BRAND_TYPE_MP42;
218 brands[brand_count++] = ISOM_BRAND_TYPE_MP41;
219 brands[brand_count++] = ISOM_BRAND_TYPE_ISOM;
220 if( p_mp4->b_use_recovery )
221 {
222 brands[brand_count++] = ISOM_BRAND_TYPE_AVC1; /* sdtp, sgpd, sbgp and visual roll recovery grouping */
223 if( p_param->b_open_gop )
224 brands[brand_count++] = ISOM_BRAND_TYPE_ISO6; /* cslg and visual random access grouping */
225 }
226
227 /* Set file */
228 lsmash_file_parameters_t *file_param = &p_mp4->file_param;
229 file_param->major_brand = brands[0];
230 file_param->brands = brands;
231 file_param->brand_count = brand_count;
232 file_param->minor_version = 0;
233 MP4_FAIL_IF_ERR( !lsmash_set_file( p_mp4->p_root, file_param ), "failed to add an output file into a ROOT.\n" );
234
235 /* Set movie parameters. */
236 lsmash_movie_parameters_t movie_param;
237 lsmash_initialize_movie_parameters( &movie_param );
238 MP4_FAIL_IF_ERR( lsmash_set_movie_parameters( p_mp4->p_root, &movie_param ),
239 "failed to set movie parameters.\n" );
240 p_mp4->i_movie_timescale = lsmash_get_movie_timescale( p_mp4->p_root );
241 MP4_FAIL_IF_ERR( !p_mp4->i_movie_timescale, "movie timescale is broken.\n" );
242
243 /* Create a video track. */
244 p_mp4->i_track = lsmash_create_track( p_mp4->p_root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK );
245 MP4_FAIL_IF_ERR( !p_mp4->i_track, "failed to create a video track.\n" );
246
247 p_mp4->summary->width = p_param->i_width;
248 p_mp4->summary->height = p_param->i_height;
249 uint32_t i_display_width = p_param->i_width << 16;
250 uint32_t i_display_height = p_param->i_height << 16;
251 if( p_param->vui.i_sar_width && p_param->vui.i_sar_height )
252 {
253 double sar = (double)p_param->vui.i_sar_width / p_param->vui.i_sar_height;
254 if( sar > 1.0 )
255 i_display_width *= sar;
256 else
257 i_display_height /= sar;
258 p_mp4->summary->par_h = p_param->vui.i_sar_width;
259 p_mp4->summary->par_v = p_param->vui.i_sar_height;
260 }
261 p_mp4->summary->color.primaries_index = p_param->vui.i_colorprim;
262 p_mp4->summary->color.transfer_index = p_param->vui.i_transfer;
263 p_mp4->summary->color.matrix_index = p_param->vui.i_colmatrix >= 0 ? p_param->vui.i_colmatrix : ISOM_MATRIX_INDEX_UNSPECIFIED;
264 p_mp4->summary->color.full_range = p_param->vui.b_fullrange >= 0 ? p_param->vui.b_fullrange : 0;
265
266 /* Set video track parameters. */
267 lsmash_track_parameters_t track_param;
268 lsmash_initialize_track_parameters( &track_param );
269 lsmash_track_mode track_mode = ISOM_TRACK_ENABLED | ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
270 track_param.mode = track_mode;
271 track_param.display_width = i_display_width;
272 track_param.display_height = i_display_height;
273 MP4_FAIL_IF_ERR( lsmash_set_track_parameters( p_mp4->p_root, p_mp4->i_track, &track_param ),
274 "failed to set track parameters for video.\n" );
275
276 /* Set video media parameters. */
277 lsmash_media_parameters_t media_param;
278 lsmash_initialize_media_parameters( &media_param );
279 media_param.timescale = i_media_timescale;
280 media_param.media_handler_name = "L-SMASH Video Media Handler";
281 if( p_mp4->b_use_recovery )
282 {
283 media_param.roll_grouping = p_param->b_intra_refresh;
284 media_param.rap_grouping = p_param->b_open_gop;
285 }
286 MP4_FAIL_IF_ERR( lsmash_set_media_parameters( p_mp4->p_root, p_mp4->i_track, &media_param ),
287 "failed to set media parameters for video.\n" );
288 p_mp4->i_video_timescale = lsmash_get_media_timescale( p_mp4->p_root, p_mp4->i_track );
289 MP4_FAIL_IF_ERR( !p_mp4->i_video_timescale, "media timescale for video is broken.\n" );
290
291 return 0;
292 }
293
write_headers(hnd_t handle,x264_nal_t * p_nal)294 static int write_headers( hnd_t handle, x264_nal_t *p_nal )
295 {
296 mp4_hnd_t *p_mp4 = handle;
297
298 uint32_t sps_size = p_nal[0].i_payload - H264_NALU_LENGTH_SIZE;
299 uint32_t pps_size = p_nal[1].i_payload - H264_NALU_LENGTH_SIZE;
300 uint32_t sei_size = p_nal[2].i_payload;
301
302 uint8_t *sps = p_nal[0].p_payload + H264_NALU_LENGTH_SIZE;
303 uint8_t *pps = p_nal[1].p_payload + H264_NALU_LENGTH_SIZE;
304 uint8_t *sei = p_nal[2].p_payload;
305
306 lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264,
307 LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED );
308
309 lsmash_h264_specific_parameters_t *param = (lsmash_h264_specific_parameters_t *)cs->data.structured;
310 param->lengthSizeMinusOne = H264_NALU_LENGTH_SIZE - 1;
311
312 /* SPS
313 * The remaining parameters are automatically set by SPS. */
314 if( lsmash_append_h264_parameter_set( param, H264_PARAMETER_SET_TYPE_SPS, sps, sps_size ) )
315 {
316 MP4_LOG_ERROR( "failed to append SPS.\n" );
317 return -1;
318 }
319
320 /* PPS */
321 if( lsmash_append_h264_parameter_set( param, H264_PARAMETER_SET_TYPE_PPS, pps, pps_size ) )
322 {
323 MP4_LOG_ERROR( "failed to append PPS.\n" );
324 return -1;
325 }
326
327 if( lsmash_add_codec_specific_data( (lsmash_summary_t *)p_mp4->summary, cs ) )
328 {
329 MP4_LOG_ERROR( "failed to add H.264 specific info.\n" );
330 return -1;
331 }
332
333 lsmash_destroy_codec_specific_data( cs );
334
335 /* Additional extensions */
336 /* Bitrate info */
337 cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_H264_BITRATE,
338 LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED );
339 if( cs )
340 lsmash_add_codec_specific_data( (lsmash_summary_t *)p_mp4->summary, cs );
341 lsmash_destroy_codec_specific_data( cs );
342
343 p_mp4->i_sample_entry = lsmash_add_sample_entry( p_mp4->p_root, p_mp4->i_track, p_mp4->summary );
344 MP4_FAIL_IF_ERR( !p_mp4->i_sample_entry,
345 "failed to add sample entry for video.\n" );
346
347 /* SEI */
348 p_mp4->p_sei_buffer = malloc( sei_size );
349 MP4_FAIL_IF_ERR( !p_mp4->p_sei_buffer,
350 "failed to allocate sei transition buffer.\n" );
351 memcpy( p_mp4->p_sei_buffer, sei, sei_size );
352 p_mp4->i_sei_size = sei_size;
353
354 return sei_size + sps_size + pps_size;
355 }
356
write_frame(hnd_t handle,uint8_t * p_nalu,int i_size,x264_picture_t * p_picture)357 static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture )
358 {
359 mp4_hnd_t *p_mp4 = handle;
360 uint64_t dts, cts;
361
362 if( !p_mp4->i_numframe )
363 {
364 p_mp4->i_start_offset = p_picture->i_dts * -1;
365 p_mp4->i_first_cts = p_mp4->b_dts_compress ? 0 : p_mp4->i_start_offset * p_mp4->i_time_inc;
366 if( p_mp4->b_fragments )
367 {
368 lsmash_edit_t edit;
369 edit.duration = ISOM_EDIT_DURATION_UNKNOWN32; /* QuickTime doesn't support 64bit duration. */
370 edit.start_time = p_mp4->i_first_cts;
371 edit.rate = ISOM_EDIT_MODE_NORMAL;
372 MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, edit ),
373 "failed to set timeline map for video.\n" );
374 }
375 }
376
377 lsmash_sample_t *p_sample = lsmash_create_sample( i_size + p_mp4->i_sei_size );
378 MP4_FAIL_IF_ERR( !p_sample,
379 "failed to create a video sample data.\n" );
380
381 if( p_mp4->p_sei_buffer )
382 {
383 memcpy( p_sample->data, p_mp4->p_sei_buffer, p_mp4->i_sei_size );
384 free( p_mp4->p_sei_buffer );
385 p_mp4->p_sei_buffer = NULL;
386 }
387
388 memcpy( p_sample->data + p_mp4->i_sei_size, p_nalu, i_size );
389 p_mp4->i_sei_size = 0;
390
391 if( p_mp4->b_dts_compress )
392 {
393 if( p_mp4->i_numframe == 1 )
394 p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
395 dts = p_mp4->i_numframe > p_mp4->i_delay_frames
396 ? p_picture->i_dts * p_mp4->i_time_inc
397 : p_mp4->i_numframe * (p_mp4->i_init_delta / p_mp4->i_dts_compress_multiplier);
398 cts = p_picture->i_pts * p_mp4->i_time_inc;
399 }
400 else
401 {
402 dts = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
403 cts = (p_picture->i_pts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
404 }
405
406 p_sample->dts = dts;
407 p_sample->cts = cts;
408 p_sample->index = p_mp4->i_sample_entry;
409 p_sample->prop.ra_flags = p_picture->b_keyframe ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE;
410
411 if( p_mp4->b_fragments && p_mp4->i_numframe && p_sample->prop.ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
412 {
413 MP4_FAIL_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, p_sample->dts - p_mp4->i_prev_dts ),
414 "failed to flush the rest of samples.\n" );
415 MP4_FAIL_IF_ERR( lsmash_create_fragment_movie( p_mp4->p_root ),
416 "failed to create a movie fragment.\n" );
417 }
418
419 /* Append data per sample. */
420 MP4_FAIL_IF_ERR( lsmash_append_sample( p_mp4->p_root, p_mp4->i_track, p_sample ),
421 "failed to append a video frame.\n" );
422
423 p_mp4->i_prev_dts = dts;
424 p_mp4->i_numframe++;
425
426 return i_size;
427 }
428
429 const cli_output_t mp4_output = { open_file, set_param, write_headers, write_frame, close_file };
430