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