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); } else { /* For teletext, the encoders might use completely different timestamps */ /* If the difference is greater than 2 seconds, use the actual dts value */ if (tfs->tfs_type == SCT_TELETEXT && tfs->tfs_local_ref == PTS_UNSET && tf->tf_tsref != PTS_UNSET && pkt->pkt_dts != PTS_UNSET) { int64_t diff = tsfix_ts_diff(tf->tf_tsref, pkt->pkt_dts); if (diff > 2 * 90000) { tfstream_t *tfs2; tvhwarn("parser", "The timediff for TELETEXT is big (%"PRId64"), using current dts", diff); tfs->tfs_local_ref = pkt->pkt_dts; /* Text subtitles extracted from teletext have same timebase */ LIST_FOREACH(tfs2, &tf->tf_streams, tfs_link) if(tfs2->tfs_type == SCT_TEXTSUB) tfs2->tfs_local_ref = pkt->pkt_dts; } else {
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); }
/** * Figure out the mimetype */ static const char* tvh_muxer_mime(muxer_t* m, const struct streaming_start *ss) { int i; int has_audio; int has_video; const streaming_start_component_t *ssc; has_audio = 0; has_video = 0; for(i=0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; if(ssc->ssc_disabled) continue; has_video |= SCT_ISVIDEO(ssc->ssc_type); has_audio |= SCT_ISAUDIO(ssc->ssc_type); } if(has_video) return muxer_container_type2mime(m->m_container, 1); else if(has_audio) return muxer_container_type2mime(m->m_container, 0); else return muxer_container_type2mime(MC_UNKNOWN, 0); }
/** * Check if a container supports a given streaming component */ static int lav_muxer_support_stream(muxer_container_type_t mc, streaming_component_type_t type) { int ret = 0; switch(mc) { case MC_MATROSKA: case MC_AVMATROSKA: ret |= SCT_ISAUDIO(type); ret |= SCT_ISVIDEO(type); ret |= SCT_ISSUBTITLE(type); break; case MC_WEBM: case MC_AVWEBM: ret |= type == SCT_VP8; ret |= type == SCT_VORBIS; break; case MC_MPEGTS: ret |= (type == SCT_MPEG2VIDEO); ret |= (type == SCT_H264); ret |= (type == SCT_HEVC); ret |= (type == SCT_MPEG2AUDIO); ret |= (type == SCT_AC3); ret |= (type == SCT_AAC); ret |= (type == SCT_MP4A); ret |= (type == SCT_EAC3); //Some pids lack pts, disable for now //ret |= (type == SCT_TELETEXT); ret |= (type == SCT_DVBSUB); break; case MC_MPEGPS: ret |= (type == SCT_MPEG2VIDEO); ret |= (type == SCT_MPEG2AUDIO); ret |= (type == SCT_AC3); break; case MC_AVMP4: ret |= (type == SCT_MPEG2VIDEO); ret |= (type == SCT_H264); ret |= (type == SCT_HEVC); ret |= (type == SCT_MPEG2AUDIO); ret |= (type == SCT_AC3); ret |= (type == SCT_AAC); ret |= (type == SCT_MP4A); ret |= (type == SCT_EAC3); break; default: break; } return ret; }
static inline ssize_t _process_msg0 ( timeshift_t *ts, timeshift_file_t *tsf, streaming_message_t **smp ) { int i; ssize_t err; streaming_start_t *ss; streaming_message_t *sm = *smp; if (sm->sm_type == SMT_START) { err = 0; timeshift_index_data_t *ti = calloc(1, sizeof(timeshift_index_data_t)); ti->pos = tsf->size; ti->data = sm; *smp = NULL; TAILQ_INSERT_TAIL(&tsf->sstart, ti, link); /* Update video index */ ss = sm->sm_data; for (i = 0; i < ss->ss_num_components; i++) if (SCT_ISVIDEO(ss->ss_components[i].ssc_type)) ts->vididx = ss->ss_components[i].ssc_index; } else if (sm->sm_type == SMT_SIGNAL_STATUS) err = timeshift_write_sigstat(tsf, sm->sm_time, sm->sm_data); else if (sm->sm_type == SMT_PACKET) { err = timeshift_write_packet(tsf, sm->sm_time, sm->sm_data); if (err > 0) { th_pkt_t *pkt = sm->sm_data; /* Index video iframes */ if (pkt->pkt_componentindex == ts->vididx && pkt->pkt_frametype == PKT_I_FRAME) { timeshift_index_iframe_t *ti = calloc(1, sizeof(timeshift_index_iframe_t)); ti->pos = tsf->size; ti->time = sm->sm_time; TAILQ_INSERT_TAIL(&tsf->iframes, ti, link); } } } else if (sm->sm_type == SMT_MPEGTS) err = timeshift_write_mpegts(tsf, sm->sm_time, sm->sm_data); else err = 0; /* OK */ if (err > 0) { tsf->last = sm->sm_time; tsf->size += err; atomic_add_u64(×hift_total_size, err); if (tsf->ram) atomic_add_u64(×hift_total_ram_size, err); } return err; }
/* * Update smt_start */ static void _update_smt_start ( timeshift_t *ts, streaming_start_t *ss ) { int i; if (ts->smt_start) streaming_start_unref(ts->smt_start); streaming_start_ref(ss); ts->smt_start = ss; /* Update video index */ for (i = 0; i < ss->ss_num_components; i++) if (SCT_ISVIDEO(ss->ss_components[i].ssc_type)) { ts->vididx = ss->ss_components[i].ssc_index; break; } }
/** * 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); }
static void tsfix_start(tsfix_t *tf, streaming_start_t *ss) { int i; int hasvideo = 0; for(i = 0; i < ss->ss_num_components; i++) { const streaming_start_component_t *ssc = &ss->ss_components[i]; tsfix_add_stream(tf, ssc->ssc_index, ssc->ssc_type); hasvideo |= SCT_ISVIDEO(ssc->ssc_type); } TAILQ_INIT(&tf->tf_ptsq); tf->tf_tsref = PTS_UNSET; tf->tf_hasvideo = hasvideo; }
static void tsfix_start(tsfix_t *tf, streaming_start_t *ss) { int i, hasvideo = 0, vwait = 0; for(i = 0; i < ss->ss_num_components; i++) { const streaming_start_component_t *ssc = &ss->ss_components[i]; tsfix_add_stream(tf, ssc->ssc_index, ssc->ssc_type); if (SCT_ISVIDEO(ssc->ssc_type)) { hasvideo = 1; if (ssc->ssc_width == 0 || ssc->ssc_height == 0) vwait = 1; } } TAILQ_INIT(&tf->tf_ptsq); tf->tf_tsref = PTS_UNSET; tf->tf_hasvideo = hasvideo; tf->tf_wait_for_video = vwait; }
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); }
/** * Figure out the mime-type for the muxed data stream */ static const char* pass_muxer_mime(muxer_t* m, const struct streaming_start *ss) { int i; int has_audio; int has_video; muxer_container_type_t mc; const streaming_start_component_t *ssc; const source_info_t *si = &ss->ss_si; has_audio = 0; has_video = 0; for(i=0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; if(ssc->ssc_disabled) continue; has_video |= SCT_ISVIDEO(ssc->ssc_type); has_audio |= SCT_ISAUDIO(ssc->ssc_type); } if(si->si_type == S_MPEG_TS) mc = MC_MPEGTS; else if(si->si_type == S_MPEG_PS) mc = MC_MPEGPS; else mc = MC_UNKNOWN; if(has_video) return muxer_container_type2mime(mc, 1); else if(has_audio) return muxer_container_type2mime(mc, 0); else return muxer_container_type2mime(MC_UNKNOWN, 0); }
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; }
/** * Add a stream to the muxer */ static int lav_muxer_add_stream(lav_muxer_t *lm, const streaming_start_component_t *ssc) { AVStream *st; AVCodecContext *c; st = avformat_new_stream(lm->lm_oc, NULL); if (!st) return -1; st->id = ssc->ssc_index; c = st->codec; c->codec_id = streaming_component_type2codec_id(ssc->ssc_type); switch(lm->m_container) { case MC_MATROSKA: st->time_base.num = 1000000; st->time_base.den = 1; break; case MC_MPEGPS: c->rc_buffer_size = 224*1024*8; //Fall-through case MC_MPEGTS: st->time_base.num = 90000; st->time_base.den = 1; break; default: st->time_base = AV_TIME_BASE_Q; break; } if(ssc->ssc_gh) { c->extradata_size = pktbuf_len(ssc->ssc_gh); c->extradata = av_malloc(c->extradata_size); memcpy(c->extradata, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); } if(SCT_ISAUDIO(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_fmt = AV_SAMPLE_FMT_S16; c->sample_rate = sri_to_rate(ssc->ssc_sri); c->channels = ssc->ssc_channels; c->time_base.num = 1; c->time_base.den = c->sample_rate; av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); } else if(SCT_ISVIDEO(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_VIDEO; c->width = ssc->ssc_width; c->height = ssc->ssc_height; c->time_base.num = 1; c->time_base.den = 25; c->sample_aspect_ratio.num = ssc->ssc_aspect_num; c->sample_aspect_ratio.den = ssc->ssc_aspect_den; st->sample_aspect_ratio.num = c->sample_aspect_ratio.num; st->sample_aspect_ratio.den = c->sample_aspect_ratio.den; } else if(SCT_ISSUBTITLE(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_SUBTITLE; av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); } if(lm->lm_oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; return 0; }
/** * Add a stream to the muxer */ static int lav_muxer_add_stream(lav_muxer_t *lm, const streaming_start_component_t *ssc) { AVStream *st; AVCodecContext *c; st = avformat_new_stream(lm->lm_oc, NULL); if (!st) return -1; st->id = ssc->ssc_index; c = st->codec; c->codec_id = streaming_component_type2codec_id(ssc->ssc_type); switch(lm->m_config.m_type) { case MC_MATROSKA: case MC_AVMATROSKA: case MC_AVMP4: st->time_base.num = 1000000; st->time_base.den = 1; break; case MC_MPEGPS: c->rc_buffer_size = 224*1024*8; //Fall-through case MC_MPEGTS: st->time_base.num = 90000; st->time_base.den = 1; break; default: st->time_base = AV_TIME_BASE_Q; break; } if(ssc->ssc_gh) { if (ssc->ssc_type == SCT_H264 || ssc->ssc_type == SCT_HEVC) { sbuf_t hdr; sbuf_init(&hdr); if (ssc->ssc_type == SCT_H264) { isom_write_avcc(&hdr, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); } else { isom_write_hvcc(&hdr, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); } c->extradata_size = hdr.sb_ptr; c->extradata = av_malloc(hdr.sb_ptr); memcpy(c->extradata, hdr.sb_data, hdr.sb_ptr); sbuf_free(&hdr); } else { c->extradata_size = pktbuf_len(ssc->ssc_gh); c->extradata = av_malloc(c->extradata_size); memcpy(c->extradata, pktbuf_ptr(ssc->ssc_gh), pktbuf_len(ssc->ssc_gh)); } } if(SCT_ISAUDIO(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_fmt = AV_SAMPLE_FMT_S16; c->sample_rate = sri_to_rate(ssc->ssc_sri); c->channels = ssc->ssc_channels; #if 0 c->time_base.num = 1; c->time_base.den = c->sample_rate; #else c->time_base = st->time_base; #endif av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); } else if(SCT_ISVIDEO(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_VIDEO; c->width = ssc->ssc_width; c->height = ssc->ssc_height; c->time_base.num = 1; c->time_base.den = 25; c->sample_aspect_ratio.num = ssc->ssc_aspect_num; c->sample_aspect_ratio.den = ssc->ssc_aspect_den; if (lm->m_config.m_type == MC_AVMP4) { /* this is a whole hell */ AVRational ratio = { c->height, c->width }; c->sample_aspect_ratio = av_mul_q(c->sample_aspect_ratio, ratio); } st->sample_aspect_ratio.num = c->sample_aspect_ratio.num; st->sample_aspect_ratio.den = c->sample_aspect_ratio.den; } else if(SCT_ISSUBTITLE(ssc->ssc_type)) { c->codec_type = AVMEDIA_TYPE_SUBTITLE; av_dict_set(&st->metadata, "language", ssc->ssc_lang, 0); } if(lm->lm_oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; return 0; }