/** * 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; }
/** * 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; }