From: Jaroslav Kysela Date: Fri, 8 Jan 2016 19:48:03 +0000 (+0100) Subject: muxer: add experimental avlib/mp4 muxer X-Git-Tag: v4.2.1~1190 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=61ad50aa71ec799eb51519b068c07ff4cf7ed263;p=thirdparty%2Ftvheadend.git muxer: add experimental avlib/mp4 muxer --- diff --git a/src/muxer.c b/src/muxer.c index aa6da806f..af8240509 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -45,6 +45,7 @@ static struct strtab container_audio_mime[] = { { "audio/webm", MC_AVWEBM }, { "audio/mp2t", MC_MPEGTS }, { "audio/mpeg", MC_MPEGPS }, + { "audio/mp4", MC_AVMP4 }, { "application/octet-stream", MC_PASS }, { "application/octet-stream", MC_RAW }, }; @@ -61,6 +62,7 @@ static struct strtab container_video_mime[] = { { "video/webm", MC_AVWEBM }, { "video/mp2t", MC_MPEGTS }, { "video/mpeg", MC_MPEGPS }, + { "video/mp4", MC_AVMP4 }, { "application/octet-stream", MC_PASS }, { "application/octet-stream", MC_RAW }, }; @@ -79,6 +81,7 @@ static struct strtab container_name[] = { { "raw", MC_RAW }, { "avmatroska", MC_AVMATROSKA }, { "avwebm", MC_AVWEBM }, + { "avmp4", MC_AVMP4 }, }; @@ -95,6 +98,7 @@ static struct strtab container_audio_file_suffix[] = { { "bin", MC_RAW }, { "mka", MC_AVMATROSKA }, { "webm", MC_AVWEBM }, + { "mp4", MC_AVMP4 }, }; @@ -111,6 +115,7 @@ static struct strtab container_video_file_suffix[] = { { "bin", MC_RAW }, { "mkv", MC_AVMATROSKA }, { "webm", MC_AVWEBM }, + { "mp4", MC_AVMP4 }, }; diff --git a/src/muxer.h b/src/muxer.h index 06abae019..28a623730 100644 --- a/src/muxer.h +++ b/src/muxer.h @@ -33,6 +33,7 @@ typedef enum { MC_WEBM = 6, MC_AVMATROSKA = 7, MC_AVWEBM = 8, + MC_AVMP4 = 9, } muxer_container_type_t; typedef enum { diff --git a/src/muxer/muxer_libav.c b/src/muxer/muxer_libav.c index f6b972ddb..82b0c9a55 100644 --- a/src/muxer/muxer_libav.c +++ b/src/muxer/muxer_libav.c @@ -91,6 +91,7 @@ lav_muxer_add_stream(lav_muxer_t *lm, 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; @@ -217,6 +218,18 @@ lav_muxer_support_stream(muxer_container_type_t mc, ret |= (type == SCT_MPEG2AUDIO); ret |= (type == SCT_AC3); + 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; } @@ -270,6 +283,7 @@ 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]; @@ -310,17 +324,25 @@ lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) } } + 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) { tvhlog(LOG_ERR, "libav", "No supported streams available"); lm->m_errors++; return -1; - } else if(avformat_write_header(lm->lm_oc, NULL) < 0) { + } else if(avformat_write_header(lm->lm_oc, &opts) < 0) { tvhlog(LOG_ERR, "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; @@ -399,6 +421,7 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) AVFormatContext *oc; AVStream *st; AVPacket packet; + enum AVCodecID codec_id; th_pkt_t *pkt = (th_pkt_t*)data, *opkt; lav_muxer_t *lm = (lav_muxer_t*)m; unsigned char *tofree; @@ -430,9 +453,10 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) tofree = NULL; av_init_packet(&packet); + codec_id = st->codec->codec_id; - if((lm->lm_h264_filter && st->codec->codec_id == AV_CODEC_ID_H264) || - (lm->lm_hevc_filter && st->codec->codec_id == AV_CODEC_ID_HEVC)) { + if((lm->lm_h264_filter && codec_id == AV_CODEC_ID_H264) || + (lm->lm_hevc_filter && codec_id == AV_CODEC_ID_HEVC)) { pkt = avc_convert_pkt(opkt = pkt); pkt_ref_dec(opkt); if(av_bitstream_filter_filter(st->codec->codec_id == AV_CODEC_ID_H264 ? @@ -451,15 +475,21 @@ lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) } else { tofree = packet.data; } - } else if (st->codec->codec_id == AV_CODEC_ID_AAC) { + } else if (codec_id == AV_CODEC_ID_AAC) { /* remove ADTS header */ packet.data = pktbuf_ptr(pkt->pkt_payload) + 7; packet.size = pktbuf_len(pkt->pkt_payload) - 7; } else { + if (lm->m_config.m_type == MC_AVMP4 && + (codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_HEVC)) { + pkt = avc_convert_pkt(opkt = pkt); + pkt_ref_dec(opkt); + } packet.data = pktbuf_ptr(pkt->pkt_payload); packet.size = pktbuf_len(pkt->pkt_payload); } + packet.stream_index = st->index; packet.pts = av_rescale_q(pkt->pkt_pts , mpeg_tc, st->time_base); @@ -581,6 +611,9 @@ lav_muxer_create(const muxer_config_t *m_cfg) 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; diff --git a/src/profile.c b/src/profile.c index 47483c45d..1ef53fe02 100644 --- a/src/profile.c +++ b/src/profile.c @@ -1399,6 +1399,75 @@ profile_libav_matroska_builder(void) return (profile_t *)pro; } +/* + * LibAV/MP4 muxer + */ +typedef struct profile_libav_mp4 { + profile_t; +} profile_libav_mp4_t; + +const idclass_t profile_libav_mp4_class = +{ + .ic_super = &profile_class, + .ic_class = "profile-libav-mp4", + .ic_caption = N_("MP4/av-lib"), +}; + +static int +profile_libav_mp4_reopen(profile_chain_t *prch, + muxer_config_t *m_cfg, int flags) +{ + muxer_config_t c; + + if (m_cfg) + c = *m_cfg; /* do not alter the original parameter */ + else + memset(&c, 0, sizeof(c)); + if (c.m_type != MC_AVMP4) + c.m_type = MC_AVMP4; + + assert(!prch->prch_muxer); + prch->prch_muxer = muxer_create(&c); + return 0; +} + +static int +profile_libav_mp4_open(profile_chain_t *prch, + muxer_config_t *m_cfg, int flags, size_t qsize) +{ + int r; + + prch->prch_flags = SUBSCRIPTION_PACKET; + prch->prch_sq.sq_maxsize = qsize; + + r = profile_htsp_work(prch, &prch->prch_sq.sq_st, 0, 0); + if (r) { + profile_chain_close(prch); + return r; + } + + profile_libav_mp4_reopen(prch, m_cfg, flags); + + return 0; +} + +static muxer_container_type_t +profile_libav_mp4_get_mc(profile_t *_pro) +{ + return MC_AVMP4; +} + +static profile_t * +profile_libav_mp4_builder(void) +{ + profile_libav_mp4_t *pro = calloc(1, sizeof(*pro)); + pro->pro_sflags = SUBSCRIPTION_PACKET; + pro->pro_reopen = profile_libav_mp4_reopen; + pro->pro_open = profile_libav_mp4_open; + pro->pro_get_mc = profile_libav_mp4_get_mc; + return (profile_t *)pro; +} + /* * Transcoding + packet-like muxers */ @@ -1916,6 +1985,7 @@ profile_init(void) #if ENABLE_LIBAV profile_register(&profile_libav_mpegts_class, profile_libav_mpegts_builder); profile_register(&profile_libav_matroska_class, profile_libav_matroska_builder); + profile_register(&profile_libav_mp4_class, profile_libav_mp4_builder); profile_transcode_experimental_codecs = getenv("TVHEADEND_LIBAV_NO_EXPERIMENTAL_CODECS") ? 0 : 1; profile_register(&profile_transcode_class, profile_transcode_builder);