]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Re-add Audioes muxer
authorHypfer <soeren@soerenbeye.de>
Thu, 25 Aug 2016 19:29:53 +0000 (21:29 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 6 Oct 2016 08:31:17 +0000 (10:31 +0200)
src/muxer.c
src/muxer.h
src/muxer/muxer_audioes.c [new file with mode: 0644]
src/muxer/muxer_audioes.h [new file with mode: 0644]
src/profile.c
src/tvhlog.c
src/tvhlog.h

index c6bb6ef2e89f05334895d5282c81f42591b39562..c68e82b431214fefcdf406b8c4946da819ad5d61 100644 (file)
@@ -24,6 +24,7 @@
 #include "muxer.h"
 #include "muxer/muxer_mkv.h"
 #include "muxer/muxer_pass.h"
+#include "muxer/muxer_audioes.h"
 #if CONFIG_LIBAV
 #include "muxer/muxer_libav.h"
 #endif
@@ -48,6 +49,7 @@ static struct strtab container_audio_mime[] = {
   { "audio/mp4",                MC_AVMP4 },
   { "application/octet-stream", MC_PASS },
   { "application/octet-stream", MC_RAW },
+  { "audio/mpeg",               MC_AUDIOES },
 };
 
 
@@ -82,6 +84,7 @@ static struct strtab container_name[] = {
   { "avmatroska", MC_AVMATROSKA },
   { "avwebm",     MC_AVWEBM },
   { "avmp4",      MC_AVMP4 },
+  { "audioes",  MC_AUDIOES },
 };
 
 
@@ -99,6 +102,7 @@ static struct strtab container_audio_file_suffix[] = {
   { "mka",  MC_AVMATROSKA },
   { "webm", MC_AVWEBM },
   { "mp4",  MC_AVMP4 },
+  { "mp2",  MC_AUDIOES }, /* Or maybe ac3 or adts */
 };
 
 
@@ -116,6 +120,7 @@ static struct strtab container_video_file_suffix[] = {
   { "mkv",  MC_AVMATROSKA },
   { "webm", MC_AVWEBM },
   { "mp4",  MC_AVMP4 },
+  { NULL,   MC_AUDIOES },
 };
 
 
@@ -255,6 +260,9 @@ muxer_create(const muxer_config_t *m_cfg)
   if(!m)
     m = mkv_muxer_create(m_cfg);
 
+  if(!m)
+       m = audioes_muxer_create(m_cfg);
+
 #if CONFIG_LIBAV
   if(!m)
     m = lav_muxer_create(m_cfg);
index d0bedff9fd772e07e582933513fbb672856b3348..b78839539f335035c03f42ca15df756d9c91f7a4 100644 (file)
@@ -34,6 +34,7 @@ typedef enum {
   MC_AVMATROSKA  = 7,
   MC_AVWEBM      = 8,
   MC_AVMP4       = 9,
+  MC_AUDIOES     = 10
 } muxer_container_type_t;
 
 typedef enum {
diff --git a/src/muxer/muxer_audioes.c b/src/muxer/muxer_audioes.c
new file mode 100644 (file)
index 0000000..ebc9054
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ *  "muxer" to write mpeg audio elementary streams
+ *  Copyright (C) 2013 Dave Chapman
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <htmlui://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "tvheadend.h"
+#include "streaming.h"
+#include "epg.h"
+#include "channels.h"
+#include "muxer_audioes.h"
+
+typedef struct audioes_muxer {
+  muxer_t;
+
+  int error;
+
+  /* Info about the service */
+  int   am_index;
+
+  /* File descriptor stuff */
+  int   am_fd;
+  int   am_seekable;
+  int   am_error;
+
+  /* Filename is also used for logging */
+  char *am_filename;
+} audioes_muxer_t;
+
+
+/**
+ * Figure out the mimetype
+ */
+static const char*
+audioes_muxer_mime(muxer_t* m, const struct streaming_start *ss)
+{
+  int i;
+  int has_audio;
+  const streaming_start_component_t *ssc;
+  
+  has_audio = 0;
+
+  for(i=0; i < ss->ss_num_components; i++) {
+    ssc = &ss->ss_components[i];
+
+    if(ssc->ssc_disabled)
+      continue;
+
+    has_audio |= SCT_ISAUDIO(ssc->ssc_type);
+  }
+
+  if(has_audio)
+    return muxer_container_type2mime(MC_AUDIOES, 0);
+  else
+    return muxer_container_type2mime(MC_UNKNOWN, 0);
+}
+
+
+/**
+ * Init the builtin mkv muxer with streams
+ */
+static int
+audioes_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name)
+{
+  audioes_muxer_t *am = (audioes_muxer_t*)m;
+  const streaming_start_component_t *ssc;
+  int i;
+
+  am->am_index = -1;
+
+  for(i = 0; i < ss->ss_num_components;i++) {
+    ssc = &ss->ss_components[i];
+
+    if ((!ssc->ssc_disabled) && (SCT_ISAUDIO(ssc->ssc_type))) {
+      am->am_index = ssc->ssc_index;
+      break;
+    }
+  }
+
+  return 0;
+}
+
+
+static int
+audioes_muxer_reconfigure(muxer_t* m, const struct streaming_start *ss)
+{
+  /* TODO: Check our stream still exists? */
+
+  return 0;
+}
+
+
+/*
+ * Open the muxer as a stream muxer (using a non-seekable socket)
+ */
+static int
+audioes_muxer_open_stream(muxer_t *m, int fd)
+{
+  audioes_muxer_t *am = (audioes_muxer_t*)m;
+
+  am->am_fd       = fd;
+  am->am_seekable = 0;
+  am->am_filename = strdup("Live stream");
+
+  return 0;
+}
+
+
+/**
+ * Open the file and set the file descriptor
+ */
+static int
+audioes_muxer_open_file(muxer_t *m, const char *filename)
+{
+  int fd;
+  audioes_muxer_t *am = (audioes_muxer_t*)m;
+
+  tvhtrace(LS_AUDIOES, "Creating file \"%s\" with file permissions \"%o\"", filename, am->m_config.m_file_permissions);
+  fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, am->m_config.m_file_permissions);
+
+  if(fd < 0) {
+    am->am_error = errno;
+    tvherror(LS_AUDIOES, "%s: Unable to create file, open failed -- %s",
+            filename, strerror(errno));
+    am->m_errors++;
+    return -1;
+  }
+
+  /* bypass umask settings */
+  if (fchmod(fd, am->m_config.m_file_permissions))
+    tvherror(LS_AUDIOES, "%s: Unable to change permissions -- %s",
+             filename, strerror(errno));
+
+  am->am_seekable = 1;
+  am->am_fd       = fd;
+  am->am_filename = strdup(filename);
+  return 0;
+}
+
+/**
+ * Write a packet to the muxer
+ */
+static int
+audioes_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data)
+{
+  th_pkt_t *pkt = (th_pkt_t*)data;
+  audioes_muxer_t *am = (audioes_muxer_t*)m;
+
+  assert(smt == SMT_PACKET);
+
+  // TODO: pkt->pkt_componentindex
+  /* TODO: ^ What does this even mean? */      
+
+  if(pkt->pkt_componentindex != am->am_index) {
+    pkt_ref_dec(pkt);
+    return am->error;
+  }
+
+  if(am->am_error) {
+    am->m_errors++;
+  } else if(tvh_write(am->am_fd, pktbuf_ptr(pkt->pkt_payload), pktbuf_len(pkt->pkt_payload))) {
+    am->am_error = errno;
+    tvherror(LS_AUDIOES, "%s: Write failed -- %s", am->am_filename, 
+           strerror(errno));
+    /* TODO: Do some EOS handling here. Whatever that is. See muxer_pass.c:415 */
+    am->m_errors++;
+       /* TODO: A muxer_cache_update() call is still missing here. */
+  }
+
+  pkt_ref_dec(pkt);
+  return am->error;
+}
+
+
+/**
+ * NOP
+ */
+static int
+audioes_muxer_write_meta(muxer_t *m, struct epg_broadcast *eb, const char *comment)
+{
+  return 0;
+}
+
+
+
+/**
+ * Close the muxer
+ */
+static int
+audioes_muxer_close(muxer_t *m)
+{
+  audioes_muxer_t *am = (audioes_muxer_t*)m;
+
+  if ((am->am_seekable) && (close(am->am_fd))) {
+    am->am_error = errno;
+    tvherror(LS_AUDIOES, "%s: Unable to close file -- %s",
+           am->am_filename, strerror(errno));
+    am->m_errors++;
+    return -1;
+  }
+
+  return 0;
+}
+
+
+/**
+ * Free all memory associated with the muxer
+ */
+static void
+audioes_muxer_destroy(muxer_t *m)
+{
+       audioes_muxer_t *am = (audioes_muxer_t*)m;
+
+       if(am->am_filename)
+               free(am->am_filename);
+       
+       free(am);       
+}
+
+
+/**
+ * Create a new builtin muxer
+ */
+muxer_t*
+audioes_muxer_create(const muxer_config_t *m_cfg)
+{
+  audioes_muxer_t *am;
+
+  if(m_cfg->m_type != MC_AUDIOES)
+    return NULL;
+
+  am = calloc(1, sizeof(audioes_muxer_t));
+  am->m_open_stream  = audioes_muxer_open_stream;
+  am->m_open_file    = audioes_muxer_open_file;
+  am->m_mime         = audioes_muxer_mime;
+  am->m_init         = audioes_muxer_init;
+  am->m_reconfigure  = audioes_muxer_reconfigure;
+  am->m_write_meta   = audioes_muxer_write_meta;
+  am->m_write_pkt    = audioes_muxer_write_pkt;
+  am->m_close        = audioes_muxer_close;
+  am->m_destroy      = audioes_muxer_destroy;
+  //am->m_container    = *m_cfg; //WTF DOES THIS DO?!
+
+  return (muxer_t*)am;
+}
+
diff --git a/src/muxer/muxer_audioes.h b/src/muxer/muxer_audioes.h
new file mode 100644 (file)
index 0000000..1e4d3c0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *  "muxer" to write raw audio streams
+ *  Copyright (C) 2013 Dave Chapman
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <htmlui://www.gnu.org/licenses/>.
+ */
+
+#ifndef AUDIOES_MUXER_H_
+#define AUDIOES_MUXER_H_
+
+#include "muxer.h"
+
+muxer_t* audioes_muxer_create (const muxer_config_t* m_cfg);
+
+#endif
index 3aa1a4c16ecc6a027baf3b26ed071d60a24ceb69..fcee69fe1165ae1a4b6e52f916443763cc7e1be9 100644 (file)
@@ -1294,6 +1294,83 @@ profile_matroska_builder(void)
   return (profile_t *)pro;
 }
 
+
+/*
+ *  Audioes Muxer
+ */
+typedef struct profile_audioes {
+  profile_t;
+} profile_audioes_t;
+
+const idclass_t profile_audioes_class =
+{
+  .ic_super      = &profile_class,
+  .ic_class      = "profile-audioes",
+  .ic_caption    = N_("Audioes"),
+  .ic_properties = (const property_t[]){
+    { }
+  }
+};
+
+
+static int
+profile_audioes_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));
+  c.m_type = MC_AUDIOES;
+
+  assert(!prch->prch_muxer);
+  prch->prch_muxer = muxer_create(&c);
+  return 0;
+}
+
+static int
+profile_audioes_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_audioes_reopen(prch, m_cfg, flags);
+  return 0;
+}
+
+static muxer_container_type_t
+profile_audioes_get_mc(profile_t *_pro)
+{
+  return MC_AUDIOES;
+}
+
+static profile_t *
+profile_audioes_builder(void)
+{
+  profile_audioes_t *pro = calloc(1, sizeof(*pro));
+  pro->pro_sflags = SUBSCRIPTION_PACKET;
+  pro->pro_reopen = profile_audioes_reopen;
+  pro->pro_open   = profile_audioes_open;
+  pro->pro_get_mc = profile_audioes_get_mc;
+  return (profile_t *)pro;
+}
+
+
+
+
+
+
 #if ENABLE_LIBAV
 
 /*
@@ -1564,6 +1641,7 @@ profile_class_mc_list ( void *o, const char *lang )
     { N_("WEBM/built-in"),                MC_WEBM, },
     { N_("MPEG-TS/av-lib"),               MC_MPEGTS },
     { N_("MPEG-PS (DVD)/av-lib"),         MC_MPEGPS },
+       { N_("Audioes"),                                  MC_AUDIOES },
     { N_("Matroska (mkv)/av-lib"),        MC_AVMATROSKA },
     { N_("WEBM/av-lib"),                  MC_AVWEBM },
   };
@@ -1959,6 +2037,7 @@ profile_transcode_mc_valid(int mc)
   case MC_WEBM:
   case MC_MPEGTS:
   case MC_MPEGPS:
+  case MC_AUDIOES:
   case MC_AVMATROSKA:
     return 1;
   default:
@@ -2057,6 +2136,7 @@ profile_init(void)
   profile_register(&profile_mpegts_pass_class, profile_mpegts_pass_builder);
   profile_register(&profile_matroska_class, profile_matroska_builder);
   profile_register(&profile_htsp_class, profile_htsp_builder);
+  profile_register(&profile_audioes_class, profile_audioes_builder);
 #if ENABLE_LIBAV
   profile_register(&profile_libav_mpegts_class, profile_libav_mpegts_builder);
   profile_register(&profile_libav_matroska_class, profile_libav_matroska_builder);
@@ -2128,6 +2208,21 @@ profile_init(void)
     htsmsg_destroy(conf);
   }
 
+  name = "audioes";
+  pro = profile_find_by_name2(name, NULL, 1);
+  if (pro == NULL || strcmp(profile_get_name(pro), name)) {
+    htsmsg_t *conf;
+
+    conf = htsmsg_create_map();
+    htsmsg_add_str (conf, "class", "profile-audioes");
+    htsmsg_add_bool(conf, "enabled", 1);
+    htsmsg_add_str (conf, "name", name);
+    htsmsg_add_str (conf, "comment", _("Audio-only MPEG elementary stream"));
+    htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
+    (void)profile_create(NULL, conf, 1);
+    htsmsg_destroy(conf);
+  }
+
 #if ENABLE_LIBAV
 
   name = "webtv-vp8-vorbis-webm";
index 24f0e8eec37e6ac005f8095e4dfd19f68cec755a..a3fab4c845135e1066d065150e7cf8997a99341d 100644 (file)
@@ -122,6 +122,7 @@ tvhlog_subsys_t tvhlog_subsystems[] = {
   [LS_HEVC]          = { "hevc",          N_("HEVC - H.265") },
   [LS_MUXER]         = { "muxer",         N_("Muxer") },
   [LS_PASS]          = { "pass",          N_("Pass-thru muxer") },
+  [LS_AUDIOES]       = { "audioes",       N_("Audioes muxer") },
   [LS_MKV]           = { "mkv",           N_("Matroska muxer") },
   [LS_SERVICE]       = { "service",       N_("Service") },
   [LS_CHANNEL]       = { "channel",       N_("Channel") },
index d4b277feacef6535030d487e1baf78a9f1b9f176..36ffca84da57f3d1a5161996d3d19a8ca0ba83e9 100644 (file)
@@ -146,6 +146,7 @@ enum {
   LS_HEVC,
   LS_MUXER,
   LS_PASS,
+  LS_AUDIOES,
   LS_MKV,
   LS_SERVICE,
   LS_CHANNEL,