/* * Copyright (c) 2007-2010 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 * simple media prober based on the FFmpeg libraries */ #include "config.h"#include "libavutil/ffversion.h" #include <string.h> #include "libavformat/avformat.h"#include "libavcodec/avcodec.h"#include "libavutil/avassert.h"#include "libavutil/avstring.h"#include "libavutil/bprint.h"#include "libavutil/display.h"#include "libavutil/hash.h"#include "libavutil/opt.h"#include "libavutil/pixdesc.h"#include "libavutil/spherical.h"#include "libavutil/stereo3d.h"#include "libavutil/dict.h"#include "libavutil/intreadwrite.h"#include "libavutil/libm.h"#include "libavutil/parseutils.h"#include "libavutil/timecode.h"#include "libavutil/timestamp.h"#include "libavdevice/avdevice.h"#include "libswscale/swscale.h"#include "libswresample/swresample.h"#include "libpostproc/postprocess.h"#include "cmdutils.h" typedef struct InputStream { AVStream *st; AVCodecContext *dec_ctx;} InputStream; typedef struct InputFile { AVFormatContext *fmt_ctx; InputStream *streams; int nb_streams;} InputFile; const char program_name[] = "ffprobe";const int program_birth_year = 2007; static int do_bitexact = 0;static int do_count_frames = 0;static int do_count_packets = 0;static int do_read_frames = 0;static int do_read_packets = 0;static int do_show_chapters = 0;static int do_show_error = 0;static int do_show_format = 0;static int do_show_frames = 0;static int do_show_packets = 0;static int do_show_programs = 0;static int do_show_streams = 0;static int do_show_stream_disposition = 0;static int do_show_data = 0;static int do_show_program_version = 0;static int do_show_library_versions = 0;static int do_show_pixel_formats = 0;static int do_show_pixel_format_flags = 0;static int do_show_pixel_format_components = 0; static int do_show_chapter_tags = 0;static int do_show_format_tags = 0;static int do_show_frame_tags = 0;static int do_show_program_tags = 0;static int do_show_stream_tags = 0;static int do_show_packet_tags = 0; static int show_value_unit = 0;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 char *print_format;static char *stream_specifier;static char *show_data_hash; typedef struct ReadInterval { int id; ///< identifier int64_t start, end; ///< start, end in second/AV_TIME_BASE units int has_start, has_end; int start_is_offset, end_is_offset; int duration_frames;} ReadInterval; static ReadInterval *read_intervals;static int read_intervals_nb = 0; /* section structure definition */ #define SECTION_MAX_NB_CHILDREN 10 struct section { int id; ///< unique id identifying a section const char *name; #define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. /// For these sections the element_name field is mandatory. int flags; int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 const char *element_name; ///< name of the contained element, if provided const char *unique_name; ///< unique section name, in case the name is ambiguous AVDictionary *entries_to_show; int show_all_entries;}; typedef enum { SECTION_ID_NONE = -1, SECTION_ID_CHAPTER, SECTION_ID_CHAPTER_TAGS, SECTION_ID_CHAPTERS, SECTION_ID_ERROR, SECTION_ID_FORMAT, SECTION_ID_FORMAT_TAGS, SECTION_ID_FRAME, SECTION_ID_FRAMES, SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_SIDE_DATA, SECTION_ID_LIBRARY_VERSION, SECTION_ID_LIBRARY_VERSIONS, SECTION_ID_PACKET, SECTION_ID_PACKET_TAGS, SECTION_ID_PACKETS, SECTION_ID_PACKETS_AND_FRAMES, SECTION_ID_PACKET_SIDE_DATA_LIST, SECTION_ID_PACKET_SIDE_DATA, SECTION_ID_PIXEL_FORMAT, SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENT, SECTION_ID_PIXEL_FORMAT_COMPONENTS, SECTION_ID_PIXEL_FORMATS, SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, SECTION_ID_PROGRAM, SECTION_ID_PROGRAM_STREAMS, SECTION_ID_PROGRAM_STREAM, SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_VERSION, SECTION_ID_PROGRAMS, SECTION_ID_ROOT, SECTION_ID_STREAM, SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAMS, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, SECTION_ID_STREAM_SIDE_DATA, SECTION_ID_SUBTITLE,} SectionID; static struct section sections[] = { [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" }, [SECTION_ID_ERROR] = { SECTION_ID_ERROR, "error", 0, { -1 } }, [SECTION_ID_FORMAT] = { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } }, [SECTION_ID_FORMAT_TAGS] = { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" }, [SECTION_ID_FRAMES] = { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } }, [SECTION_ID_FRAME] = { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, -1 } }, [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" }, [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" }, [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } }, [SECTION_ID_LIBRARY_VERSION] = { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } }, [SECTION_ID_PACKETS] = { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, [SECTION_ID_PACKET] = { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } }, [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" }, [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" }, [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } }, [SECTION_ID_PIXEL_FORMAT] = { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } }, [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" }, [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" }, [SECTION_ID_PIXEL_FORMAT_COMPONENT] = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } }, [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" }, [SECTION_ID_PROGRAM_STREAM_TAGS] = { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" }, [SECTION_ID_PROGRAM] = { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } }, [SECTION_ID_PROGRAM_STREAMS] = { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" }, [SECTION_ID_PROGRAM_STREAM] = { SECTION_ID_PROGRAM_STREAM, "stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 }, .unique_name = "program_stream" }, [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" }, [SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } }, [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } }, [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER, { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS, SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, SECTION_ID_PIXEL_FORMATS, -1} }, [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } }, [SECTION_ID_STREAM] = { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } }, [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" }, [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" }, [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" }, [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } },}; static const OptionDef *options; /* FFprobe context */static const char *input_filename;static AVInputFormat *iformat = NULL; static struct AVHashContext *hash; static const struct { double bin_val; double dec_val; const char *bin_str; const char *dec_str;} si_prefixes[] = { { 1.0, 1.0, "", "" }, { 1.024e3, 1e3, "Ki", "K" }, { 1.048576e6, 1e6, "Mi", "M" }, { 1.073741824e9, 1e9, "Gi", "G" }, { 1.099511627776e12, 1e12, "Ti", "T" }, { 1.125899906842624e15, 1e15, "Pi", "P" },}; static const char unit_second_str[] = "s" ;static const char unit_hertz_str[] = "Hz" ;static const char unit_byte_str[] = "byte" ;static const char unit_bit_per_second_str[] = "bit/s"; static int nb_streams;static uint64_t *nb_streams_packets;static uint64_t *nb_streams_frames;static int *selected_streams; static void ffprobe_cleanup(int ret){ int i; for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) av_dict_free(&(sections[i].entries_to_show));} struct unit_value { union { double d; long long int i; } val; const char *unit;}; static char *value_string(char *buf, int buf_size, struct unit_value uv){ double vald; long long int vali; int show_float = 0; if (uv.unit == unit_second_str) { vald = uv.val.d; show_float = 1; } else { vald = vali = uv.val.i; } if (uv.unit == unit_second_str && use_value_sexagesimal_format) { double secs; int hours, mins; secs = vald; mins = (int)secs / 60; secs = secs - mins * 60; hours = mins / 60; mins %= 60; snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs); } else { const char *prefix_string = ""; if (use_value_prefix && vald > 1) { long long int index; if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) { index = (long long int) (log2(vald)) / 10; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].bin_val; prefix_string = si_prefixes[index].bin_str; } else { index = (long long int) (log10(vald)) / 3; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].dec_val; prefix_string = si_prefixes[index].dec_str; } vali = vald; } if (show_float || (use_value_prefix && vald != (long long int)vald)) snprintf(buf, buf_size, "%f", vald); else snprintf(buf, buf_size, "%lld", vali); av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "", prefix_string, show_value_unit ? uv.unit : ""); } return buf;} /* WRITERS API */ typedef struct WriterContext WriterContext; #define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2 typedef enum { WRITER_STRING_VALIDATION_FAIL, WRITER_STRING_VALIDATION_REPLACE, WRITER_STRING_VALIDATION_IGNORE, WRITER_STRING_VALIDATION_NB} StringValidation; typedef struct Writer { const AVClass *priv_class; ///< private class of the writer, if any int priv_size; ///< private size for the writer context const char *name; int (*init) (WriterContext *wctx); void (*uninit)(WriterContext *wctx); void (*print_section_header)(WriterContext *wctx); void (*print_section_footer)(WriterContext *wctx); void (*print_integer) (WriterContext *wctx, const char *, long long int); void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep); void (*print_string) (WriterContext *wctx, const char *, const char *); int flags; ///< a combination or WRITER_FLAG_*} Writer; #define SECTION_MAX_NB_LEVELS 10 struct WriterContext { const AVClass *class; ///< class of the writer const Writer *writer; ///< the Writer of which this is an instance char *name; ///< name of this writer instance void *priv; ///< private data for use by the filter const struct section *sections; ///< array containing all sections int nb_sections; ///< number of sections int level; ///< current level, starting from 0 /** number of the item printed in the given section, starting from 0 */ unsigned int nb_item[SECTION_MAX_NB_LEVELS]; /** section per each level */ const struct section *section[SECTION_MAX_NB_LEVELS]; AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section, /// used by various writers unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section unsigned int nb_section_frame; ///< number of the frame section in case we are in "packets_and_frames" section unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames int string_validation; char *string_validation_replacement; unsigned int string_validation_utf8_flags;}; static const char *writer_get_name(void *p){ WriterContext *wctx = p; return wctx->writer->name;} #define OFFSET(x) offsetof(WriterContext, x) static const AVOption writer_options[] = { { "string_validation", "set string validation mode", OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, { "sv", "set string validation mode", OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE}, .unit = "sv" }, { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" }, { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL}, .unit = "sv" }, { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}}, { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}}, { NULL }}; static void *writer_child_next(void *obj, void *prev){ WriterContext *ctx = obj; if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv) return ctx->priv; return NULL;} static const AVClass writer_class = { .class_name = "Writer", .item_name = writer_get_name, .option = writer_options, .version = LIBAVUTIL_VERSION_INT, .child_next = writer_child_next,}; static void writer_close(WriterContext **wctx){ int i; if (!*wctx) return; if ((*wctx)->writer->uninit) (*wctx)->writer->uninit(*wctx); for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL); if ((*wctx)->writer->priv_class) av_opt_free((*wctx)->priv); av_freep(&((*wctx)->priv)); av_opt_free(*wctx); av_freep(wctx);} static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size){ int i; av_bprintf(bp, "0X"); for (i = 0; i < ubuf_size; i++) av_bprintf(bp, "%02X", ubuf[i]);} static int writer_open(WriterContext **wctx, const Writer *writer, const char *args, const struct section *sections, int nb_sections){ int i, ret = 0; if (!(*wctx = av_mallocz(sizeof(WriterContext)))) { ret = AVERROR(ENOMEM); goto fail; } if (!((*wctx)->priv = av_mallocz(writer->priv_size))) { ret = AVERROR(ENOMEM); goto fail; } (*wctx)->class = &writer_class; (*wctx)->writer = writer; (*wctx)->level = -1; (*wctx)->sections = sections; (*wctx)->nb_sections = nb_sections; av_opt_set_defaults(*wctx); if (writer->priv_class) { void *priv_ctx = (*wctx)->priv; *((const AVClass **)priv_ctx) = writer->priv_class; av_opt_set_defaults(priv_ctx); } /* convert options to dictionary */ if (args) { AVDictionary *opts = NULL; AVDictionaryEntry *opt = NULL; if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) { av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args); av_dict_free(&opts); goto fail; } while ((opt = av_dict_get(opts, "", opt, AV_DICT_IGNORE_SUFFIX))) { if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) { av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n", opt->key, opt->value); av_dict_free(&opts); goto fail; } } av_dict_free(&opts); } Lines 1–500 of 3400. Load more
/* * Copyright (c) 2007-2010 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 * simple media prober based on the FFmpeg libraries */ #include "config.h" #include "libavutil/ffversion.h" #include <string.h> #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/display.h" #include "libavutil/hash.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/spherical.h" #include "libavutil/stereo3d.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" #include "libavutil/libm.h" #include "libavutil/parseutils.h" #include "libavutil/timecode.h" #include "libavutil/timestamp.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" #include "libpostproc/postprocess.h" #include "cmdutils.h" typedef struct InputStream { AVStream *st; AVCodecContext *dec_ctx; } InputStream; typedef struct InputFile { AVFormatContext *fmt_ctx; InputStream *streams; int nb_streams; } InputFile; const char program_name[] = "ffprobe"; const int program_birth_year = 2007; static int do_bitexact = 0; static int do_count_frames = 0; static int do_count_packets = 0; static int do_read_frames = 0; static int do_read_packets = 0; static int do_show_chapters = 0; static int do_show_error = 0; static int do_show_format = 0; static int do_show_frames = 0; static int do_show_packets = 0; static int do_show_programs = 0; static int do_show_streams = 0; static int do_show_stream_disposition = 0; static int do_show_data = 0; static int do_show_program_version = 0; static int do_show_library_versions = 0; static int do_show_pixel_formats = 0; static int do_show_pixel_format_flags = 0; static int do_show_pixel_format_components = 0; static int do_show_chapter_tags = 0; static int do_show_format_tags = 0; static int do_show_frame_tags = 0; static int do_show_program_tags = 0; static int do_show_stream_tags = 0; static int do_show_packet_tags = 0; static int show_value_unit = 0; 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 char *print_format; static char *stream_specifier; static char *show_data_hash; typedef struct ReadInterval { int id; ///< identifier int64_t start, end; ///< start, end in second/AV_TIME_BASE units int has_start, has_end; int start_is_offset, end_is_offset; int duration_frames; } ReadInterval; static ReadInterval *read_intervals; static int read_intervals_nb = 0; /* section structure definition */ #define SECTION_MAX_NB_CHILDREN 10 struct section { int id; ///< unique id identifying a section const char *name; #define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level #define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type #define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. /// For these sections the element_name field is mandatory. int flags; int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 const char *element_name; ///< name of the contained element, if provided const char *unique_name; ///< unique section name, in case the name is ambiguous AVDictionary *entries_to_show; int show_all_entries; }; typedef enum { SECTION_ID_NONE = -1, SECTION_ID_CHAPTER, SECTION_ID_CHAPTER_TAGS, SECTION_ID_CHAPTERS, SECTION_ID_ERROR, SECTION_ID_FORMAT, SECTION_ID_FORMAT_TAGS, SECTION_ID_FRAME, SECTION_ID_FRAMES, SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_SIDE_DATA, SECTION_ID_LIBRARY_VERSION, SECTION_ID_LIBRARY_VERSIONS, SECTION_ID_PACKET, SECTION_ID_PACKET_TAGS, SECTION_ID_PACKETS, SECTION_ID_PACKETS_AND_FRAMES, SECTION_ID_PACKET_SIDE_DATA_LIST, SECTION_ID_PACKET_SIDE_DATA, SECTION_ID_PIXEL_FORMAT, SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENT, SECTION_ID_PIXEL_FORMAT_COMPONENTS, SECTION_ID_PIXEL_FORMATS, SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, SECTION_ID_PROGRAM, SECTION_ID_PROGRAM_STREAMS, SECTION_ID_PROGRAM_STREAM, SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_VERSION, SECTION_ID_PROGRAMS, SECTION_ID_ROOT, SECTION_ID_STREAM, SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAMS, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, SECTION_ID_STREAM_SIDE_DATA, SECTION_ID_SUBTITLE, } SectionID; static struct section sections[] = { [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" }, [SECTION_ID_ERROR] = { SECTION_ID_ERROR, "error", 0, { -1 } }, [SECTION_ID_FORMAT] = { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } }, [SECTION_ID_FORMAT_TAGS] = { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" }, [SECTION_ID_FRAMES] = { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } }, [SECTION_ID_FRAME] = { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, -1 } }, [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" }, [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" }, [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } }, [SECTION_ID_LIBRARY_VERSION] = { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } }, [SECTION_ID_PACKETS] = { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, [SECTION_ID_PACKET] = { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } }, [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" }, [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" }, [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } }, [SECTION_ID_PIXEL_FORMAT] = { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } }, [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" }, [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" }, [SECTION_ID_PIXEL_FORMAT_COMPONENT] = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } }, [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" }, [SECTION_ID_PROGRAM_STREAM_TAGS] = { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" }, [SECTION_ID_PROGRAM] = { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } }, [SECTION_ID_PROGRAM_STREAMS] = { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" }, [SECTION_ID_PROGRAM_STREAM] = { SECTION_ID_PROGRAM_STREAM, "stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 }, .unique_name = "program_stream" }, [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" }, [SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } }, [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } }, [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER, { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS, SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, SECTION_ID_PIXEL_FORMATS, -1} }, [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } }, [SECTION_ID_STREAM] = { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } }, [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" }, [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" }, [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" }, [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", 0, { -1 } }, [SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } }, }; static const OptionDef *options; /* FFprobe context */ static const char *input_filename; static AVInputFormat *iformat = NULL; static struct AVHashContext *hash; static const struct { double bin_val; double dec_val; const char *bin_str; const char *dec_str; } si_prefixes[] = { { 1.0, 1.0, "", "" }, { 1.024e3, 1e3, "Ki", "K" }, { 1.048576e6, 1e6, "Mi", "M" }, { 1.073741824e9, 1e9, "Gi", "G" }, { 1.099511627776e12, 1e12, "Ti", "T" }, { 1.125899906842624e15, 1e15, "Pi", "P" }, }; static const char unit_second_str[] = "s" ; static const char unit_hertz_str[] = "Hz" ; static const char unit_byte_str[] = "byte" ; static const char unit_bit_per_second_str[] = "bit/s"; static int nb_streams; static uint64_t *nb_streams_packets; static uint64_t *nb_streams_frames; static int *selected_streams; static void ffprobe_cleanup(int ret) { int i; for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) av_dict_free(&(sections[i].entries_to_show)); } struct unit_value { union { double d; long long int i; } val; const char *unit; }; static char *value_string(char *buf, int buf_size, struct unit_value uv) { double vald; long long int vali; int show_float = 0; if (uv.unit == unit_second_str) { vald = uv.val.d; show_float = 1; } else { vald = vali = uv.val.i; } if (uv.unit == unit_second_str && use_value_sexagesimal_format) { double secs; int hours, mins; secs = vald; mins = (int)secs / 60; secs = secs - mins * 60; hours = mins / 60; mins %= 60; snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs); } else { const char *prefix_string = ""; if (use_value_prefix && vald > 1) { long long int index; if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) { index = (long long int) (log2(vald)) / 10; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].bin_val; prefix_string = si_prefixes[index].bin_str; } else { index = (long long int) (log10(vald)) / 3; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].dec_val; prefix_string = si_prefixes[index].dec_str; } vali = vald; } if (show_float || (use_value_prefix && vald != (long long int)vald)) snprintf(buf, buf_size, "%f", vald); else snprintf(buf, buf_size, "%lld", vali); av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "", prefix_string, show_value_unit ? uv.unit : ""); } return buf; } /* WRITERS API */ typedef struct WriterContext WriterContext; #define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1 #define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2 typedef enum { WRITER_STRING_VALIDATION_FAIL, WRITER_STRING_VALIDATION_REPLACE, WRITER_STRING_VALIDATION_IGNORE, WRITER_STRING_VALIDATION_NB } StringValidation; typedef struct Writer { const AVClass *priv_class; ///< private class of the writer, if any int priv_size; ///< private size for the writer context const char *name; int (*init) (WriterContext *wctx); void (*uninit)(WriterContext *wctx); void (*print_section_header)(WriterContext *wctx); void (*print_section_footer)(WriterContext *wctx); void (*print_integer) (WriterContext *wctx, const char *, long long int); void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep); void (*print_string) (WriterContext *wctx, const char *, const char *); int flags; ///< a combination or WRITER_FLAG_* } Writer; #define SECTION_MAX_NB_LEVELS 10 struct WriterContext { const AVClass *class; ///< class of the writer const Writer *writer; ///< the Writer of which this is an instance char *name; ///< name of this writer instance void *priv; ///< private data for use by the filter const struct section *sections; ///< array containing all sections int nb_sections; ///< number of sections int level; ///< current level, starting from 0 /** number of the item printed in the given section, starting from 0 */ unsigned int nb_item[SECTION_MAX_NB_LEVELS]; /** section per each level */ const struct section *section[SECTION_MAX_NB_LEVELS]; AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section, /// used by various writers unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section unsigned int nb_section_frame; ///< number of the frame section in case we are in "packets_and_frames" section unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames int string_validation; char *string_validation_replacement; unsigned int string_validation_utf8_flags; }; static const char *writer_get_name(void *p) { WriterContext *wctx = p; return wctx->writer->name; } #define OFFSET(x) offsetof(WriterContext, x) static const AVOption writer_options[] = { { "string_validation", "set string validation mode", OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, { "sv", "set string validation mode", OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE}, .unit = "sv" }, { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" }, { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL}, .unit = "sv" }, { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}}, { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}}, { NULL } }; static void *writer_child_next(void *obj, void *prev) { WriterContext *ctx = obj; if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv) return ctx->priv; return NULL; } static const AVClass writer_class = { .class_name = "Writer", .item_name = writer_get_name, .option = writer_options, .version = LIBAVUTIL_VERSION_INT, .child_next = writer_child_next, }; static void writer_close(WriterContext **wctx) { int i; if (!*wctx) return; if ((*wctx)->writer->uninit) (*wctx)->writer->uninit(*wctx); for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL); if ((*wctx)->writer->priv_class) av_opt_free((*wctx)->priv); av_freep(&((*wctx)->priv)); av_opt_free(*wctx); av_freep(wctx); } static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size) { int i; av_bprintf(bp, "0X"); for (i = 0; i < ubuf_size; i++) av_bprintf(bp, "%02X", ubuf[i]); } static int writer_open(WriterContext **wctx, const Writer *writer, const char *args, const struct section *sections, int nb_sections) { int i, ret = 0; if (!(*wctx = av_mallocz(sizeof(WriterContext)))) { ret = AVERROR(ENOMEM); goto fail; } if (!((*wctx)->priv = av_mallocz(writer->priv_size))) { ret = AVERROR(ENOMEM); goto fail; } (*wctx)->class = &writer_class; (*wctx)->writer = writer; (*wctx)->level = -1; (*wctx)->sections = sections; (*wctx)->nb_sections = nb_sections; av_opt_set_defaults(*wctx); if (writer->priv_class) { void *priv_ctx = (*wctx)->priv; *((const AVClass **)priv_ctx) = writer->priv_class; av_opt_set_defaults(priv_ctx); } /* convert options to dictionary */ if (args) { AVDictionary *opts = NULL; AVDictionaryEntry *opt = NULL; if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) { av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args); av_dict_free(&opts); goto fail; } while ((opt = av_dict_get(opts, "", opt, AV_DICT_IGNORE_SUFFIX))) { if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) { av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n", opt->key, opt->value); av_dict_free(&opts); goto fail; } } av_dict_free(&opts); }