]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
muxer: add experimental avlib/mp4 muxer
authorJaroslav Kysela <perex@perex.cz>
Fri, 8 Jan 2016 19:48:03 +0000 (20:48 +0100)
committerJaroslav Kysela <perex@perex.cz>
Fri, 8 Jan 2016 19:48:03 +0000 (20:48 +0100)
src/muxer.c
src/muxer.h
src/muxer/muxer_libav.c
src/profile.c

index aa6da806fbbce52b508bf672926fffaa4cd29062..af82405096bbbb5630b4004c4da98e32a5d378e0 100644 (file)
@@ -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 },
 };
 
 
index 06abae019ffec4810706d376dd118bc775139e3d..28a62373062bb5dcd666f4bddfc5ffcda18ead75 100644 (file)
@@ -33,6 +33,7 @@ typedef enum {
   MC_WEBM        = 6,
   MC_AVMATROSKA  = 7,
   MC_AVWEBM      = 8,
+  MC_AVMP4       = 9,
 } muxer_container_type_t;
 
 typedef enum {
index f6b972ddbce2f2dbc63c0cb322bbabfc75432b74..82b0c9a550b12ace33425748ee3938770aa11d13 100644 (file)
@@ -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;
index 47483c45dcf2a405c51b9a102903c1943f1d8cd2..1ef53fe0299bb1ea5150a73668d5bef4093ae4d7 100644 (file)
@@ -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);