/** * Get a list of supported containers */ int muxer_container_list(htsmsg_t *array) { htsmsg_t *mc; int c = 0; mc = htsmsg_create_map(); htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MATROSKA)); htsmsg_add_str(mc, "description", "Matroska"); htsmsg_add_msg(array, NULL, mc); c++; mc = htsmsg_create_map(); htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_PASS)); htsmsg_add_str(mc, "description", "Same as source (pass through)"); htsmsg_add_msg(array, NULL, mc); c++; #if ENABLE_LIBAV mc = htsmsg_create_map(); htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MPEGTS)); htsmsg_add_str(mc, "description", "MPEG-TS"); htsmsg_add_msg(array, NULL, mc); c++; mc = htsmsg_create_map(); htsmsg_add_str(mc, "name", muxer_container_type2txt(MC_MPEGPS)); htsmsg_add_str(mc, "description", "MPEG-PS (DVD)"); htsmsg_add_msg(array, NULL, mc); c++; #endif return c; }
/** * 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; }
/** * Create a new muxer */ muxer_t* muxer_create(muxer_container_type_t mc, const muxer_config_t *m_cfg) { muxer_t *m; assert(m_cfg); m = pass_muxer_create(mc, m_cfg); if(!m) m = tvh_muxer_create(mc, m_cfg); #if CONFIG_LIBAV if(!m) m = lav_muxer_create(mc, m_cfg); #endif if(!m) { tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container", muxer_container_type2txt(mc)); return NULL; } memcpy(&m->m_config, m_cfg, sizeof(muxer_config_t)); return m; }
/** * Create a new libavformat based muxer */ muxer_t* lav_muxer_create(const muxer_config_t *m_cfg) { const char *mux_name; lav_muxer_t *lm; AVOutputFormat *fmt; switch(m_cfg->m_type) { case MC_MPEGPS: mux_name = "dvd"; break; case MC_MATROSKA: case MC_AVMATROSKA: mux_name = "matroska"; break; case MC_WEBM: case MC_AVWEBM: mux_name = "webm"; break; case MC_AVMP4: mux_name = "mp4"; break; default: mux_name = muxer_container_type2txt(m_cfg->m_type); break; } fmt = av_guess_format(mux_name, NULL, NULL); if(!fmt) { tvherror(LS_LIBAV, "Can't find the '%s' muxer", mux_name); return NULL; } lm = calloc(1, sizeof(lav_muxer_t)); lm->m_open_stream = lav_muxer_open_stream; lm->m_open_file = lav_muxer_open_file; lm->m_init = lav_muxer_init; lm->m_reconfigure = lav_muxer_reconfigure; lm->m_mime = lav_muxer_mime; lm->m_add_marker = lav_muxer_add_marker; lm->m_write_meta = lav_muxer_write_meta; lm->m_write_pkt = lav_muxer_write_pkt; lm->m_close = lav_muxer_close; lm->m_destroy = lav_muxer_destroy; lm->lm_oc = avformat_alloc_context(); lm->lm_oc->oformat = fmt; lm->lm_fd = -1; lm->lm_init = 0; return (muxer_t*)lm; }
/** * Close the muxer and append trailer to output */ static int lav_muxer_close(muxer_t *m) { int ret = 0; lav_muxer_t *lm = (lav_muxer_t*)m; if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) { tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer", muxer_container_type2txt(lm->m_config.m_type)); lm->m_errors++; ret = -1; } return ret; }
/** * Create a new libavformat based muxer */ muxer_t* lav_muxer_create(muxer_container_type_t mc) { const char *mux_name; lav_muxer_t *lm; AVOutputFormat *fmt; switch(mc) { case MC_MPEGPS: mux_name = "dvd"; break; default: mux_name = muxer_container_type2txt(mc); break; } fmt = av_guess_format(mux_name, NULL, NULL); if(!fmt) { tvhlog(LOG_ERR, "libav", "Can't find the '%s' muxer", mux_name); return NULL; } lm = calloc(1, sizeof(lav_muxer_t)); lm->m_open_stream = lav_muxer_open_stream; lm->m_open_file = lav_muxer_open_file; lm->m_init = lav_muxer_init; lm->m_reconfigure = lav_muxer_reconfigure; lm->m_mime = lav_muxer_mime; lm->m_add_marker = lav_muxer_add_marker; lm->m_write_meta = lav_muxer_write_meta; lm->m_write_pkt = lav_muxer_write_pkt; lm->m_close = lav_muxer_close; lm->m_destroy = lav_muxer_destroy; lm->m_container = mc; lm->lm_oc = avformat_alloc_context(); lm->lm_oc->oformat = fmt; lm->lm_fd = -1; lm->lm_init = 0; return (muxer_t*)lm; }
/** * Create a new muxer */ muxer_t* muxer_create(muxer_container_type_t mc) { muxer_t *m; m = pass_muxer_create(mc); if(!m) m = tvh_muxer_create(mc); #if ENABLE_LIBAV if(!m) m = lav_muxer_create(mc); #endif if(!m) tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container", muxer_container_type2txt(mc)); return m; }
/** * Close the muxer and append trailer to output */ static int lav_muxer_close(muxer_t *m) { int i; int ret = 0; lav_muxer_t *lm = (lav_muxer_t*)m; if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) { tvhlog(LOG_WARNING, "libav", "Failed to write %s trailer", muxer_container_type2txt(lm->m_container)); lm->m_errors++; ret = -1; } if(lm->lm_h264_filter) av_bitstream_filter_close(lm->lm_h264_filter); for(i=0; i<lm->lm_oc->nb_streams; i++) av_freep(&lm->lm_oc->streams[i]->codec->extradata); lm->lm_oc->nb_streams = 0; return ret; }
/** * 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; }