{ "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 },
};
{ "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 },
};
{ "raw", MC_RAW },
{ "avmatroska", MC_AVMATROSKA },
{ "avwebm", MC_AVWEBM },
+ { "avmp4", MC_AVMP4 },
};
{ "bin", MC_RAW },
{ "mka", MC_AVMATROSKA },
{ "webm", MC_AVWEBM },
+ { "mp4", MC_AVMP4 },
};
{ "bin", MC_RAW },
{ "mkv", MC_AVMATROSKA },
{ "webm", MC_AVWEBM },
+ { "mp4", MC_AVMP4 },
};
MC_WEBM = 6,
MC_AVMATROSKA = 7,
MC_AVWEBM = 8,
+ MC_AVMP4 = 9,
} muxer_container_type_t;
typedef enum {
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;
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;
}
int i;
streaming_start_component_t *ssc;
AVFormatContext *oc;
+ AVDictionary *opts = NULL;
lav_muxer_t *lm = (lav_muxer_t*)m;
char app[128];
}
}
+ 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;
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;
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 ?
} 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);
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;
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
*/
#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);