[FFmpeg-devel] [PATCH] lavf: add textdata virtual demuxer and demuxer
Stefano Sabatini
stefasab at gmail.com
Thu May 19 18:45:22 CEST 2016
This format is useful to inject custom user data into streams.
---
doc/demuxers.texi | 40 +++++++++
doc/muxers.texi | 31 +++++++
libavformat/Makefile | 2 +
libavformat/allformats.c | 1 +
libavformat/fftextdatadec.c | 212 ++++++++++++++++++++++++++++++++++++++++++++
libavformat/fftextdataenc.c | 103 +++++++++++++++++++++
6 files changed, 389 insertions(+)
create mode 100644 libavformat/fftextdatadec.c
create mode 100644 libavformat/fftextdataenc.c
diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index e34f8b3..9fc58eb 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -254,6 +254,46 @@ This demuxer is used to demux FLV files and RTMP network streams.
Allocate the streams according to the onMetaData array content.
@end table
+ at anchor{fftextdata}
+ at section fftextdata, fftd
+
+FFmpeg text data demuxer.
+
+This special demuxer allows to read serialized data base64-encoded and
+remux it. It is especially useful for injecting opaque data streams.
+
+The fftextdata bytestream consists of a sequence of packets. Each
+packet starts with a timestamps expressed in a format recognized by
+FFmpeg (see
+ at ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils})
+followed by a sequence of spaces and the base64 encoded data for the
+packet, terminated by ";". The data representation may contain
+interleaved space characters (a space, a tab, or a newline) which are
+ignored.
+
+At the moment a single stream can be represented by an fftextdata
+bytestream.
+
+If an input filename is "fftextdata" or "fftd" then the file format is
+recognized as fftextdata.
+
+ at subsection Options
+ at table @option
+ at item codec_name
+Set the codec name for the packets data.
+ at end table
+
+ at subsection Examples
+
+ at itemize
+ at item
+Inject timed_id3 packed data stored into the data.fftd file into the
+output file.
+ at example
+ffmpeg -i input.mp4 -codec_name timed_id3 -f fftextdata -i data.fftd -y -map 0 -map 1 -c copy output.ts
+ at end example
+ at end itemize
+
@section libgme
The Game Music Emu library is a collection of video game music file emulators.
diff --git a/doc/muxers.texi b/doc/muxers.texi
index c62d4b5..df5ec08 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -129,6 +129,37 @@ and the input video converted to MPEG-2 video, use the command:
ffmpeg -i INPUT -c:a pcm_u8 -c:v mpeg2video -f crc -
@end example
+ at section fftextdata, fftd
+
+FFmpeg text data muxer.
+
+The fftextdata bytestream consists of a sequence of packets. Each
+packet starts with a timestamps expressed in a format recognized by
+FFmpeg (see
+ at ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils})
+followed by a sequence of spaces and the base64 encoded data for the
+packet, terminated by ";". The data representation may contain
+interleaved space characters (a space, a tab, or a newline) which are
+ignored.
+
+At the moment only a single stream can be represented by an fftextdata
+bytestream.
+
+This muxer can be used to reinject the stream (e.g. a data stream) in
+a different output, or to provide serialized data of the encoded data.
+
+The output can then be read using the fftextdata demuxer.
+
+ at subsection Examples
+
+ at itemize
+ at item
+Store a data stream to an output file:
+ at example
+ffmpeg -i INPUT -codec copy -map 0 -an -vn data.fftd
+ at end example
+ at end itemize
+
@anchor{framecrc}
@section framecrc
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 742aff5..4effccd 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -162,6 +162,8 @@ OBJS-$(CONFIG_FFM_DEMUXER) += ffmdec.o
OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o
OBJS-$(CONFIG_FFMETADATA_DEMUXER) += ffmetadec.o
OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o
+OBJS-$(CONFIG_FFTEXTDATA_DEMUXER) += fftextdatadec.o
+OBJS-$(CONFIG_FFTEXTDATA_MUXER) += fftextdataenc.o
OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o
OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o
OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index e6ee8d6..7657f94 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -124,6 +124,7 @@ void av_register_all(void)
REGISTER_MUXER (F4V, f4v);
REGISTER_MUXDEMUX(FFM, ffm);
REGISTER_MUXDEMUX(FFMETADATA, ffmetadata);
+ REGISTER_MUXDEMUX(FFTEXTDATA, fftextdata);
REGISTER_MUXDEMUX(FILMSTRIP, filmstrip);
REGISTER_MUXDEMUX(FLAC, flac);
REGISTER_DEMUXER (FLIC, flic);
diff --git a/libavformat/fftextdatadec.c b/libavformat/fftextdatadec.c
new file mode 100644
index 0000000..9516559
--- /dev/null
+++ b/libavformat/fftextdatadec.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2016 Stefano Sabatini
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * timestamped data virtual demuxer
+ */
+
+#include "libavutil/base64.h"
+#include "libavutil/bprint.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct {
+ const AVClass *class; /**< Class for private options. */
+ int nb_packets;
+ AVBPrint bp;
+ const char *codec_name;
+} FFTextdataContext;
+
+av_cold static int fftextdata_read_close(AVFormatContext *avctx)
+{
+ FFTextdataContext *td = avctx->priv_data;
+
+ av_bprint_finalize(&td->bp, NULL);
+ return 0;
+}
+
+av_cold static int fftextdata_read_header(AVFormatContext *s)
+{
+ FFTextdataContext *td = s->priv_data;
+ AVStream *st;
+ const AVCodecDescriptor *cd;
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ cd = avcodec_descriptor_get_by_name(td->codec_name);
+ if (!cd) {
+ av_log(s, AV_LOG_ERROR, "Impossible to find a codec with name '%s'\n",
+ td->codec_name);
+ return AVERROR(EINVAL);
+ }
+
+ st->codecpar->codec_type = cd->type;
+ st->codecpar->codec_id = cd->id;
+ avpriv_set_pts_info(st, 64, 1, 1000000);
+
+ av_bprint_init(&(td->bp), 0, 1);
+ td->nb_packets = 0;
+
+ return 0;
+}
+
+static inline int is_space(char c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static int read_word(AVIOContext *avio, AVBPrint *bp)
+{
+ int c;
+
+ av_bprint_clear(bp);
+
+ /* skip spaces */
+ do {
+ c = avio_r8(avio);
+ if (!c)
+ goto end;
+ } while (is_space(c));
+
+ /* read word */
+ av_bprint_chars(bp, c, 1);
+ do {
+ c = avio_r8(avio);
+ if (!c)
+ goto end;
+ if (is_space(c)) {
+ avio_skip(avio, -1);
+ goto end;
+ }
+ av_bprint_chars(bp, c, 1);
+ } while (1);
+
+end:
+ return bp->len;
+}
+
+static int read_data(AVIOContext *avio, AVBPrint *bp)
+{
+ int c;
+
+ av_bprint_clear(bp);
+
+ /* skip spaces */
+ do {
+ c = avio_r8(avio);
+ if (!c)
+ goto end;
+ } while (is_space(c));
+
+ /* read data chunk */
+ av_bprint_chars(bp, c, 1);
+ do {
+ c = avio_r8(avio);
+ if (!c || c == ';')
+ goto end;
+ if (is_space(c)) {
+ continue;
+ }
+ av_bprint_chars(bp, c, 1);
+ } while (1);
+
+end:
+ return bp->len;
+}
+
+static int fftextdata_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ FFTextdataContext *td = s->priv_data;
+ AVIOContext *avio = s->pb;
+ int ret;
+ AVBPrint *bp = &(td->bp);
+
+ pkt->pos = avio_tell(avio);
+
+ /* read PTS */
+ ret = read_word(avio, bp);
+ if (ret == 0)
+ return AVERROR_EOF;
+
+ ret = av_parse_time(&pkt->pts, bp->str, 1);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR, "Invalid time specification '%s' for data packet #%d\n",
+ bp->str, td->nb_packets);
+ return ret;
+ }
+
+ ret = read_data(avio, bp);
+ if (ret == 0) {
+ av_log(s, AV_LOG_WARNING, "Incomplete packet #%d with no data at the end of the data stream\n",
+ td->nb_packets);
+ return AVERROR_EOF;
+ }
+
+ pkt->size = AV_BASE64_DECODE_SIZE(ret);
+ pkt->data = av_malloc(pkt->size);
+ if (ret < 0)
+ return ret;
+
+ ret = av_base64_decode(pkt->data, bp->str, pkt->size);
+ if (ret < 0) {
+ av_freep(&pkt->data);
+ return ret;
+ }
+
+ pkt->size = ret;
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ td->nb_packets++;
+
+ return ret;
+}
+
+#define OFFSET(x) offsetof(FFTextdataContext, x)
+
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+#define OFFSET(x) offsetof(FFTextdataContext, x)
+
+static const AVOption options[] = {
+ { "codec_name", "set output codec name", OFFSET(codec_name), AV_OPT_TYPE_STRING, {.str = "bin_data"}, CHAR_MIN, CHAR_MAX, D },
+ { NULL },
+};
+
+static const AVClass fftextdata_class = {
+ .class_name = "fftexdata demuxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_fftextdata_demuxer = {
+ .name = "fftextdata",
+ .long_name = NULL_IF_CONFIG_SMALL("Timestamped data virtual demuxer"),
+ .extensions = "fftextdata,fftd",
+ .priv_data_size = sizeof(FFTextdataContext),
+ .read_header = fftextdata_read_header,
+ .read_packet = fftextdata_read_packet,
+ .read_close = fftextdata_read_close,
+ .priv_class = &fftextdata_class,
+};
diff --git a/libavformat/fftextdataenc.c b/libavformat/fftextdataenc.c
new file mode 100644
index 0000000..6029ab0
--- /dev/null
+++ b/libavformat/fftextdataenc.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Stefano Sabatini
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * timestamped data virtual muxer
+ */
+
+#include "avformat.h"
+#include "libavutil/base64.h"
+
+typedef struct {
+ uint8_t *buf;
+ size_t buf_size;
+} FFTextdataContext;
+
+static int fftextdata_write_header(AVFormatContext *s)
+{
+ FFTextdataContext *td = s->priv_data;
+
+ td->buf = NULL;
+ td->buf_size = 0;
+
+ return 0;
+}
+
+static int fftextdata_write_trailer(AVFormatContext *s)
+{
+ FFTextdataContext *td = s->priv_data;
+
+ av_freep(&td->buf);
+ td->buf_size = 0;
+
+ return 0;
+}
+
+static int fftextdata_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ FFTextdataContext *td = s->priv_data;
+ char ts[32];
+ size_t encoded_data_size;
+ AVStream *st = s->streams[pkt->stream_index];
+ int64_t pts = pkt->pts;
+ double secs;
+ int hours, mins;
+
+ if (st->start_time != AV_NOPTS_VALUE)
+ pts += st->start_time;
+
+ secs = (double)pkt->pts * av_q2d(st->time_base);
+ mins = (int)secs / 60;
+ secs = secs - mins * 60;
+ hours = mins / 60;
+ mins %= 60;
+ snprintf(ts, sizeof(ts), "%d:%02d:%09.6f", hours, mins, secs);
+ avio_put_str(s->pb, ts);
+ avio_skip(s->pb, -1);
+ avio_w8(s->pb, '\n');
+
+ encoded_data_size = AV_BASE64_SIZE(pkt->size);
+ if (encoded_data_size > td->buf_size) {
+ td->buf = av_realloc_f(td->buf, encoded_data_size, 1);
+ if (!td->buf)
+ return AVERROR(ENOMEM);
+ td->buf_size = encoded_data_size;
+ }
+
+ av_base64_encode(td->buf, td->buf_size, pkt->data, pkt->size);
+ avio_put_str(s->pb, td->buf);
+ avio_skip(s->pb, -1);
+
+ avio_put_str(s->pb, "\n;\n");
+ avio_skip(s->pb, -1);
+
+ return 0;
+}
+
+AVOutputFormat ff_fftextdata_muxer = {
+ .name = "fftextdata",
+ .long_name = NULL_IF_CONFIG_SMALL("Timestamped data virtual muxer"),
+ .extensions = "fftextdata,fftd",
+ .priv_data_size = sizeof(FFTextdataContext),
+ .write_header = fftextdata_write_header,
+ .write_packet = fftextdata_write_packet,
+ .write_trailer = fftextdata_write_trailer,
+};
--
1.9.1
More information about the ffmpeg-devel
mailing list