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