411 lines
7.4 KiB
C
411 lines
7.4 KiB
C
/*
|
|
* tvheadend, generic muxing utils
|
|
* Copyright (C) 2012 John Törnblom
|
|
*
|
|
* 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 <string.h>
|
|
|
|
#include "tvheadend.h"
|
|
#include "service.h"
|
|
#include "muxer.h"
|
|
#include "muxer/muxer_tvh.h"
|
|
#include "muxer/muxer_pass.h"
|
|
#if CONFIG_LIBAV
|
|
#include "muxer/muxer_libav.h"
|
|
#endif
|
|
|
|
/**
|
|
* Mime type for containers containing only audio
|
|
*/
|
|
static struct strtab container_audio_mime[] = {
|
|
{ "application/octet-stream", MC_UNKNOWN },
|
|
{ "audio/x-matroska", MC_MATROSKA },
|
|
{ "audio/webm", MC_WEBM },
|
|
{ "audio/x-mpegts", MC_MPEGTS },
|
|
{ "audio/mpeg", MC_MPEGPS },
|
|
{ "application/octet-stream", MC_PASS },
|
|
{ "application/octet-stream", MC_RAW },
|
|
};
|
|
|
|
|
|
/**
|
|
* Mime type for containers
|
|
*/
|
|
static struct strtab container_video_mime[] = {
|
|
{ "application/octet-stream", MC_UNKNOWN },
|
|
{ "video/x-matroska", MC_MATROSKA },
|
|
{ "video/webm", MC_WEBM },
|
|
{ "video/x-mpegts", MC_MPEGTS },
|
|
{ "video/mpeg", MC_MPEGPS },
|
|
{ "application/octet-stream", MC_PASS },
|
|
{ "application/octet-stream", MC_RAW },
|
|
};
|
|
|
|
|
|
/**
|
|
* Name of the container
|
|
*/
|
|
static struct strtab container_name[] = {
|
|
{ "unknown", MC_UNKNOWN },
|
|
{ "matroska", MC_MATROSKA },
|
|
{ "webm", MC_WEBM },
|
|
{ "mpegts", MC_MPEGTS },
|
|
{ "mpegps", MC_MPEGPS },
|
|
{ "pass", MC_PASS },
|
|
{ "raw", MC_RAW },
|
|
};
|
|
|
|
|
|
/**
|
|
* filename suffix of audio-only streams
|
|
*/
|
|
static struct strtab container_audio_file_suffix[] = {
|
|
{ "bin", MC_UNKNOWN },
|
|
{ "mka", MC_MATROSKA },
|
|
{ "webm", MC_WEBM },
|
|
{ "ts", MC_MPEGTS },
|
|
{ "mpeg", MC_MPEGPS },
|
|
{ "bin", MC_PASS },
|
|
{ "bin", MC_RAW },
|
|
};
|
|
|
|
|
|
/**
|
|
* filename suffix of video streams
|
|
*/
|
|
static struct strtab container_video_file_suffix[] = {
|
|
{ "bin", MC_UNKNOWN },
|
|
{ "mkv", MC_MATROSKA },
|
|
{ "webm", MC_WEBM },
|
|
{ "ts", MC_MPEGTS },
|
|
{ "mpeg", MC_MPEGPS },
|
|
{ "bin", MC_PASS },
|
|
{ "bin", MC_RAW },
|
|
};
|
|
|
|
|
|
/**
|
|
* Get the mime type for a container
|
|
*/
|
|
const char*
|
|
muxer_container_type2mime(muxer_container_type_t mc, int video)
|
|
{
|
|
const char *str;
|
|
|
|
if(video)
|
|
str = val2str(mc, container_video_mime);
|
|
else
|
|
str = val2str(mc, container_audio_mime);
|
|
|
|
if(!str)
|
|
str = val2str(MC_UNKNOWN, container_video_mime);
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the suffix used in file names
|
|
*/
|
|
const char*
|
|
muxer_container_suffix(muxer_container_type_t mc, int video)
|
|
{
|
|
const char *str;
|
|
if(video)
|
|
str = val2str(mc, container_video_file_suffix);
|
|
else
|
|
str = val2str(mc, container_audio_file_suffix);
|
|
|
|
if(!str)
|
|
str = val2str(MC_UNKNOWN, container_video_file_suffix);
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a container type to a string
|
|
*/
|
|
const char*
|
|
muxer_container_type2txt(muxer_container_type_t mc)
|
|
{
|
|
const char *str;
|
|
|
|
str = val2str(mc, container_name);
|
|
if(!str)
|
|
return "unknown";
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a container name to a container type
|
|
*/
|
|
muxer_container_type_t
|
|
muxer_container_txt2type(const char *str)
|
|
{
|
|
muxer_container_type_t mc;
|
|
|
|
if(!str)
|
|
return MC_UNKNOWN;
|
|
|
|
mc = str2val(str, container_name);
|
|
if(mc == -1)
|
|
return MC_UNKNOWN;
|
|
|
|
return mc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a mime-string to a container type
|
|
*/
|
|
muxer_container_type_t
|
|
muxer_container_mime2type(const char *str)
|
|
{
|
|
muxer_container_type_t mc;
|
|
|
|
if(!str)
|
|
return MC_UNKNOWN;
|
|
|
|
mc = str2val(str, container_video_mime);
|
|
if(mc == -1)
|
|
mc = str2val(str, container_audio_mime);
|
|
|
|
if(mc == -1)
|
|
return MC_UNKNOWN;
|
|
|
|
return mc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new muxer
|
|
*/
|
|
muxer_t*
|
|
muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
|
|
{
|
|
muxer_t *m;
|
|
|
|
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 m;
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_mime()
|
|
*/
|
|
const char*
|
|
muxer_mime(muxer_t *m, const struct streaming_start *ss)
|
|
{
|
|
if(!m || !ss)
|
|
return NULL;
|
|
|
|
return m->m_mime(m, ss);
|
|
}
|
|
|
|
|
|
/**
|
|
* Figure out the file suffix by looking at the mime type
|
|
*/
|
|
const char*
|
|
muxer_suffix(muxer_t *m, const struct streaming_start *ss)
|
|
{
|
|
const char *mime;
|
|
muxer_container_type_t mc;
|
|
int video;
|
|
|
|
if(!m || !ss)
|
|
return NULL;
|
|
|
|
mime = m->m_mime(m, ss);
|
|
video = memcmp("audio", mime, 5);
|
|
mc = muxer_container_mime2type(mime);
|
|
|
|
return muxer_container_suffix(mc, video);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_init()
|
|
*/
|
|
int
|
|
muxer_init(muxer_t *m, const struct streaming_start *ss, const char *name)
|
|
{
|
|
if(!m || !ss)
|
|
return -1;
|
|
|
|
return m->m_init(m, ss, name);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_reconfigure()
|
|
*/
|
|
int
|
|
muxer_reconfigure(muxer_t *m, const struct streaming_start *ss)
|
|
{
|
|
if(!m || !ss)
|
|
return -1;
|
|
|
|
return m->m_reconfigure(m, ss);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_add_marker()
|
|
*/
|
|
int
|
|
muxer_add_marker(muxer_t *m)
|
|
{
|
|
if(!m)
|
|
return -1;
|
|
|
|
return m->m_add_marker(m);
|
|
}
|
|
|
|
/**
|
|
* sanity wrapper arround m_open_file()
|
|
*/
|
|
int
|
|
muxer_open_file(muxer_t *m, const char *filename)
|
|
{
|
|
if(!m || !filename)
|
|
return -1;
|
|
|
|
return m->m_open_file(m, filename);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_open_stream()
|
|
*/
|
|
int
|
|
muxer_open_stream(muxer_t *m, int fd)
|
|
{
|
|
if(!m || fd < 0)
|
|
return -1;
|
|
|
|
return m->m_open_stream(m, fd);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_close()
|
|
*/
|
|
int
|
|
muxer_close(muxer_t *m)
|
|
{
|
|
if(!m)
|
|
return -1;
|
|
|
|
return m->m_close(m);
|
|
}
|
|
|
|
/**
|
|
* sanity wrapper arround m_destroy()
|
|
*/
|
|
int
|
|
muxer_destroy(muxer_t *m)
|
|
{
|
|
if(!m)
|
|
return -1;
|
|
|
|
m->m_destroy(m);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapppper arround m_write_meta()
|
|
*/
|
|
int
|
|
muxer_write_meta(muxer_t *m, struct epg_broadcast *eb)
|
|
{
|
|
if(!m || !eb)
|
|
return -1;
|
|
|
|
return m->m_write_meta(m, eb);
|
|
}
|
|
|
|
|
|
/**
|
|
* sanity wrapper arround m_write_pkt()
|
|
*/
|
|
int
|
|
muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data)
|
|
{
|
|
if(!m || !data)
|
|
return -1;
|
|
|
|
return m->m_write_pkt(m, smt, data);
|
|
}
|
|
|
|
|