On date Tuesday 2016-08-23 16:53:28 +0200, Nicolas George encoded:
> Le septidi 7 fructidor, an CCXXIV, Stefano Sabatini a écrit :
> > Bump.
> > 
> > So, basically, what are the features that you want to add?
> > I can list a few:
> > 
> > - have multiple streams (with media type and optionally encoding,
> >   assumes base64 by default) in a dedicated header
> > 
> > - specify codec parameters: this could be done serializing
> >   AVCodecParameters. This is the part which would be less robust.
> > 
> > The important point to keep in mind is that this is meant to be an
> > internal format, so it should be used internally (e.g. to mux data)
> > coming from an external source, and we give no guarantee that the
> > format will be robust to changes in libavformat/libavcodec (e.g. in
> > case AVCodecParameter is extended).
> > 
> > My main objection to the ffprobe format is that it's not easily
> > parseable, but I can reconsider it in case there is a strong request
> > for that.
> 

> Sorry for the delay. Here is the patch; as you can see it is two and a half
> years old. IIRC, it used to work with the output of "ffprobe -show_format
> -show_streams -show_packets -show_data", provided the output was edited to
> put the sections in the required order.
> 

> The benefit from using ffprobe instead of a custom format is to not have yet
> another variant of text format.

Sure.

I rebased the patch, and performed two simple changes in ffprobe (see
attachment), and it's almost working.

I think supporting the show_compact_data mode should simplify the
format in case the format is generated programmatically/through
scripting.
>From e0a698f1877c52df13158f3f0744f2e5430c75f3 Mon Sep 17 00:00:00 2001
From: Nicolas George <geo...@nsup.org>
Date: Sat, 11 Jan 2014 19:42:41 +0100
Subject: [PATCH] lavf: add ffprobe demuxer

Signed-off-by: Nicolas George <geo...@nsup.org>
---
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   2 +
 libavformat/ffprobedec.c | 341 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 libavformat/ffprobedec.c

diff --git a/libavformat/Makefile b/libavformat/Makefile
index b68c27c..d222ac2 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -162,6 +162,7 @@ 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_FFPROBE_DEFAULT_DEMUXER)   += ffprobedec.o
 OBJS-$(CONFIG_FFTEXTDATA_DEMUXER)        += fftextdatadec.o
 OBJS-$(CONFIG_FFTEXTDATA_MUXER)          += fftextdataenc.o
 OBJS-$(CONFIG_FIFO_MUXER)                += fifo.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index e58e41d..2e10e26 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -123,8 +123,10 @@ void av_register_all(void)
     REGISTER_MUXER   (F4V,              f4v);
     REGISTER_MUXDEMUX(FFM,              ffm);
     REGISTER_MUXDEMUX(FFMETADATA,       ffmetadata);
+    REGISTER_DEMUXER (FFPROBE_DEFAULT,  ffprobe_default);
     REGISTER_MUXDEMUX(FFTEXTDATA,       fftextdata);
     REGISTER_MUXER   (FIFO,             fifo);
+    REGISTER_MUXDEMUX(FFTEXTDATA,       fftextdata);
     REGISTER_MUXDEMUX(FILMSTRIP,        filmstrip);
     REGISTER_MUXDEMUX(FLAC,             flac);
     REGISTER_DEMUXER (FLIC,             flic);
diff --git a/libavformat/ffprobedec.c b/libavformat/ffprobedec.c
new file mode 100644
index 0000000..28e4e66
--- /dev/null
+++ b/libavformat/ffprobedec.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2013 Nicolas George
+ *
+ * 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
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "avformat.h"
+#include "internal.h"
+
+enum SectionType {
+    SEC_NONE = 0,
+    SEC_FORMAT,
+    SEC_STREAM,
+    SEC_PACKET,
+};
+
+const char *const section_names[] = {
+    [SEC_NONE]   = "NONE",
+    [SEC_FORMAT] = "FORMAT",
+    [SEC_STREAM] = "STREAM",
+    [SEC_PACKET] = "PACKET",
+};
+
+typedef struct {
+    AVClass *class;
+    enum SectionType section;
+    AVBPrint data;
+} FFprobeContext;
+
+static int ffprobe_probe(AVProbeData *probe)
+{
+    unsigned score;
+
+    if (!av_strstart(probe->buf, "[FORMAT]\n", NULL))
+        return 0;
+    score = !!strstr(probe->buf, "\nnb_streams=") +
+            !!strstr(probe->buf, "\nnb_programs=") +
+            !!strstr(probe->buf, "\nformat_name=") +
+            !!strstr(probe->buf, "\nstart_time=") +
+            !!strstr(probe->buf, "\nsize=");
+    return score >= 3 ? AVPROBE_SCORE_MAX : AVPROBE_SCORE_MAX / 2;
+}
+
+static int ffprobe_read_close(AVFormatContext *avf)
+{
+    FFprobeContext *ffp = avf->priv_data;
+
+    av_bprint_finalize(&ffp->data, NULL);
+    return 0;
+}
+
+/**
+ * Read a section start line ("[SECTION]").
+ * Update FFprobeContext.section.
+ * @return  SectionType (>0) for success,
+ *          SEC_NONE if no section start,
+ *          <0 for error
+ */
+static int read_section_start(AVFormatContext *avf)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    uint8_t buf[4096];
+    const char *rest;
+    int i, ret;
+
+    if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
+        return ret;
+    if (*buf != '[')
+        return 0;
+    for (i = 1; i < FF_ARRAY_ELEMS(section_names); i++) {
+        if (av_strstart(buf + 1, section_names[i], &rest) &&
+            !strcmp(rest, "]\n")) {
+            ffp->section = i;
+            return i;
+        }
+    }
+    return SEC_NONE;
+}
+
+/**
+ * Read a line from withing a section.
+ * @return  >0 for success, 0 if end of section, <0 for error
+ */
+static int read_section_line(AVFormatContext *avf, uint8_t *buf, size_t size)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    const char *rest;
+    int ret;
+    size_t l;
+
+    if ((ret = ff_get_line(avf->pb, buf, size)) <= 0)
+        return ret;
+    if (av_strstart(buf, "[/", &rest)) {
+        ffp->section = 0;
+        return 0;
+    }
+    if ((l = strlen(buf)) > 0 && buf[l - 1] == '\n')
+        buf[--l] = 0;
+    return 1;
+}
+
+/**
+ * Read hexadecimal data
+ * Store it in FFprobeContext.data.
+ * @return  >=0 for success, <0 for error
+ */
+static int read_data(AVFormatContext *avf)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    uint8_t buf[4096], *cur;
+    int ret, pos, val;
+    size_t off = 0;
+
+    if (ffp->data.len)
+        return AVERROR_INVALIDDATA;
+    while ((ret = read_section_line(avf, buf, sizeof(buf)))) {
+        if (ret < 0)
+            return ret;
+        if (!buf[0])
+            break;
+        cur = buf;
+        pos = 0;
+        if (sscanf(cur, "%8x:%n", &val, &pos) < 1 || !pos || val != off)
+            return AVERROR_INVALIDDATA;
+        cur += pos;
+        while (1) {
+            if (*cur == ' ')
+                cur++;
+            if (*cur == ' ')
+                break;
+            if ((unsigned)(*cur - '0') >= 10 &&
+                (unsigned)(*cur - 'a') >=  6 &&
+                (unsigned)(*cur - 'A') >=  6)
+                return AVERROR_INVALIDDATA;
+            pos = 0;
+            if (sscanf(cur, " %02x%n", &val, &pos) < 1 || !pos)
+                return AVERROR_INVALIDDATA;
+            cur += pos;
+            av_bprint_chars(&ffp->data, val, 1);
+            off++;
+        }
+    }
+    return av_bprint_is_complete(&ffp->data) ? 0 : AVERROR(ENOMEM);
+}
+
+static int read_section_format(AVFormatContext *avf)
+{
+    uint8_t buf[4096];
+    int ret, val;
+
+    while ((ret = read_section_line(avf, buf, sizeof(buf)))) {
+        if (ret < 0)
+            return ret;
+        if (sscanf(buf, "nb_streams=%d", &val) >= 1) {
+            while (avf->nb_streams < val)
+                if (!avformat_new_stream(avf, NULL))
+                    return AVERROR(ENOMEM);
+        }
+        /* TODO programs */
+        /* TODO start_time duration bit_rate */
+        /* TODO tags */
+    }
+    return SEC_FORMAT;
+}
+
+static int read_section_stream(AVFormatContext *avf)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    uint8_t buf[4096];
+    int ret, index, val1, val2;
+    AVStream *st = NULL;
+    const char *val;
+
+    av_bprint_clear(&ffp->data);
+    while ((ret = read_section_line(avf, buf, sizeof(buf)))) {
+        if (ret < 0)
+            return ret;
+        if (!st) {
+            if (sscanf(buf, "index=%d", &index) >= 1) {
+                if (index == avf->nb_streams) {
+                    if (!avformat_new_stream(avf, NULL))
+                        return AVERROR(ENOMEM);
+                }
+                if ((unsigned)index >= avf->nb_streams) {
+                    av_log(avf, AV_LOG_ERROR, "Invalid stream index: %d\n",
+                           index);
+                    return AVERROR_INVALIDDATA;
+                }
+                st = avf->streams[index];
+            } else {
+                av_log(avf, AV_LOG_ERROR, "Stream without index\n");
+                return AVERROR_INVALIDDATA;
+            }
+        }
+        if (av_strstart(buf, "codec_name=", &val)) {
+            const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(val);
+            if (desc) {
+                st->codec->codec_id   = desc->id;
+                st->codec->codec_type = desc->type;
+            }
+        } else if (!strcmp(buf, "extradata=")) {
+            if ((ret = read_data(avf)) < 0)
+                return ret;
+            if (ffp->data.len) {
+                if ((ret = ff_alloc_extradata(st->codec, ffp->data.len)) < 0)
+                    return ret;
+                memcpy(st->codec->extradata, ffp->data.str, ffp->data.len);
+            }
+        } else if (sscanf(buf, "time_base=%d/%d", &val1, &val2) >= 2) {
+            st->time_base.num = val1;
+            st->time_base.den = val2;
+        }
+    }
+    return SEC_STREAM;
+}
+
+static int read_section_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    uint8_t buf[4096];
+    int ret;
+    AVPacket p;
+    char flags;
+
+    av_init_packet(&p);
+    p.stream_index = -1;
+    p.size = -1;
+    av_bprint_clear(&ffp->data);
+    while ((ret = read_section_line(avf, buf, sizeof(buf)))) {
+        if (ret < 0)
+            return ret;
+        sscanf(buf, "stream_index=%d", &p.stream_index);
+        sscanf(buf, "size=%d", &p.size);
+        sscanf(buf, "pts=%"SCNi64, &p.pts);
+        sscanf(buf, "dts=%"SCNi64, &p.dts);
+        sscanf(buf, "pos=%"SCNi64, &p.pos);
+        sscanf(buf, "duration=%d", &p.duration);
+        if (sscanf(buf, "flags=%c", &flags) >= 1)
+            p.flags = flags == 'K' ? AV_PKT_FLAG_KEY : 0;
+        if (!strcmp(buf, "data="))
+            if ((ret = read_data(avf)) < 0)
+                return ret;
+    }
+    if (p.size < 0 || (unsigned)p.stream_index >= avf->nb_streams)
+        return SEC_NONE;
+    if ((ret = av_new_packet(pkt, p.size)) < 0)
+        return ret;
+    p.data = pkt->data;
+    p.buf  = pkt->buf;
+    *pkt = p;
+    if (ffp->data.len) {
+        ffp->data.len = FFMIN(ffp->data.len, pkt->size);
+        memcpy(pkt->data, ffp->data.str, ffp->data.len);
+    }
+    return SEC_PACKET;
+}
+
+static int read_section(AVFormatContext *avf, AVPacket *pkt)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    int ret, section;
+
+    while (!ffp->section)
+        if ((ret = read_section_start(avf)) < 0)
+            return ret;
+    switch (section = ffp->section) {
+    case SEC_FORMAT:
+        return read_section_format(avf);
+    case SEC_STREAM:
+        return read_section_stream(avf);
+    case SEC_PACKET:
+        return read_section_packet(avf, pkt);
+    default:
+        av_assert0(!"reached");
+        return AVERROR_BUG;
+    }
+}
+
+static int ffprobe_read_header(AVFormatContext *avf)
+{
+    FFprobeContext *ffp = avf->priv_data;
+    int ret;
+
+    av_bprint_init(&ffp->data, 0, AV_BPRINT_SIZE_UNLIMITED);
+    if ((ret = read_section_start(avf)) < 0)
+        return ret;
+    if (ret != SEC_FORMAT) {
+        av_log(avf, AV_LOG_INFO, "Using noheader mode\n");
+        avf->ctx_flags |= AVFMTCTX_NOHEADER;
+        return 0;
+    }
+    if ((ret = read_section_format(avf)) < 0)
+        return ret;
+    return 0;
+}
+
+static int ffprobe_read_packet(AVFormatContext *avf, AVPacket *pkt)
+{
+    int ret;
+
+    while (1) {
+        if ((ret = read_section(avf, pkt)) < 0)
+            return ret;
+        if (ret == SEC_PACKET)
+            return 0;
+    }
+}
+
+static const AVClass ffprobe_default_class = {
+    .class_name = "ffprobe_default demuxer",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_ffprobe_default_demuxer = {
+    .name           = "ffprobe_default",
+    .long_name      = NULL_IF_CONFIG_SMALL("FFprobe output (default writer)"),
+    .priv_data_size = sizeof(FFprobeContext),
+    .read_probe     = ffprobe_probe,
+    .read_header    = ffprobe_read_header,
+    .read_packet    = ffprobe_read_packet,
+    .read_close     = ffprobe_read_close,
+    .priv_class     = &ffprobe_default_class,
+};
-- 
1.9.1

>From 21bc6f21e0ce73ae3d9f0deba2bc6d71b6d088fc Mon Sep 17 00:00:00 2001
From: Stefano Sabatini <stefa...@gmail.com>
Date: Tue, 30 Aug 2016 11:24:33 +0200
Subject: [PATCH] ffprobe: add -show_headers_first option

This is meant to be used for generating output suitable for the
ffprobe_default demuxer.
---
 ffprobe.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/ffprobe.c b/ffprobe.c
index 657867d..42a8d8e 100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -98,6 +98,7 @@ static int use_value_prefix             = 0;
 static int use_byte_value_binary_prefix = 0;
 static int use_value_sexagesimal_format = 0;
 static int show_private_data            = 1;
+static int show_headers_first           = 0;
 
 static char *print_format;
 static char *stream_specifier;
@@ -2683,6 +2684,26 @@ static int probe_file(WriterContext *wctx, const char *filename)
         }
     }
 
+    if (show_headers_first && do_show_format) {
+        ret = show_format(wctx, &ifile);
+        CHECK_END;
+    }
+
+    if (show_headers_first && do_show_chapters) {
+        ret = show_chapters(wctx, &ifile);
+        CHECK_END;
+    }
+
+    if (show_headers_first && do_show_streams) {
+        ret = show_streams(wctx, &ifile);
+        CHECK_END;
+    }
+
+    if (show_headers_first && do_show_programs) {
+        ret = show_programs(wctx, &ifile);
+        CHECK_END;
+    }
+
     if (do_read_frames || do_read_packets) {
         if (do_show_frames && do_show_packets &&
             wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
@@ -2699,20 +2720,20 @@ static int probe_file(WriterContext *wctx, const char *filename)
         CHECK_END;
     }
 
-    if (do_show_programs) {
+    if (!show_headers_first && do_show_programs) {
         ret = show_programs(wctx, &ifile);
         CHECK_END;
     }
 
-    if (do_show_streams) {
+    if (!show_headers_first && do_show_streams) {
         ret = show_streams(wctx, &ifile);
         CHECK_END;
     }
-    if (do_show_chapters) {
+    if (!show_headers_first && do_show_chapters) {
         ret = show_chapters(wctx, &ifile);
         CHECK_END;
     }
-    if (do_show_format) {
+    if (!show_headers_first && do_show_format) {
         ret = show_format(wctx, &ifile);
         CHECK_END;
     }
@@ -3213,6 +3234,7 @@ static const OptionDef real_options[] = {
     { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" },
     { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
     { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
+    { "show_headers_first", OPT_BOOL, {&show_headers_first}, "show headers before the packets/frames" },
     { NULL, },
 };
 
-- 
1.9.1

>From 5547d1c81f61ee16419872073e1775667b6e80a9 Mon Sep 17 00:00:00 2001
From: Stefano Sabatini <stefa...@gmail.com>
Date: Tue, 30 Aug 2016 11:35:18 +0200
Subject: [PATCH] ffprobe: add show_compact_data option

This is meant to slightly reduce the output size.
---
 ffprobe.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/ffprobe.c b/ffprobe.c
index 42a8d8e..a06e5d7 100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -99,6 +99,7 @@ static int use_byte_value_binary_prefix = 0;
 static int use_value_sexagesimal_format = 0;
 static int show_private_data            = 1;
 static int show_headers_first           = 0;
+static int show_compact_data            = 0;
 
 static char *print_format;
 static char *stream_specifier;
@@ -723,17 +724,20 @@ static void writer_print_data(WriterContext *wctx, const char *name,
     av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
     av_bprintf(&bp, "\n");
     while (size) {
+        if (!show_compact_data)
         av_bprintf(&bp, "%08x: ", offset);
         l = FFMIN(size, 16);
         for (i = 0; i < l; i++) {
             av_bprintf(&bp, "%02x", data[i]);
-            if (i & 1)
+            if (!show_compact_data && (i & 1))
                 av_bprintf(&bp, " ");
         }
+        if (!show_compact_data) {
         av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
         for (i = 0; i < l; i++)
             av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
         av_bprintf(&bp, "\n");
+        }
         offset += l;
         data   += l;
         size   -= l;
@@ -3235,6 +3239,7 @@ static const OptionDef real_options[] = {
     { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
     { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
     { "show_headers_first", OPT_BOOL, {&show_headers_first}, "show headers before the packets/frames" },
+    { "show_compact_data", OPT_BOOL, {&show_compact_data}, "show packet data in a compact format" },
     { NULL, },
 };
 
-- 
1.9.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to