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