int dc_audio_muxer_write(AudioOutputFile * p_aoutf, int i_frame_nb) { //GF_Err ret; switch (p_aoutf->muxer_type) { case FFMPEG_AUDIO_MUXER: return dc_ffmpeg_audio_muxer_write(p_aoutf); case GPAC_AUDIO_MUXER: case GPAC_INIT_AUDIO_MUXER: if (i_frame_nb % p_aoutf->i_frame_per_frag == 0) { gf_isom_start_fragment(p_aoutf->p_isof, 1); gf_isom_set_traf_base_media_decode_time(p_aoutf->p_isof, 1, p_aoutf->i_first_dts * p_aoutf->i_frame_size); p_aoutf->i_first_dts += p_aoutf->i_frame_per_frag; } dc_gpac_audio_isom_write(p_aoutf); if (i_frame_nb % p_aoutf->i_frame_per_frag == p_aoutf->i_frame_per_frag - 1) { gf_isom_flush_fragments(p_aoutf->p_isof, 1); } if(i_frame_nb + 1 == p_aoutf->i_frame_per_seg) return 1; return 0; default: return GF_BAD_PARAM; }; return GF_BAD_PARAM; }
int dc_audio_muxer_write(AudioOutputFile *audio_output_file, int frame_nb, Bool insert_ntp) { switch (audio_output_file->muxer_type) { case FFMPEG_AUDIO_MUXER: return dc_ffmpeg_audio_muxer_write(audio_output_file); #ifndef GPAC_DISABLE_ISOM case GPAC_AUDIO_MUXER: case GPAC_INIT_AUDIO_MUXER: if (frame_nb % audio_output_file->frame_per_frag == 0) { gf_isom_start_fragment(audio_output_file->isof, 1); if (insert_ntp) { gf_isom_set_fragment_reference_time(audio_output_file->isof, 1, audio_output_file->frame_ntp, audio_output_file->first_dts * audio_output_file->codec_ctx->frame_size); } gf_isom_set_traf_base_media_decode_time(audio_output_file->isof, 1, audio_output_file->first_dts * audio_output_file->codec_ctx->frame_size); audio_output_file->first_dts += audio_output_file->frame_per_frag; GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio start fragment first DTS %d at "LLU"\n", audio_output_file->first_dts, gf_net_get_utc() )); } dc_gpac_audio_isom_write(audio_output_file); if (frame_nb % audio_output_file->frame_per_frag == audio_output_file->frame_per_frag - 1) { gf_isom_flush_fragments(audio_output_file->isof, 1); GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DashCast] Audio flush fragment first DTS %d at "LLU"\n", audio_output_file->first_dts, gf_net_get_utc() )); } //TODO - do same as video, flush based on time in case of losses if (frame_nb + 1 == audio_output_file->frame_per_seg) { return 1; } return 0; #endif default: return GF_BAD_PARAM; } return GF_BAD_PARAM; }
int dc_video_muxer_write(VideoOutputFile *video_output_file, int frame_nb, Bool insert_utc) { u64 frame_dur; GF_Err ret; switch (video_output_file->muxer_type) { case FFMPEG_VIDEO_MUXER: return dc_ffmpeg_video_muxer_write(video_output_file); case RAW_VIDEO_H264: return dc_raw_h264_write(video_output_file); case GPAC_VIDEO_MUXER: case GPAC_INIT_VIDEO_MUXER_AVC1: case GPAC_INIT_VIDEO_MUXER_AVC3: if (video_output_file->use_source_timing) { if (!video_output_file->fragment_started) { video_output_file->fragment_started = 1; ret = gf_isom_start_fragment(video_output_file->isof, 1); if (ret < 0) return -1; video_output_file->first_dts = video_output_file->codec_ctx->coded_frame->pkt_dts; if (!video_output_file->segment_started) { u32 sec, frac; u64 ntpts; video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts; video_output_file->segment_started = 1; if (insert_utc) { gf_net_get_ntp(&sec, &frac); ntpts = sec; ntpts <<= 32; ntpts |= frac; gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, ntpts, video_output_file->pts_at_segment_start); } } gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts); } //keep track of previous frame dur and use last dur as the default duration for next sample //this works fine because we perform frame rate regulation at the capture stage frame_dur = video_output_file->codec_ctx->coded_frame->pts - video_output_file->last_pts; if (frame_dur && (video_output_file->frame_dur>(u32) frame_dur)) { GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("New frame dur detected: %d vs %d old\n", (u32) frame_dur, (u32) video_output_file->frame_dur)); video_output_file->frame_dur = (u32)frame_dur; } if (dc_gpac_video_isom_write(video_output_file) < 0) { return -1; } video_output_file->last_pts = video_output_file->codec_ctx->coded_frame->pts; video_output_file->last_dts = video_output_file->codec_ctx->coded_frame->pkt_dts; if (( video_output_file->last_dts - video_output_file->first_dts + video_output_file->frame_dur) /video_output_file->timescale >= video_output_file->frag_dur / 1000) { gf_isom_flush_fragments(video_output_file->isof, 1); video_output_file->fragment_started = 0; GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment to disk at UTC "LLU" ms - last coded frame PTS %d\n", gf_net_get_utc(), video_output_file->codec_ctx->coded_frame->pts)); } //we may have rounding errors on the input PTS :( add half frame dur safety if (1000 * ( video_output_file->last_pts - video_output_file->pts_at_segment_start + 3*video_output_file->frame_dur/2) /video_output_file->timescale >= video_output_file->seg_dur ) { return 1; } return 0; } if (frame_nb % video_output_file->frame_per_fragment == 0) { gf_isom_start_fragment(video_output_file->isof, 1); if (!video_output_file->segment_started) { u32 sec, frac; u64 ntpts; video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts; video_output_file->segment_started = 1; if (insert_utc) { time_t secs; struct tm t; gf_net_get_ntp(&sec, &frac); ntpts = sec; ntpts <<= 32; ntpts |= frac; gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, ntpts, video_output_file->pts_at_segment_start); secs = sec - GF_NTP_SEC_1900_TO_1970; t = *gmtime(&secs); GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Producer reference clock: Timestamp %d matches sender NTP time %d-%02d-%02dT%02d:%02d:%02dZ (NTP frac part %u) \n", (u32) video_output_file->pts_at_segment_start, 1900+t.tm_year, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, (u32) frac )); } } gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts); video_output_file->first_dts += video_output_file->frame_per_fragment; } dc_gpac_video_isom_write(video_output_file); if (frame_nb % video_output_file->frame_per_fragment == video_output_file->frame_per_fragment - 1) { gf_isom_flush_fragments(video_output_file->isof, 1); GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment to disk at UTC "LLU" ms - last coded frame PTS %d\n", gf_net_get_utc(), video_output_file->codec_ctx->coded_frame->pts)); } if (frame_nb + 1 == video_output_file->frame_per_segment) return 1; return 0; default: return -2; } return -2; }