/** * Init the muxer with streams */ static int lav_muxer_init(muxer_t* m, const struct streaming_start *ss, const char *name) { int i; const streaming_start_component_t *ssc; AVFormatContext *oc; lav_muxer_t *lm = (lav_muxer_t*)m; char app[128]; snprintf(app, sizeof(app), "Tvheadend %s", tvheadend_version); oc = lm->lm_oc; av_dict_set(&oc->metadata, "title", name, 0); av_dict_set(&oc->metadata, "service_name", name, 0); av_dict_set(&oc->metadata, "service_provider", app, 0); if(lm->m_container == MC_MPEGTS) lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb"); oc->max_delay = 0.7 * AV_TIME_BASE; for(i=0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; if(ssc->ssc_disabled) continue; if(!lav_muxer_support_stream(lm->m_container, ssc->ssc_type)) { tvhlog(LOG_WARNING, "libav", "%s is not supported in %s", streaming_component_type2txt(ssc->ssc_type), muxer_container_type2txt(lm->m_container)); continue; } if(lav_muxer_add_stream(lm, ssc)) { tvhlog(LOG_ERR, "libav", "Failed to add %s stream", streaming_component_type2txt(ssc->ssc_type)); continue; } } if(!lm->lm_oc->nb_streams) { tvhlog(LOG_ERR, "libav", "No supported streams available"); lm->m_errors++; return -1; } else if(avformat_write_header(lm->lm_oc, NULL) < 0) { tvhlog(LOG_ERR, "libav", "Failed to write %s header", muxer_container_type2txt(lm->m_container)); lm->m_errors++; return -1; } lm->lm_init = 1; return 0; }
static void recover_pts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) { th_pktref_t *pr, *srch; pktref_enqueue(&tf->tf_ptsq, pkt); while((pr = TAILQ_FIRST(&tf->tf_ptsq)) != NULL) { pkt = pr->pr_pkt; tfs = tfs_find(tf, pkt); switch(tfs->tfs_type) { case SCT_MPEG2VIDEO: switch(pkt->pkt_frametype) { case PKT_B_FRAME: /* B-frames have same PTS as DTS, pass them on */ pkt->pkt_pts = pkt->pkt_dts; tsfixprintf("TSFIX: %-12s PTS b-frame set to %"PRId64"\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_dts); break; case PKT_I_FRAME: case PKT_P_FRAME: /* Presentation occures at DTS of next I or P frame, try to find it */ srch = TAILQ_NEXT(pr, pr_link); while(1) { if(srch == NULL) return; /* not arrived yet, wait */ if(tfs_find(tf, srch->pr_pkt) == tfs && srch->pr_pkt->pkt_frametype <= PKT_P_FRAME) { pkt->pkt_pts = srch->pr_pkt->pkt_dts; tsfixprintf("TSFIX: %-12s PTS *-frame set to %"PRId64"\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_pts); break; } srch = TAILQ_NEXT(srch, pr_link); } break; } break; default: break; } TAILQ_REMOVE(&tf->tf_ptsq, pr, pr_link); normalize_ts(tf, tfs, pkt); free(pr); } }
static void tsfix_input_packet(tsfix_t *tf, streaming_message_t *sm) { th_pkt_t *pkt = pkt_copy_shallow(sm->sm_data); tfstream_t *tfs = tfs_find(tf, pkt); streaming_msg_free(sm); if(tfs == NULL || dispatch_clock < tf->tf_start_time) { pkt_ref_dec(pkt); return; } if(tf->tf_tsref == PTS_UNSET && (!tf->tf_hasvideo || (SCT_ISVIDEO(tfs->tfs_type) && pkt->pkt_frametype == PKT_I_FRAME))) { tf->tf_tsref = pkt->pkt_dts & PTS_MASK; tsfixprintf("reference clock set to %"PRId64"\n", tf->tf_tsref); } if(pkt->pkt_dts == PTS_UNSET) { int pdur = pkt->pkt_duration >> pkt->pkt_field; if(tfs->tfs_last_dts_in == PTS_UNSET) { pkt_ref_dec(pkt); return; } pkt->pkt_dts = (tfs->tfs_last_dts_in + pdur) & PTS_MASK; tsfixprintf("TSFIX: %-12s DTS set to last %"PRId64" +%d == %"PRId64"\n", streaming_component_type2txt(tfs->tfs_type), tfs->tfs_last_dts_in, pdur, pkt->pkt_dts); }
/** * Compute PTS (if not known) */ static void compute_pts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) { // If PTS is missing, set it to DTS if not video if(pkt->pkt_pts == PTS_UNSET && !SCT_ISVIDEO(tfs->tfs_type)) { pkt->pkt_pts = pkt->pkt_dts; tsfixprintf("TSFIX: %-12s PTS set to %"PRId64"\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_pts); } /* PTS known and no other packets in queue, deliver at once */ if(pkt->pkt_pts != PTS_UNSET && TAILQ_FIRST(&tf->tf_ptsq) == NULL) normalize_ts(tf, tfs, pkt); else recover_pts(tf, tfs, pkt); }
void pkt_trace_(const char *file, int line, int subsys, th_pkt_t *pkt, const char *fmt, ...) { char buf[512], _pcr[22], _dts[22], _pts[22], _type[2], _meta[20]; va_list args; va_start(args, fmt); if (SCT_ISVIDEO(pkt->pkt_type) && pkt->v.pkt_frametype) { _type[0] = pkt_frametype_to_char(pkt->v.pkt_frametype); _type[1] = '\0'; } else { _type[0] = '\0'; } if (pkt->pkt_meta) snprintf(_meta, sizeof(_meta), " meta %zu", pktbuf_len(pkt->pkt_meta)); else _meta[0] = '\0'; snprintf(buf, sizeof(buf), "%s%spkt stream %d %s%s%s" " pcr %s dts %s pts %s" " dur %d len %zu err %i%s%s", fmt ? fmt : "", fmt ? " (" : "", pkt->pkt_componentindex, streaming_component_type2txt(pkt->pkt_type), _type[0] ? " type " : "", _type, pts_to_string(pkt->pkt_pcr, _pcr), pts_to_string(pkt->pkt_dts, _dts), pts_to_string(pkt->pkt_pts, _pts), pkt->pkt_duration, pktbuf_len(pkt->pkt_payload), pkt->pkt_err, _meta, fmt ? ")" : ""); tvhlogv(file, line, LOG_TRACE, subsys, buf, &args); va_end(args); }
static void normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) { int64_t dts, d; int checkts = SCT_ISAUDIO(tfs->tfs_type) || SCT_ISVIDEO(tfs->tfs_type); if(tf->tf_tsref == PTS_UNSET) { pkt_ref_dec(pkt); return; } pkt->pkt_dts &= PTS_MASK; pkt->pkt_pts &= PTS_MASK; /* Subtract the transport wide start offset */ dts = pkt->pkt_dts - tf->tf_tsref; if(tfs->tfs_last_dts_norm == PTS_UNSET) { if(dts < 0) { /* Early packet with negative time stamp, drop those */ pkt_ref_dec(pkt); return; } } else if(checkts) { d = dts + tfs->tfs_dts_epoch - tfs->tfs_last_dts_norm; if(d < 0 || d > 90000) { if(d < -PTS_MASK || d > -PTS_MASK + 180000) { tfs->tfs_bad_dts++; if(tfs->tfs_bad_dts < 5) { tvhlog(LOG_ERR, "parser", "transport stream %s, DTS discontinuity. " "DTS = %" PRId64 ", last = %" PRId64, streaming_component_type2txt(tfs->tfs_type), dts, tfs->tfs_last_dts_norm); } } else { /* DTS wrapped, increase upper bits */ tfs->tfs_dts_epoch += PTS_MASK + 1; tfs->tfs_bad_dts = 0; } } else { tfs->tfs_bad_dts = 0; } } dts += tfs->tfs_dts_epoch; tfs->tfs_last_dts_norm = dts; if(pkt->pkt_pts != PTS_UNSET) { /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */ int64_t ptsoff = (pkt->pkt_pts - pkt->pkt_dts) & PTS_MASK; pkt->pkt_pts = dts + ptsoff; } pkt->pkt_dts = dts; tsfixprintf("TSFIX: %-12s %d %10"PRId64" %10"PRId64" %10d %zd\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_frametype, pkt->pkt_dts, pkt->pkt_pts, pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); streaming_message_t *sm = streaming_msg_create_pkt(pkt); streaming_target_deliver2(tf->tf_output, sm); pkt_ref_dec(pkt); }
static int dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss) { const source_info_t *si = &ss->ss_si; const streaming_start_component_t *ssc; int i; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); de->de_mux = muxer_create(de->de_mc); if(!de->de_mux) { dvr_rec_fatal_error(de, "Unable to create muxer"); return -1; } if(pvr_generate_filename(de, ss) != 0) { dvr_rec_fatal_error(de, "Unable to create directories"); return -1; } if(muxer_open_file(de->de_mux, de->de_filename)) { dvr_rec_fatal_error(de, "Unable to open file"); return -1; } if(muxer_init(de->de_mux, ss, lang_str_get(de->de_title, NULL))) { dvr_rec_fatal_error(de, "Unable to init file"); return -1; } if(cfg->dvr_flags & DVR_TAG_FILES && de->de_bcast) { if(muxer_write_meta(de->de_mux, de->de_bcast)) { dvr_rec_fatal_error(de, "Unable to write meta data"); return -1; } } tvhlog(LOG_INFO, "dvr", "%s from " "adapter: \"%s\", " "network: \"%s\", mux: \"%s\", provider: \"%s\", " "service: \"%s\"", de->de_filename ?: lang_str_get(de->de_title, NULL), si->si_adapter ?: "<N/A>", si->si_network ?: "<N/A>", si->si_mux ?: "<N/A>", si->si_provider ?: "<N/A>", si->si_service ?: "<N/A>"); tvhlog(LOG_INFO, "dvr", " # %-16s %-4s %-10s %-12s %-11s %-8s", "type", "lang", "resolution", "aspect ratio", "sample rate", "channels"); for(i = 0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; char res[11]; char asp[6]; char sr[6]; char ch[7]; if(SCT_ISAUDIO(ssc->ssc_type)) { if(ssc->ssc_sri) snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri)); else strcpy(sr, "?"); if(ssc->ssc_channels == 6) snprintf(ch, sizeof(ch), "5.1"); else if(ssc->ssc_channels == 0) strcpy(ch, "?"); else snprintf(ch, sizeof(ch), "%d", ssc->ssc_channels); } else { sr[0] = 0; ch[0] = 0; } if(SCT_ISVIDEO(ssc->ssc_type)) { if(ssc->ssc_width && ssc->ssc_height) snprintf(res, sizeof(res), "%dx%d", ssc->ssc_width, ssc->ssc_height); else strcpy(res, "?"); } else { res[0] = 0; } if(SCT_ISVIDEO(ssc->ssc_type)) { if(ssc->ssc_aspect_num && ssc->ssc_aspect_den) snprintf(asp, sizeof(asp), "%d:%d", ssc->ssc_aspect_num, ssc->ssc_aspect_den); else strcpy(asp, "?"); } else { asp[0] = 0; } tvhlog(LOG_INFO, "dvr", "%2d %-16s %-4s %-10s %-12s %-11s %-8s %s", ssc->ssc_index, streaming_component_type2txt(ssc->ssc_type), ssc->ssc_lang, res, asp, sr, ch, ssc->ssc_disabled ? "<disabled, no valid input>" : ""); } return 0; }
static void normalize_ts(tsfix_t *tf, tfstream_t *tfs, th_pkt_t *pkt) { int64_t ref, dts, d; if(tf->tf_tsref == PTS_UNSET) { pkt_ref_dec(pkt); return; } pkt->pkt_dts &= PTS_MASK; pkt->pkt_pts &= PTS_MASK; /* Subtract the transport wide start offset */ ref = tfs->tfs_local_ref != PTS_UNSET ? tfs->tfs_local_ref : tf->tf_tsref; dts = pkt->pkt_dts - ref; if(tfs->tfs_last_dts_norm == PTS_UNSET) { if(dts < 0) { /* Early packet with negative time stamp, drop those */ pkt_ref_dec(pkt); return; } } else { int64_t low = 90000; /* one second */ int64_t upper = 180000; /* two seconds */ d = dts + tfs->tfs_dts_epoch - tfs->tfs_last_dts_norm; if (SCT_ISSUBTITLE(tfs->tfs_type)) { /* * special conditions for subtitles, because they may be broadcasted * with large time gaps */ low = PTS_MASK / 2; /* more than 13 hours */ upper = low - 1; } if (d < 0 || d > low) { if(d < -PTS_MASK || d > -PTS_MASK + upper) { tfs->tfs_bad_dts++; if(tfs->tfs_bad_dts < 5) { tvhlog(LOG_ERR, "parser", "transport stream %s, DTS discontinuity. " "DTS = %" PRId64 ", last = %" PRId64, streaming_component_type2txt(tfs->tfs_type), dts, tfs->tfs_last_dts_norm); } } else { /* DTS wrapped, increase upper bits */ tfs->tfs_dts_epoch += PTS_MASK + 1; tfs->tfs_bad_dts = 0; } } else { tfs->tfs_bad_dts = 0; } } dts += tfs->tfs_dts_epoch; tfs->tfs_last_dts_norm = dts; if(pkt->pkt_pts != PTS_UNSET) { /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */ d = (pkt->pkt_pts - pkt->pkt_dts) & PTS_MASK; pkt->pkt_pts = dts + d; } pkt->pkt_dts = dts; tsfixprintf("TSFIX: %-12s %d %10"PRId64" %10"PRId64" %10d %zd\n", streaming_component_type2txt(tfs->tfs_type), pkt->pkt_frametype, pkt->pkt_dts, pkt->pkt_pts, pkt->pkt_duration, pktbuf_len(pkt->pkt_payload)); streaming_message_t *sm = streaming_msg_create_pkt(pkt); streaming_target_deliver2(tf->tf_output, sm); pkt_ref_dec(pkt); }
/** * Init the muxer with streams */ static int lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) { int i; streaming_start_component_t *ssc; AVFormatContext *oc; AVDictionary *opts = NULL; lav_muxer_t *lm = (lav_muxer_t*)m; char app[128]; snprintf(app, sizeof(app), "Tvheadend %s", tvheadend_version); oc = lm->lm_oc; av_dict_set(&oc->metadata, "title", name, 0); av_dict_set(&oc->metadata, "service_name", name, 0); av_dict_set(&oc->metadata, "service_provider", app, 0); if(lm->m_config.m_type == MC_MPEGTS) { lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb"); lm->lm_hevc_filter = av_bitstream_filter_init("hevc_mp4toannexb"); } oc->max_delay = 0.7 * AV_TIME_BASE; for(i=0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; if(ssc->ssc_disabled) continue; if(!lav_muxer_support_stream(lm->m_config.m_type, ssc->ssc_type)) { tvhwarn(LS_LIBAV, "%s is not supported in %s", streaming_component_type2txt(ssc->ssc_type), muxer_container_type2txt(lm->m_config.m_type)); ssc->ssc_muxer_disabled = 1; continue; } if(lav_muxer_add_stream(lm, ssc)) { tvherror(LS_LIBAV, "Failed to add %s stream", streaming_component_type2txt(ssc->ssc_type)); ssc->ssc_muxer_disabled = 1; continue; } } if(lm->m_config.m_type == MC_AVMP4) { av_dict_set(&opts, "frag_duration", "1", 0); av_dict_set(&opts, "ism_lookahead", "0", 0); } if(!lm->lm_oc->nb_streams) { tvherror(LS_LIBAV, "No supported streams available"); lm->m_errors++; return -1; } else if(avformat_write_header(lm->lm_oc, &opts) < 0) { tvherror(LS_LIBAV, "Failed to write %s header", muxer_container_type2txt(lm->m_config.m_type)); lm->m_errors++; return -1; } if (opts) av_dict_free(&opts); lm->lm_init = 1; return 0; }