1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Arash Shafiei
5 * Copyright (c) Telecom ParisTech 2000-2013
6 * All rights reserved
7 *
8 * This file is part of GPAC / dashcast
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 #include "video_muxer.h"
27 #include "libavutil/opt.h"
28 #include <gpac/network.h>
29
30
31 /**
32 * A function which takes FFmpeg H264 extradata (SPS/PPS) and bring them ready to be pushed to the MP4 muxer.
33 * @param extradata
34 * @param extradata_size
35 * @param dstcfg
36 * @returns GF_OK is the extradata was parsed and is valid, other values otherwise.
37 */
avc_import_ffextradata(const u8 * extradata,const u64 extradata_size,GF_AVCConfig * dstcfg)38 static GF_Err avc_import_ffextradata(const u8 *extradata, const u64 extradata_size, GF_AVCConfig *dstcfg)
39 {
40 #ifdef GPAC_DISABLE_AV_PARSERS
41 return GF_OK;
42 #else
43 u8 nal_size;
44 AVCState avc;
45 GF_BitStream *bs;
46 if (!extradata || (extradata_size < sizeof(u32)))
47 return GF_BAD_PARAM;
48 bs = gf_bs_new((const char *) extradata, extradata_size, GF_BITSTREAM_READ);
49 if (!bs)
50 return GF_BAD_PARAM;
51 if (gf_bs_read_u32(bs) != 0x00000001) {
52 gf_bs_del(bs);
53 return GF_BAD_PARAM;
54 }
55
56 //SPS
57 {
58 s32 idx;
59 char *buffer = NULL;
60 const u64 nal_start = 4;
61 nal_size = gf_media_nalu_next_start_code_bs(bs);
62 if (nal_start + nal_size > extradata_size) {
63 gf_bs_del(bs);
64 return GF_BAD_PARAM;
65 }
66 buffer = (char*)gf_malloc(nal_size);
67 gf_bs_read_data(bs, buffer, nal_size);
68 gf_bs_seek(bs, nal_start);
69 if ((gf_bs_read_u8(bs) & 0x1F) != GF_AVC_NALU_SEQ_PARAM) {
70 gf_bs_del(bs);
71 gf_free(buffer);
72 return GF_BAD_PARAM;
73 }
74
75 idx = gf_media_avc_read_sps(buffer, nal_size, &avc, 0, NULL);
76 if (idx < 0) {
77 gf_bs_del(bs);
78 gf_free(buffer);
79 return GF_BAD_PARAM;
80 }
81
82 dstcfg->configurationVersion = 1;
83 dstcfg->profile_compatibility = avc.sps[idx].prof_compat;
84 dstcfg->AVCProfileIndication = avc.sps[idx].profile_idc;
85 dstcfg->AVCLevelIndication = avc.sps[idx].level_idc;
86 dstcfg->chroma_format = avc.sps[idx].chroma_format;
87 dstcfg->luma_bit_depth = 8 + avc.sps[idx].luma_bit_depth_m8;
88 dstcfg->chroma_bit_depth = 8 + avc.sps[idx].chroma_bit_depth_m8;
89
90 {
91 GF_AVCConfigSlot *slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
92 slc->size = nal_size;
93 slc->id = idx;
94 slc->data = buffer;
95 gf_list_add(dstcfg->sequenceParameterSets, slc);
96 }
97 }
98
99 //PPS
100 {
101 s32 idx;
102 char *buffer = NULL;
103 const u64 nal_start = 4 + nal_size + 4;
104 gf_bs_seek(bs, nal_start);
105 nal_size = gf_media_nalu_next_start_code_bs(bs);
106 if (nal_start + nal_size > extradata_size) {
107 gf_bs_del(bs);
108 return GF_BAD_PARAM;
109 }
110 buffer = (char*)gf_malloc(nal_size);
111 gf_bs_read_data(bs, buffer, nal_size);
112 gf_bs_seek(bs, nal_start);
113 if ((gf_bs_read_u8(bs) & 0x1F) != GF_AVC_NALU_PIC_PARAM) {
114 gf_bs_del(bs);
115 gf_free(buffer);
116 return GF_BAD_PARAM;
117 }
118
119 idx = gf_media_avc_read_pps(buffer, nal_size, &avc);
120 if (idx < 0) {
121 gf_bs_del(bs);
122 gf_free(buffer);
123 return GF_BAD_PARAM;
124 }
125
126 {
127 GF_AVCConfigSlot *slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
128 slc->size = nal_size;
129 slc->id = idx;
130 slc->data = buffer;
131 gf_list_add(dstcfg->pictureParameterSets, slc);
132 }
133 }
134
135 gf_bs_del(bs);
136 return GF_OK;
137 #endif
138 }
139
140 /**
141 * A function which takes FFmpeg H265 extradata (SPS/PPS) and bring them ready to be pushed to the MP4 muxer.
142 * @param extradata
143 * @param extradata_size
144 * @param dstcfg
145 * @returns GF_OK is the extradata was parsed and is valid, other values otherwise.
146 */
hevc_import_ffextradata(const u8 * extradata,const u64 extradata_size,GF_HEVCConfig * dst_cfg)147 static GF_Err hevc_import_ffextradata(const u8 *extradata, const u64 extradata_size, GF_HEVCConfig *dst_cfg)
148 {
149 #ifdef GPAC_DISABLE_AV_PARSERS
150 return GF_OK;
151 #else
152 HEVCState hevc;
153 GF_HEVCParamArray *vpss = NULL, *spss = NULL, *ppss = NULL, *seis = NULL;
154 GF_BitStream *bs;
155 char *buffer = NULL;
156 u32 buffer_size = 0;
157 if (!extradata || (extradata_size < sizeof(u32)))
158 return GF_BAD_PARAM;
159 bs = gf_bs_new((const char *) extradata, extradata_size, GF_BITSTREAM_READ);
160 if (!bs)
161 return GF_BAD_PARAM;
162
163 memset(&hevc, 0, sizeof(HEVCState));
164 hevc.sps_active_idx = -1;
165
166 while (gf_bs_available(bs)) {
167 s32 idx;
168 GF_AVCConfigSlot *slc;
169 u8 nal_unit_type, temporal_id, layer_id;
170 u64 nal_start, start_code;
171 u32 nal_size;
172
173 start_code = gf_bs_read_u32(bs);
174 if (start_code>>8 == 0x000001) {
175 nal_start = gf_bs_get_position(bs) - 1;
176 gf_bs_seek(bs, nal_start);
177 start_code = 1;
178 }
179 if (start_code != 0x00000001) {
180 gf_bs_del(bs);
181 if (buffer) gf_free(buffer);
182 if (vpss && spss && ppss) return GF_OK;
183 return GF_BAD_PARAM;
184 }
185 nal_start = gf_bs_get_position(bs);
186 nal_size = gf_media_nalu_next_start_code_bs(bs);
187 if (nal_start + nal_size > extradata_size) {
188 gf_bs_del(bs);
189 return GF_BAD_PARAM;
190 }
191
192 if (nal_size > buffer_size) {
193 buffer = (char*)gf_realloc(buffer, nal_size);
194 buffer_size = nal_size;
195 }
196 gf_bs_read_data(bs, buffer, nal_size);
197
198 gf_media_hevc_parse_nalu(buffer, nal_size, &hevc, &nal_unit_type, &temporal_id, &layer_id);
199 if (layer_id) {
200 gf_bs_del(bs);
201 gf_free(buffer);
202 return GF_BAD_PARAM;
203 }
204
205 switch (nal_unit_type) {
206 case GF_HEVC_NALU_VID_PARAM:
207 idx = gf_media_hevc_read_vps(buffer, nal_size , &hevc);
208 if (idx < 0) {
209 gf_bs_del(bs);
210 gf_free(buffer);
211 return GF_BAD_PARAM;
212 }
213
214 assert(hevc.vps[idx].state == 1); //we don't expect multiple VPS
215 if (hevc.vps[idx].state == 1) {
216 hevc.vps[idx].state = 2;
217 hevc.vps[idx].crc = gf_crc_32(buffer, nal_size);
218
219 dst_cfg->avgFrameRate = hevc.vps[idx].rates[0].avg_pic_rate;
220 dst_cfg->constantFrameRate = hevc.vps[idx].rates[0].constand_pic_rate_idc;
221 dst_cfg->numTemporalLayers = hevc.vps[idx].max_sub_layers;
222 dst_cfg->temporalIdNested = hevc.vps[idx].temporal_id_nesting;
223
224 if (!vpss) {
225 GF_SAFEALLOC(vpss, GF_HEVCParamArray);
226 if (vpss) {
227 vpss->nalus = gf_list_new();
228 gf_list_add(dst_cfg->param_array, vpss);
229 vpss->array_completeness = 1;
230 vpss->type = GF_HEVC_NALU_VID_PARAM;
231 }
232 }
233
234 slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
235 if (slc) {
236 slc->size = nal_size;
237 slc->id = idx;
238 slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
239 if (slc->data)
240 memcpy(slc->data, buffer, sizeof(char)*slc->size);
241
242 if (vpss)
243 gf_list_add(vpss->nalus, slc);
244 }
245 }
246 break;
247 case GF_HEVC_NALU_SEQ_PARAM:
248 idx = gf_media_hevc_read_sps(buffer, nal_size, &hevc);
249 if (idx < 0) {
250 gf_bs_del(bs);
251 gf_free(buffer);
252 return GF_BAD_PARAM;
253 }
254
255 assert(!(hevc.sps[idx].state & AVC_SPS_DECLARED)); //we don't expect multiple SPS
256 if ((hevc.sps[idx].state & AVC_SPS_PARSED) && !(hevc.sps[idx].state & AVC_SPS_DECLARED)) {
257 hevc.sps[idx].state |= AVC_SPS_DECLARED;
258 hevc.sps[idx].crc = gf_crc_32(buffer, nal_size);
259 }
260
261 dst_cfg->configurationVersion = 1;
262 dst_cfg->profile_space = hevc.sps[idx].ptl.profile_space;
263 dst_cfg->tier_flag = hevc.sps[idx].ptl.tier_flag;
264 dst_cfg->profile_idc = hevc.sps[idx].ptl.profile_idc;
265 dst_cfg->general_profile_compatibility_flags = hevc.sps[idx].ptl.profile_compatibility_flag;
266 dst_cfg->progressive_source_flag = hevc.sps[idx].ptl.general_progressive_source_flag;
267 dst_cfg->interlaced_source_flag = hevc.sps[idx].ptl.general_interlaced_source_flag;
268 dst_cfg->non_packed_constraint_flag = hevc.sps[idx].ptl.general_non_packed_constraint_flag;
269 dst_cfg->frame_only_constraint_flag = hevc.sps[idx].ptl.general_frame_only_constraint_flag;
270
271 dst_cfg->constraint_indicator_flags = hevc.sps[idx].ptl.general_reserved_44bits;
272 dst_cfg->level_idc = hevc.sps[idx].ptl.level_idc;
273
274 dst_cfg->chromaFormat = hevc.sps[idx].chroma_format_idc;
275 dst_cfg->luma_bit_depth = hevc.sps[idx].bit_depth_luma;
276 dst_cfg->chroma_bit_depth = hevc.sps[idx].bit_depth_chroma;
277
278 if (!spss) {
279 GF_SAFEALLOC(spss, GF_HEVCParamArray);
280 if (spss) {
281 spss->nalus = gf_list_new();
282 gf_list_add(dst_cfg->param_array, spss);
283 spss->array_completeness = 1;
284 spss->type = GF_HEVC_NALU_SEQ_PARAM;
285 }
286 }
287
288 slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
289 if (slc) {
290 slc->size = nal_size;
291 slc->id = idx;
292 slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
293 if (slc->data)
294 memcpy(slc->data, buffer, sizeof(char)*slc->size);
295 if (spss)
296 gf_list_add(spss->nalus, slc);
297 }
298 break;
299 case GF_HEVC_NALU_PIC_PARAM:
300 idx = gf_media_hevc_read_pps(buffer, nal_size, &hevc);
301 if (idx < 0) {
302 gf_bs_del(bs);
303 gf_free(buffer);
304 return GF_BAD_PARAM;
305 }
306
307 assert(hevc.pps[idx].state == 1); //we don't expect multiple PPS
308 if (hevc.pps[idx].state == 1) {
309 hevc.pps[idx].state = 2;
310 hevc.pps[idx].crc = gf_crc_32(buffer, nal_size);
311
312 if (!ppss) {
313 GF_SAFEALLOC(ppss, GF_HEVCParamArray);
314 if (ppss) {
315 ppss->nalus = gf_list_new();
316 gf_list_add(dst_cfg->param_array, ppss);
317 ppss->array_completeness = 1;
318 ppss->type = GF_HEVC_NALU_PIC_PARAM;
319 }
320 }
321
322 slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
323 if (slc) {
324 slc->size = nal_size;
325 slc->id = idx;
326 slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
327 if (slc->data)
328 memcpy(slc->data, buffer, sizeof(char)*slc->size);
329
330 if (ppss)
331 gf_list_add(ppss->nalus, slc);
332 }
333 }
334 break;
335 case GF_HEVC_NALU_SEI_PREFIX:
336 if (!seis) {
337 GF_SAFEALLOC(seis, GF_HEVCParamArray);
338 if (seis) {
339 seis->nalus = gf_list_new();
340 seis->array_completeness = 0;
341 seis->type = GF_HEVC_NALU_SEI_PREFIX;
342 }
343 }
344 slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
345 if (slc) {
346 slc->size = nal_size;
347 slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
348 if (slc->data)
349 memcpy(slc->data, buffer, sizeof(char)*slc->size);
350 if (seis)
351 gf_list_add(seis->nalus, slc);
352 }
353 break;
354 default:
355 break;
356 }
357 }
358
359 gf_bs_del(bs);
360 if (buffer) gf_free(buffer);
361
362 return GF_OK;
363 #endif
364 }
365
366 #ifndef GPAC_DISABLE_ISOM
367
dc_gpac_video_write_config(VideoOutputFile * video_output_file,u32 * di,u32 track)368 static GF_Err dc_gpac_video_write_config(VideoOutputFile *video_output_file, u32 *di, u32 track) {
369 GF_Err ret;
370 if (video_output_file->codec_ctx->codec_id == CODEC_ID_H264) {
371 GF_AVCConfig *avccfg;
372 avccfg = gf_odf_avc_cfg_new();
373 if (!avccfg) {
374 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create AVCConfig\n"));
375 return GF_OUT_OF_MEM;
376 }
377
378 ret = avc_import_ffextradata(video_output_file->codec_ctx->extradata, video_output_file->codec_ctx->extradata_size, avccfg);
379 if (ret != GF_OK) {
380 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot parse AVC/H264 SPS/PPS\n"));
381 gf_odf_avc_cfg_del(avccfg);
382 return ret;
383 }
384
385 ret = gf_isom_avc_config_new(video_output_file->isof, track, avccfg, NULL, NULL, di);
386 if (ret != GF_OK) {
387 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_avc_config_new\n", gf_error_to_string(ret)));
388 return ret;
389 }
390
391 gf_odf_avc_cfg_del(avccfg);
392
393 //inband SPS/PPS
394 if (video_output_file->muxer_type == GPAC_INIT_VIDEO_MUXER_AVC3) {
395 ret = gf_isom_avc_set_inband_config(video_output_file->isof, track, 1);
396 if (ret != GF_OK) {
397 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_avc_set_inband_config\n", gf_error_to_string(ret)));
398 return ret;
399 }
400 }
401 } else if (!strcmp(video_output_file->codec_ctx->codec->name, "libx265")) { //FIXME CODEC_ID_HEVC would break on old releases
402 GF_HEVCConfig *hevccfg = gf_odf_hevc_cfg_new();
403 if (!hevccfg) {
404 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create HEVCConfig\n"));
405 return GF_OUT_OF_MEM;
406 }
407
408 ret = hevc_import_ffextradata(video_output_file->codec_ctx->extradata, video_output_file->codec_ctx->extradata_size, hevccfg);
409 if (ret != GF_OK) {
410 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot parse HEVC/H265 SPS/PPS\n"));
411 gf_odf_hevc_cfg_del(hevccfg);
412 return ret;
413 }
414
415 ret = gf_isom_hevc_config_new(video_output_file->isof, track, hevccfg, NULL, NULL, di);
416 if (ret != GF_OK) {
417 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_hevc_config_new\n", gf_error_to_string(ret)));
418 return ret;
419 }
420
421 gf_odf_hevc_cfg_del(hevccfg);
422
423 //inband SPS/PPS
424 if (video_output_file->muxer_type == GPAC_INIT_VIDEO_MUXER_AVC3) {
425 ret = gf_isom_hevc_set_inband_config(video_output_file->isof, track, 1);
426 if (ret != GF_OK) {
427 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_hevc_set_inband_config\n", gf_error_to_string(ret)));
428 return ret;
429 }
430 }
431 }
432
433 return GF_OK;
434 }
435
dc_gpac_video_moov_create(VideoOutputFile * video_output_file,char * filename)436 int dc_gpac_video_moov_create(VideoOutputFile *video_output_file, char *filename)
437 {
438 GF_Err ret;
439 AVCodecContext *video_codec_ctx = video_output_file->codec_ctx;
440 u32 di=1, track;
441
442 //TODO: For the moment it is fixed
443 //u32 sample_dur = video_output_file->codec_ctx->time_base.den;
444
445 //int64_t profile = 0;
446 //av_opt_get_int(video_output_file->codec_ctx->priv_data, "level", AV_OPT_SEARCH_CHILDREN, &profile);
447
448 video_output_file->isof = gf_isom_open(filename, GF_ISOM_OPEN_WRITE, NULL);
449 if (!video_output_file->isof) {
450 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open iso file %s\n", filename));
451 return -1;
452 }
453 //gf_isom_store_movie_config(video_output_file->isof, 0);
454 track = gf_isom_new_track(video_output_file->isof, 0, GF_ISOM_MEDIA_VISUAL, video_codec_ctx->time_base.den);
455 video_output_file->trackID = gf_isom_get_track_id(video_output_file->isof, track);
456
457 video_output_file->timescale = video_codec_ctx->time_base.den;
458 if (!video_output_file->frame_dur)
459 video_output_file->frame_dur = video_codec_ctx->time_base.num;
460
461 if (!track) {
462 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create new track\n"));
463 return -1;
464 }
465
466 ret = gf_isom_set_track_enabled(video_output_file->isof, track, 1);
467 if (ret != GF_OK) {
468 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_set_track_enabled\n", gf_error_to_string(ret)));
469 return -1;
470 }
471
472 ret = dc_gpac_video_write_config(video_output_file, &di, track);
473 if (ret != GF_OK) {
474 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: dc_gpac_video_write_config\n", gf_error_to_string(ret)));
475 return -1;
476 }
477
478 gf_isom_set_visual_info(video_output_file->isof, track, di, video_codec_ctx->width, video_codec_ctx->height);
479 gf_isom_set_sync_table(video_output_file->isof, track);
480
481 ret = gf_isom_setup_track_fragment(video_output_file->isof, track, 1, video_output_file->use_source_timing ? (u32) video_output_file->frame_dur : 1, 0, 0, 0, 0, 0);
482 if (ret != GF_OK) {
483 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_setup_track_fragment\n", gf_error_to_string(ret)));
484 return -1;
485 }
486
487 ret = gf_isom_finalize_for_fragment(video_output_file->isof, track);
488 if (ret != GF_OK) {
489 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_finalize_for_fragment\n", gf_error_to_string(ret)));
490 return -1;
491 }
492
493 ret = gf_media_get_rfc_6381_codec_name(video_output_file->isof, track, video_output_file->video_data_conf->codec6381, GF_FALSE, GF_FALSE);
494 if (ret != GF_OK) {
495 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_finalize_for_fragment\n", gf_error_to_string(ret)));
496 return -1;
497 }
498
499 return 0;
500 }
501
dc_gpac_video_isom_open_seg(VideoOutputFile * video_output_file,char * filename)502 int dc_gpac_video_isom_open_seg(VideoOutputFile *video_output_file, char *filename)
503 {
504 GF_Err ret;
505 ret = gf_isom_start_segment(video_output_file->isof, filename, 1);
506 if (ret != GF_OK) {
507 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_start_segment\n", gf_error_to_string(ret)));
508 return -1;
509 }
510 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Opening new segment %s at UTC "LLU" ms\n", filename, gf_net_get_utc() ));
511 return 0;
512 }
513
dc_gpac_video_isom_write(VideoOutputFile * video_output_file)514 int dc_gpac_video_isom_write(VideoOutputFile *video_output_file)
515 {
516 GF_Err ret;
517 AVCodecContext *video_codec_ctx = video_output_file->codec_ctx;
518
519 u32 sc_size = 0;
520 u32 nalu_size = 0;
521
522 u32 buf_len = video_output_file->encoded_frame_size;
523 u8 *buf_ptr = video_output_file->vbuf;
524
525 GF_BitStream *out_bs = gf_bs_new(NULL, 2 * buf_len, GF_BITSTREAM_WRITE);
526 nalu_size = gf_media_nalu_next_start_code(buf_ptr, buf_len, &sc_size);
527 if (nalu_size != 0) {
528 gf_bs_write_u32(out_bs, nalu_size);
529 gf_bs_write_data(out_bs, (const char*) buf_ptr, nalu_size);
530 }
531 if (sc_size) {
532 buf_ptr += (nalu_size + sc_size);
533 buf_len -= (nalu_size + sc_size);
534 }
535
536 while (buf_len) {
537 nalu_size = gf_media_nalu_next_start_code(buf_ptr, buf_len, &sc_size);
538 if (nalu_size != 0) {
539 gf_bs_write_u32(out_bs, nalu_size);
540 gf_bs_write_data(out_bs, (const char*) buf_ptr, nalu_size);
541 }
542
543 buf_ptr += nalu_size;
544
545 if (!sc_size || (buf_len < nalu_size + sc_size))
546 break;
547 buf_len -= nalu_size + sc_size;
548 buf_ptr += sc_size;
549 }
550
551 gf_bs_get_content(out_bs, &video_output_file->sample->data, &video_output_file->sample->dataLength);
552 //video_output_file->sample->data = //(char *) (video_output_file->vbuf + nalu_size + sc_size);
553 //video_output_file->sample->dataLength = //video_output_file->encoded_frame_size - (sc_size + nalu_size);
554
555 video_output_file->sample->DTS = video_codec_ctx->coded_frame->pkt_dts;
556 video_output_file->sample->CTS_Offset = (s32) (video_codec_ctx->coded_frame->pts - video_output_file->sample->DTS);
557 video_output_file->sample->IsRAP = video_codec_ctx->coded_frame->key_frame;
558 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("Isom Write: RAP %d , DTS "LLD" CTS offset %d \n", video_output_file->sample->IsRAP, video_output_file->sample->DTS, video_output_file->sample->CTS_Offset));
559
560 ret = gf_isom_fragment_add_sample(video_output_file->isof, video_output_file->trackID, video_output_file->sample, 1, video_output_file->use_source_timing ? (u32) video_output_file->frame_dur : 1, 0, 0, 0);
561 if (ret != GF_OK) {
562 gf_bs_del(out_bs);
563 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_fragment_add_sample\n", gf_error_to_string(ret)));
564 return -1;
565 }
566
567 //free data but keep sample structure alive
568 gf_free(video_output_file->sample->data);
569 video_output_file->sample->data = NULL;
570 video_output_file->sample->dataLength = 0;
571
572 gf_bs_del(out_bs);
573 return 0;
574 }
575
dc_gpac_video_isom_close_seg(VideoOutputFile * video_output_file)576 int dc_gpac_video_isom_close_seg(VideoOutputFile *video_output_file)
577 {
578 u64 seg_size;
579 GF_Err ret = gf_isom_close_segment(video_output_file->isof, 0, 0, 0, 0, 0, 0, 0, GF_TRUE, GF_FALSE, video_output_file->seg_marker, NULL, NULL, &seg_size);
580 if (ret != GF_OK) {
581 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_close_segment\n", gf_error_to_string(ret)));
582 return -1;
583 }
584 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Rep %s Closing segment %s at UTC "LLU" ms - size "LLU" bytes\n", video_output_file->rep_id, gf_isom_get_segment_name(video_output_file->isof), gf_net_get_utc(), seg_size ));
585
586 return 0;
587 }
588
dc_gpac_video_isom_close(VideoOutputFile * video_output_file)589 int dc_gpac_video_isom_close(VideoOutputFile *video_output_file)
590 {
591 GF_Err ret;
592 ret = gf_isom_close(video_output_file->isof);
593 if (ret != GF_OK) {
594 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("%s: gf_isom_close\n", gf_error_to_string(ret)));
595 return -1;
596 }
597
598 return 0;
599 }
600
601 #endif
602
603
dc_raw_h264_open(VideoOutputFile * video_output_file,char * filename)604 int dc_raw_h264_open(VideoOutputFile *video_output_file, char *filename)
605 {
606 video_output_file->file = gf_fopen(filename, "w");
607 return 0;
608 }
609
dc_raw_h264_write(VideoOutputFile * video_output_file)610 int dc_raw_h264_write(VideoOutputFile *video_output_file)
611 {
612 fwrite(video_output_file->vbuf, video_output_file->encoded_frame_size, 1, video_output_file->file);
613 return 0;
614 }
615
dc_raw_h264_close(VideoOutputFile * video_output_file)616 int dc_raw_h264_close(VideoOutputFile *video_output_file)
617 {
618 gf_fclose(video_output_file->file);
619 return 0;
620 }
621
dc_ffmpeg_video_muxer_open(VideoOutputFile * video_output_file,char * filename)622 int dc_ffmpeg_video_muxer_open(VideoOutputFile *video_output_file, char *filename)
623 {
624 AVStream *video_stream;
625 AVOutputFormat *output_fmt;
626 int ret;
627
628 AVCodecContext *video_codec_ctx = video_output_file->codec_ctx;
629 video_output_file->av_fmt_ctx = NULL;
630
631 // video_output_file->vbr = video_data_conf->bitrate;
632 // video_output_file->vfr = video_data_conf->framerate;
633 // video_output_file->width = video_data_conf->width;
634 // video_output_file->height = video_data_conf->height;
635 // strcpy(video_output_file->filename, video_data_conf->filename);
636 // strcpy(video_output_file->codec, video_data_conf->codec);
637
638 /* Find output format */
639 output_fmt = av_guess_format(NULL, filename, NULL);
640 if (!output_fmt) {
641 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot find suitable output format\n"));
642 return -1;
643 }
644
645 video_output_file->av_fmt_ctx = avformat_alloc_context();
646 if (!video_output_file->av_fmt_ctx) {
647 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot allocate memory for pOutVideoFormatCtx\n"));
648 return -1;
649 }
650
651 video_output_file->av_fmt_ctx->oformat = output_fmt;
652 strcpy(video_output_file->av_fmt_ctx->filename, filename);
653
654 /* Open the output file */
655 if (!(output_fmt->flags & AVFMT_NOFILE)) {
656 if (avio_open(&video_output_file->av_fmt_ctx->pb, filename, URL_WRONLY) < 0) {
657 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot not open '%s'\n", filename));
658 return -1;
659 }
660 }
661
662 video_stream = avformat_new_stream(video_output_file->av_fmt_ctx,
663 video_output_file->codec);
664 if (!video_stream) {
665 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot create output video stream\n"));
666 return -1;
667 }
668
669 //video_stream->codec = video_output_file->codec_ctx;
670
671 video_stream->codec->codec_id = video_output_file->codec->id;
672 video_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
673 video_stream->codec->bit_rate = video_codec_ctx->bit_rate; //video_output_file->video_data_conf->bitrate;
674 video_stream->codec->width = video_codec_ctx->width; //video_output_file->video_data_conf->width;
675 video_stream->codec->height = video_codec_ctx->height; //video_output_file->video_data_conf->height;
676
677 video_stream->codec->time_base = video_codec_ctx->time_base;
678
679 video_stream->codec->pix_fmt = PIX_FMT_YUV420P;
680 video_stream->codec->gop_size = video_codec_ctx->time_base.den; //video_output_file->video_data_conf->framerate;
681
682 av_opt_set(video_stream->codec->priv_data, "preset", "ultrafast", 0);
683 av_opt_set(video_stream->codec->priv_data, "tune", "zerolatency", 0);
684
685 /* open the video codec */
686 if (avcodec_open2(video_stream->codec, video_output_file->codec, NULL) < 0) {
687 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Cannot open output video codec\n"));
688 return -1;
689 }
690
691 ret = avformat_write_header(video_output_file->av_fmt_ctx, NULL);
692
693 if (!ret) {
694 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("avformat_write_header returned %d\n", ret));
695 return ret;
696 }
697
698 video_output_file->timescale = video_codec_ctx->time_base.den;
699 return 0;
700 }
701
dc_ffmpeg_video_muxer_write(VideoOutputFile * video_output_file)702 int dc_ffmpeg_video_muxer_write(VideoOutputFile *video_output_file)
703 {
704 AVPacket pkt;
705 AVStream *video_stream = video_output_file->av_fmt_ctx->streams[video_output_file->vstream_idx];
706 AVCodecContext *video_codec_ctx = video_stream->codec;
707
708 av_init_packet(&pkt);
709 pkt.data = NULL;
710 pkt.size = 0;
711
712 if (video_codec_ctx->coded_frame->pts != AV_NOPTS_VALUE) {
713 pkt.pts = av_rescale_q(video_codec_ctx->coded_frame->pts, video_codec_ctx->time_base, video_stream->time_base);
714 }
715
716 if (video_codec_ctx->coded_frame->key_frame)
717 pkt.flags |= AV_PKT_FLAG_KEY;
718
719 pkt.stream_index = video_stream->index;
720 pkt.data = video_output_file->vbuf;
721 pkt.size = video_output_file->encoded_frame_size;
722
723 // write the compressed frame in the media file
724 if (av_interleaved_write_frame(video_output_file->av_fmt_ctx, &pkt) != 0) {
725 GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Writing frame is not successful\n"));
726 return -1;
727 }
728
729 av_free_packet(&pkt);
730
731 return 0;
732 }
733
dc_ffmpeg_video_muxer_close(VideoOutputFile * video_output_file)734 int dc_ffmpeg_video_muxer_close(VideoOutputFile *video_output_file)
735 {
736 u32 i;
737
738 av_write_trailer(video_output_file->av_fmt_ctx);
739
740 avio_close(video_output_file->av_fmt_ctx->pb);
741
742 // free the streams
743 for (i = 0; i < video_output_file->av_fmt_ctx->nb_streams; i++) {
744 avcodec_close(video_output_file->av_fmt_ctx->streams[i]->codec);
745 av_freep(&video_output_file->av_fmt_ctx->streams[i]->info);
746 }
747
748 //video_output_file->av_fmt_ctx->streams[video_output_file->vstream_idx]->codec = NULL;
749 avformat_free_context(video_output_file->av_fmt_ctx);
750
751 return 0;
752 }
753
dc_video_muxer_init(VideoOutputFile * video_output_file,VideoDataConf * video_data_conf,VideoMuxerType muxer_type,int frame_per_segment,int frame_per_fragment,u32 seg_marker,int gdr,int seg_dur,int frag_dur,int frame_dur,int gop_size,int video_cb_size)754 int dc_video_muxer_init(VideoOutputFile *video_output_file, VideoDataConf *video_data_conf, VideoMuxerType muxer_type, int frame_per_segment, int frame_per_fragment, u32 seg_marker, int gdr, int seg_dur, int frag_dur, int frame_dur, int gop_size, int video_cb_size)
755 {
756 char name[GF_MAX_PATH];
757 memset(video_output_file, 0, sizeof(VideoOutputFile));
758 snprintf(name, sizeof(name), "video encoder %s", video_data_conf->filename);
759 dc_consumer_init(&video_output_file->consumer, video_cb_size, name);
760
761 #ifndef GPAC_DISABLE_ISOM
762 video_output_file->sample = gf_isom_sample_new();
763 video_output_file->isof = NULL;
764 #endif
765
766 video_output_file->muxer_type = muxer_type;
767
768 video_output_file->frame_per_segment = frame_per_segment;
769 video_output_file->frame_per_fragment = frame_per_fragment;
770
771 video_output_file->seg_dur = seg_dur;
772 video_output_file->frag_dur = frag_dur;
773
774 video_output_file->seg_marker = seg_marker;
775 video_output_file->gdr = gdr;
776 video_output_file->gop_size = gop_size;
777 video_output_file->frame_dur = frame_dur;
778
779 return 0;
780 }
781
dc_video_muxer_free(VideoOutputFile * video_output_file)782 int dc_video_muxer_free(VideoOutputFile *video_output_file)
783 {
784 #ifndef GPAC_DISABLE_ISOM
785 if (video_output_file->isof != NULL) {
786 gf_isom_close(video_output_file->isof);
787 }
788
789 gf_isom_sample_del(&video_output_file->sample);
790 #endif
791 return 0;
792 }
793
dc_video_muxer_open(VideoOutputFile * video_output_file,char * directory,char * id_name,int seg)794 GF_Err dc_video_muxer_open(VideoOutputFile *video_output_file, char *directory, char *id_name, int seg)
795 {
796 char name[GF_MAX_PATH];
797
798 switch (video_output_file->muxer_type) {
799 case FFMPEG_VIDEO_MUXER:
800 snprintf(name, sizeof(name), "%s/%s_%d_ffmpeg.mp4", directory, id_name, seg);
801 return dc_ffmpeg_video_muxer_open(video_output_file, name);
802 case RAW_VIDEO_H264:
803 snprintf(name, sizeof(name), "%s/%s_%d.264", directory, id_name, seg);
804 return dc_raw_h264_open(video_output_file, name);
805 #ifndef GPAC_DISABLE_ISOM
806 case GPAC_VIDEO_MUXER:
807 snprintf(name, sizeof(name), "%s/%s_%d_gpac.mp4", directory, id_name, seg);
808 dc_gpac_video_moov_create(video_output_file, name);
809 return dc_gpac_video_isom_open_seg(video_output_file, NULL);
810 case GPAC_INIT_VIDEO_MUXER_AVC1:
811 if (seg == 1) {
812 snprintf(name, sizeof(name), "%s/%s_init_gpac.mp4", directory, id_name);
813 dc_gpac_video_moov_create(video_output_file, name);
814 video_output_file->first_dts_in_fragment = 0;
815 }
816 snprintf(name, sizeof(name), "%s/%s_%d_gpac.m4s", directory, id_name, seg);
817 return dc_gpac_video_isom_open_seg(video_output_file, name);
818 case GPAC_INIT_VIDEO_MUXER_AVC3:
819 if (seg == 0) {
820 snprintf(name, sizeof(name), "%s/%s_init_gpac.mp4", directory, id_name);
821 dc_gpac_video_moov_create(video_output_file, name);
822 video_output_file->first_dts_in_fragment = 0;
823 }
824 snprintf(name, sizeof(name), "%s/%s_%d_gpac.m4s", directory, id_name, seg);
825 return dc_gpac_video_isom_open_seg(video_output_file, name);
826 #endif
827 default:
828 return GF_BAD_PARAM;
829 };
830
831 return -2;
832 }
833
dc_video_muxer_write(VideoOutputFile * video_output_file,int frame_nb,Bool insert_ntp)834 int dc_video_muxer_write(VideoOutputFile *video_output_file, int frame_nb, Bool insert_ntp)
835 {
836 Bool segment_close = GF_FALSE;
837 Bool fragment_close = GF_FALSE;
838 switch (video_output_file->muxer_type) {
839 case FFMPEG_VIDEO_MUXER:
840 return dc_ffmpeg_video_muxer_write(video_output_file);
841 case RAW_VIDEO_H264:
842 return dc_raw_h264_write(video_output_file);
843 #ifndef GPAC_DISABLE_ISOM
844 case GPAC_VIDEO_MUXER:
845 case GPAC_INIT_VIDEO_MUXER_AVC1:
846 case GPAC_INIT_VIDEO_MUXER_AVC3:
847 if (video_output_file->use_source_timing) {
848 GF_Err ret;
849 if (!video_output_file->fragment_started) {
850 video_output_file->fragment_started = 1;
851 ret = gf_isom_start_fragment(video_output_file->isof, 1);
852 if (ret < 0)
853 return -1;
854
855
856 //insert UTC for each fragment
857 if (insert_ntp) {
858 gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, video_output_file->frame_ntp, video_output_file->codec_ctx->coded_frame->pts);
859 }
860
861 video_output_file->first_dts_in_fragment = video_output_file->codec_ctx->coded_frame->pkt_dts;
862 if (!video_output_file->segment_started) {
863 video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts;
864 video_output_file->segment_started = 1;
865 if (!video_output_file->nb_segments) {
866 video_output_file->pts_at_first_segment = video_output_file->pts_at_segment_start;
867 }
868
869 #ifndef GPAC_DISABLE_LOG
870 if (insert_ntp && gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_INFO)) {
871 if (!video_output_file->ntp_at_first_dts) {
872 video_output_file->ntp_at_first_dts = video_output_file->frame_ntp;
873 } else {
874 s32 ntp_diff = gf_net_get_ntp_diff_ms(video_output_file->ntp_at_first_dts);
875 s32 ts_diff = (s32) ( 1000 * (video_output_file->codec_ctx->coded_frame->pts - video_output_file->pts_at_first_segment) / video_output_file->timescale );
876
877 s32 diff_ms = ts_diff - ntp_diff;
878 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Video Segment start NTP diff: %d ms TS diff: %d ms drift: %d ms\n", ntp_diff, ts_diff, diff_ms));
879 }
880 }
881 #endif
882 }
883 gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts_in_fragment);
884 }
885
886 if (dc_gpac_video_isom_write(video_output_file) < 0) {
887 return -1;
888 }
889 video_output_file->last_pts = video_output_file->codec_ctx->coded_frame->pts;
890 video_output_file->last_dts = video_output_file->codec_ctx->coded_frame->pkt_dts;
891 GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] PTS: "LLU", DTS: "LLU", first DTS in frag: "LLU", timescale: %d, frag dur: %d\n", video_output_file->last_pts, video_output_file->last_dts, video_output_file->first_dts_in_fragment, video_output_file->timescale, video_output_file->frag_dur));
892
893 //we may have rounding errors on the input PTS :( add half frame dur safety
894 //flush segments based on the cumultated duration , to avoid drift
895 /* Check why segment tests work on PTS while fragment tests work on DTS ? */
896 /* Check why fragment closing is not tested based on accumulation of fragment duration to avoid drifts */
897 segment_close = ((video_output_file->last_pts - video_output_file->pts_at_first_segment + video_output_file->frame_dur) * 1000 >=
898 (video_output_file->nb_segments+1)*video_output_file->seg_dur * (u64)video_output_file->timescale);
899 #if 0
900 segment_close = ((video_output_file->last_pts - video_output_file->pts_at_segment_start + 3*video_output_file->frame_dur/2) * 1000 >=
901 (video_output_file->seg_dur * (u64)video_output_file->timescale);
902 #endif
903 //flush fragment if adding next frame will exceed target duration by half the frame duration
904 fragment_close = ((video_output_file->last_dts - video_output_file->first_dts_in_fragment + 3 * video_output_file->frame_dur / 2) * 1000 >=
905 (video_output_file->frag_dur * (u64)video_output_file->timescale));
906
907 if (segment_close || fragment_close) {
908 gf_isom_flush_fragments(video_output_file->isof, 1);
909 video_output_file->fragment_started = 0;
910 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment at UTC "LLU" ms - First DTS "LLU" last PTS "LLU" - First Segment PTS "LLU" timescale %d\n", gf_net_get_utc(), video_output_file->first_dts_in_fragment, video_output_file->codec_ctx->coded_frame->pts, video_output_file->pts_at_segment_start, video_output_file->timescale));
911 }
912
913 if (segment_close) {
914 return 1;
915 }
916 return 0;
917 }
918
919 if (frame_nb % video_output_file->frame_per_fragment == 0) {
920 gf_isom_start_fragment(video_output_file->isof, 1);
921
922 if (!video_output_file->segment_started) {
923 video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts;
924 video_output_file->segment_started = 1;
925
926 if (insert_ntp) {
927 gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, video_output_file->frame_ntp, video_output_file->pts_at_segment_start);
928 }
929 }
930
931
932 gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts_in_fragment);
933 video_output_file->first_dts_in_fragment += video_output_file->frame_per_fragment;
934 }
935
936 dc_gpac_video_isom_write(video_output_file);
937
938 if (frame_nb % video_output_file->frame_per_fragment == video_output_file->frame_per_fragment - 1) {
939 gf_isom_flush_fragments(video_output_file->isof, 1);
940 GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment to disk at UTC "LLU" ms - last coded frame PTS "LLU"\n", gf_net_get_utc(), video_output_file->codec_ctx->coded_frame->pts));
941 }
942
943 if (frame_nb + 1 == video_output_file->frame_per_segment)
944 return 1;
945
946 return 0;
947 #endif
948
949 default:
950 return -2;
951 }
952
953 return -2;
954 }
955
dc_video_muxer_close(VideoOutputFile * video_output_file)956 int dc_video_muxer_close(VideoOutputFile *video_output_file)
957 {
958 video_output_file->fragment_started = video_output_file->segment_started = 0;
959 video_output_file->nb_segments++;
960
961 switch (video_output_file->muxer_type) {
962 case FFMPEG_VIDEO_MUXER:
963 return dc_ffmpeg_video_muxer_close(video_output_file);
964 case RAW_VIDEO_H264:
965 return dc_raw_h264_close(video_output_file);
966 #ifndef GPAC_DISABLE_ISOM
967 case GPAC_VIDEO_MUXER:
968 dc_gpac_video_isom_close_seg(video_output_file);
969 return dc_gpac_video_isom_close(video_output_file);
970 case GPAC_INIT_VIDEO_MUXER_AVC1:
971 case GPAC_INIT_VIDEO_MUXER_AVC3:
972 return dc_gpac_video_isom_close_seg(video_output_file);
973 #endif
974 default:
975 return -2;
976 }
977
978 return -2;
979 }
980