| | 1 | /* |
| | 2 | * Copyright (c) 2007-2010 Stefano Sabatini |
| | 3 | * |
| | 4 | * This file is part of FFmpeg. |
| | 5 | * |
| | 6 | * FFmpeg is free software; you can redistribute it and/or |
| | 7 | * modify it under the terms of the GNU Lesser General Public |
| | 8 | * License as published by the Free Software Foundation; either |
| | 9 | * version 2.1 of the License, or (at your option) any later version. |
| | 10 | * |
| | 11 | * FFmpeg is distributed in the hope that it will be useful, |
| | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | 14 | * Lesser General Public License for more details. |
| | 15 | * |
| | 16 | * You should have received a copy of the GNU Lesser General Public |
| | 17 | * License along with FFmpeg; if not, write to the Free Software |
| | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| | 19 | */ |
| | 20 | |
| | 21 | /** |
| | 22 | * @file |
| | 23 | * simple media prober based on the FFmpeg libraries |
| | 24 | */ |
| | 25 | |
| | 26 | #include "config.h" |
| | 27 | #include "libavutil/ffversion.h" |
| | 28 | |
| | 29 | #include <string.h> |
| | 30 | #include <math.h> |
| | 31 | |
| | 32 | #include "libavformat/avformat.h" |
| | 33 | #include "libavformat/version.h" |
| | 34 | #include "libavcodec/avcodec.h" |
| | 35 | #include "libavcodec/version.h" |
| | 36 | #include "libavutil/ambient_viewing_environment.h" |
| | 37 | #include "libavutil/avassert.h" |
| | 38 | #include "libavutil/avstring.h" |
| | 39 | #include "libavutil/bprint.h" |
| | 40 | #include "libavutil/channel_layout.h" |
| | 41 | #include "libavutil/display.h" |
| | 42 | #include "libavutil/hash.h" |
| | 43 | #include "libavutil/hdr_dynamic_metadata.h" |
| | 44 | #include "libavutil/mastering_display_metadata.h" |
| | 45 | #include "libavutil/hdr_dynamic_vivid_metadata.h" |
| | 46 | #include "libavutil/dovi_meta.h" |
| | 47 | #include "libavutil/opt.h" |
| | 48 | #include "libavutil/pixdesc.h" |
| | 49 | #include "libavutil/spherical.h" |
| | 50 | #include "libavutil/stereo3d.h" |
| | 51 | #include "libavutil/dict.h" |
| | 52 | #include "libavutil/intreadwrite.h" |
| | 53 | #include "libavutil/libm.h" |
| | 54 | #include "libavutil/parseutils.h" |
| | 55 | #include "libavutil/timecode.h" |
| | 56 | #include "libavutil/timestamp.h" |
| | 57 | #include "libavdevice/avdevice.h" |
| | 58 | #include "libavdevice/version.h" |
| | 59 | #include "libswscale/swscale.h" |
| | 60 | #include "libswscale/version.h" |
| | 61 | #include "libswresample/swresample.h" |
| | 62 | #include "libswresample/version.h" |
| | 63 | #include "libpostproc/postprocess.h" |
| | 64 | #include "libpostproc/version.h" |
| | 65 | #include "libavfilter/version.h" |
| | 66 | #include "cmdutils.h" |
| | 67 | #include "opt_common.h" |
| | 68 | |
| | 69 | #include "libavutil/thread.h" |
| | 70 | |
| | 71 | #include "ffprobe_in_ffmpeg.h" |
| | 72 | |
| | 73 | #if !HAVE_THREADS |
| | 74 | # ifdef pthread_mutex_lock |
| | 75 | # undef pthread_mutex_lock |
| | 76 | # endif |
| | 77 | # define pthread_mutex_lock(a) do{}while(0) |
| | 78 | # ifdef pthread_mutex_unlock |
| | 79 | # undef pthread_mutex_unlock |
| | 80 | # endif |
| | 81 | # define pthread_mutex_unlock(a) do{}while(0) |
| | 82 | #endif |
| | 83 | |
| | 84 | // attached as opaque_ref to packets/frames |
| | 85 | typedef struct FrameData { |
| | 86 | int64_t pkt_pos; |
| | 87 | int pkt_size; |
| | 88 | } FrameData; |
| | 89 | |
| | 90 | typedef struct InputStream { |
| | 91 | AVStream *st; |
| | 92 | |
| | 93 | AVCodecContext *dec_ctx; |
| | 94 | } InputStream; |
| | 95 | |
| | 96 | typedef struct InputFile { |
| | 97 | AVFormatContext *fmt_ctx; |
| | 98 | |
| | 99 | InputStream *streams; |
| | 100 | int nb_streams; |
| | 101 | } InputFile; |
| | 102 | |
| | 103 | static int do_bitexact = 0; |
| | 104 | static int do_count_frames = 0; |
| | 105 | static int do_count_packets = 0; |
| | 106 | static int do_read_frames = 0; |
| | 107 | static int do_read_packets = 0; |
| | 108 | static int do_show_chapters = 0; |
| | 109 | static int do_show_error = 0; |
| | 110 | static int do_show_format = 0; |
| | 111 | static int do_show_frames = 0; |
| | 112 | static int do_show_packets = 0; |
| | 113 | static int do_show_programs = 0; |
| | 114 | static int do_show_streams = 0; |
| | 115 | static int do_show_stream_disposition = 0; |
| | 116 | static int do_show_data = 0; |
| | 117 | static int do_show_program_version = 0; |
| | 118 | static int do_show_library_versions = 0; |
| | 119 | static int do_show_pixel_formats = 0; |
| | 120 | static int do_show_pixel_format_flags = 0; |
| | 121 | static int do_show_pixel_format_components = 0; |
| | 122 | static int do_show_log = 0; |
| | 123 | |
| | 124 | static int do_show_chapter_tags = 0; |
| | 125 | static int do_show_format_tags = 0; |
| | 126 | static int do_show_frame_tags = 0; |
| | 127 | static int do_show_program_tags = 0; |
| | 128 | static int do_show_stream_tags = 0; |
| | 129 | static int do_show_packet_tags = 0; |
| | 130 | |
| | 131 | static int show_value_unit = 0; |
| | 132 | static int use_value_prefix = 0; |
| | 133 | static int use_byte_value_binary_prefix = 0; |
| | 134 | static int use_value_sexagesimal_format = 0; |
| | 135 | static int show_private_data = 1; |
| | 136 | |
| | 137 | #define SHOW_OPTIONAL_FIELDS_AUTO -1 |
| | 138 | #define SHOW_OPTIONAL_FIELDS_NEVER 0 |
| | 139 | #define SHOW_OPTIONAL_FIELDS_ALWAYS 1 |
| | 140 | static int show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; |
| | 141 | |
| | 142 | static char *output_format; |
| | 143 | static char *stream_specifier; |
| | 144 | static char *show_data_hash; |
| | 145 | |
| | 146 | typedef struct ReadInterval { |
| | 147 | int id; ///< identifier |
| | 148 | int64_t start, end; ///< start, end in second/AV_TIME_BASE units |
| | 149 | int has_start, has_end; |
| | 150 | int start_is_offset, end_is_offset; |
| | 151 | int duration_frames; |
| | 152 | } ReadInterval; |
| | 153 | |
| | 154 | static ReadInterval *read_intervals; |
| | 155 | static int read_intervals_nb = 0; |
| | 156 | |
| | 157 | static int find_stream_info = 1; |
| | 158 | |
| | 159 | /* section structure definition */ |
| | 160 | |
| | 161 | #define SECTION_MAX_NB_CHILDREN 10 |
| | 162 | |
| | 163 | typedef enum { |
| | 164 | SECTION_ID_NONE = -1, |
| | 165 | SECTION_ID_CHAPTER, |
| | 166 | SECTION_ID_CHAPTER_TAGS, |
| | 167 | SECTION_ID_CHAPTERS, |
| | 168 | SECTION_ID_ERROR, |
| | 169 | SECTION_ID_FORMAT, |
| | 170 | SECTION_ID_FORMAT_TAGS, |
| | 171 | SECTION_ID_FRAME, |
| | 172 | SECTION_ID_FRAMES, |
| | 173 | SECTION_ID_FRAME_TAGS, |
| | 174 | SECTION_ID_FRAME_SIDE_DATA_LIST, |
| | 175 | SECTION_ID_FRAME_SIDE_DATA, |
| | 176 | SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, |
| | 177 | SECTION_ID_FRAME_SIDE_DATA_TIMECODE, |
| | 178 | SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, |
| | 179 | SECTION_ID_FRAME_SIDE_DATA_COMPONENT, |
| | 180 | SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, |
| | 181 | SECTION_ID_FRAME_SIDE_DATA_PIECE, |
| | 182 | SECTION_ID_FRAME_LOG, |
| | 183 | SECTION_ID_FRAME_LOGS, |
| | 184 | SECTION_ID_LIBRARY_VERSION, |
| | 185 | SECTION_ID_LIBRARY_VERSIONS, |
| | 186 | SECTION_ID_PACKET, |
| | 187 | SECTION_ID_PACKET_TAGS, |
| | 188 | SECTION_ID_PACKETS, |
| | 189 | SECTION_ID_PACKETS_AND_FRAMES, |
| | 190 | SECTION_ID_PACKET_SIDE_DATA_LIST, |
| | 191 | SECTION_ID_PACKET_SIDE_DATA, |
| | 192 | SECTION_ID_PIXEL_FORMAT, |
| | 193 | SECTION_ID_PIXEL_FORMAT_FLAGS, |
| | 194 | SECTION_ID_PIXEL_FORMAT_COMPONENT, |
| | 195 | SECTION_ID_PIXEL_FORMAT_COMPONENTS, |
| | 196 | SECTION_ID_PIXEL_FORMATS, |
| | 197 | SECTION_ID_PROGRAM_STREAM_DISPOSITION, |
| | 198 | SECTION_ID_PROGRAM_STREAM_TAGS, |
| | 199 | SECTION_ID_PROGRAM, |
| | 200 | SECTION_ID_PROGRAM_STREAMS, |
| | 201 | SECTION_ID_PROGRAM_STREAM, |
| | 202 | SECTION_ID_PROGRAM_TAGS, |
| | 203 | SECTION_ID_PROGRAM_VERSION, |
| | 204 | SECTION_ID_PROGRAMS, |
| | 205 | SECTION_ID_ROOT, |
| | 206 | SECTION_ID_STREAM, |
| | 207 | SECTION_ID_STREAM_DISPOSITION, |
| | 208 | SECTION_ID_STREAMS, |
| | 209 | SECTION_ID_STREAM_TAGS, |
| | 210 | SECTION_ID_STREAM_SIDE_DATA_LIST, |
| | 211 | SECTION_ID_STREAM_SIDE_DATA, |
| | 212 | SECTION_ID_SUBTITLE, |
| | 213 | } SectionID; |
| | 214 | |
| | 215 | struct section { |
| | 216 | int id; ///< unique id identifying a section |
| | 217 | const char *name; |
| | 218 | |
| | 219 | #define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level |
| | 220 | #define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type |
| | 221 | #define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. |
| | 222 | /// For these sections the element_name field is mandatory. |
| | 223 | #define SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements |
| | 224 | |
| | 225 | int flags; |
| | 226 | const SectionID children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 |
| | 227 | const char *element_name; ///< name of the contained element, if provided |
| | 228 | const char *unique_name; ///< unique section name, in case the name is ambiguous |
| | 229 | AVDictionary *entries_to_show; |
| | 230 | const char *(* get_type)(void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined |
| | 231 | int show_all_entries; |
| | 232 | }; |
| | 233 | |
| | 234 | static const char *get_packet_side_data_type(void *data) { |
| | 235 | const AVPacketSideData *sd = (const AVPacketSideData *)data; |
| | 236 | return av_x_if_null(av_packet_side_data_name(sd->type), "unknown"); |
| | 237 | } |
| | 238 | |
| | 239 | static const char *get_frame_side_data_type(void *data) { |
| | 240 | const AVFrameSideData *sd = (const AVFrameSideData *)data; |
| | 241 | return av_x_if_null(av_frame_side_data_name(sd->type), "unknown"); |
| | 242 | } |
| | 243 | |
| | 244 | static struct section sections[] = { |
| | 245 | [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, |
| | 246 | [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, |
| | 247 | [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" }, |
| | 248 | [SECTION_ID_ERROR] = { SECTION_ID_ERROR, "error", 0, { -1 } }, |
| | 249 | [SECTION_ID_FORMAT] = { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } }, |
| | 250 | [SECTION_ID_FORMAT_TAGS] = { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" }, |
| | 251 | [SECTION_ID_FRAMES] = { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } }, |
| | 252 | [SECTION_ID_FRAME] = { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } }, |
| | 253 | [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" }, |
| | 254 | [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" }, |
| | 255 | [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data", .element_name = "side_datum", .get_type = get_frame_side_data_type }, |
| | 256 | [SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, "timecodes", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, -1 } }, |
| | 257 | [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } }, |
| | 258 | [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 } }, |
| | 259 | [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", 0, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 } }, |
| | 260 | [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 } }, |
| | 261 | [SECTION_ID_FRAME_SIDE_DATA_PIECE] = { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", 0, { -1 } }, |
| | 262 | [SECTION_ID_FRAME_LOGS] = { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } }, |
| | 263 | [SECTION_ID_FRAME_LOG] = { SECTION_ID_FRAME_LOG, "log", 0, { -1 }, }, |
| | 264 | [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } }, |
| | 265 | [SECTION_ID_LIBRARY_VERSION] = { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } }, |
| | 266 | [SECTION_ID_PACKETS] = { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, |
| | 267 | [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} }, |
| | 268 | [SECTION_ID_PACKET] = { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } }, |
| | 269 | [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" }, |
| | 270 | [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" }, |
| | 271 | [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .unique_name = "packet_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type }, |
| | 272 | [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } }, |
| | 273 | [SECTION_ID_PIXEL_FORMAT] = { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } }, |
| | 274 | [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" }, |
| | 275 | [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" }, |
| | 276 | [SECTION_ID_PIXEL_FORMAT_COMPONENT] = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } }, |
| | 277 | [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" }, |
| | 278 | [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" }, |
| | 279 | [SECTION_ID_PROGRAM] = { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } }, |
| | 280 | [SECTION_ID_PROGRAM_STREAMS] = { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" }, |
| | 281 | [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" }, |
| | 282 | [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" }, |
| | 283 | [SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } }, |
| | 284 | [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } }, |
| | 285 | [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER, |
| | 286 | { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS, |
| | 287 | SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, |
| | 288 | SECTION_ID_PIXEL_FORMATS, -1} }, |
| | 289 | [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } }, |
| | 290 | [SECTION_ID_STREAM] = { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } }, |
| | 291 | [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" }, |
| | 292 | [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" }, |
| | 293 | [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" }, |
| | 294 | [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", SECTION_FLAG_HAS_TYPE|SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type }, |
| | 295 | [SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } }, |
| | 296 | }; |
| | 297 | |
| | 298 | static const OptionDef *options; |
| | 299 | |
| | 300 | /* FFprobe context */ |
| | 301 | static const char *input_filename; |
| | 302 | static const char *print_input_filename; |
| | 303 | static const AVInputFormat *iformat = NULL; |
| | 304 | static const char *output_filename = NULL; |
| | 305 | |
| | 306 | static struct AVHashContext *hash; |
| | 307 | |
| | 308 | static const struct { |
| | 309 | double bin_val; |
| | 310 | double dec_val; |
| | 311 | const char *bin_str; |
| | 312 | const char *dec_str; |
| | 313 | } si_prefixes[] = { |
| | 314 | { 1.0, 1.0, "", "" }, |
| | 315 | { 1.024e3, 1e3, "Ki", "K" }, |
| | 316 | { 1.048576e6, 1e6, "Mi", "M" }, |
| | 317 | { 1.073741824e9, 1e9, "Gi", "G" }, |
| | 318 | { 1.099511627776e12, 1e12, "Ti", "T" }, |
| | 319 | { 1.125899906842624e15, 1e15, "Pi", "P" }, |
| | 320 | }; |
| | 321 | |
| | 322 | static const char unit_second_str[] = "s" ; |
| | 323 | static const char unit_hertz_str[] = "Hz" ; |
| | 324 | static const char unit_byte_str[] = "byte" ; |
| | 325 | static const char unit_bit_per_second_str[] = "bit/s"; |
| | 326 | |
| | 327 | static int nb_streams; |
| | 328 | static uint64_t *nb_streams_packets; |
| | 329 | static uint64_t *nb_streams_frames; |
| | 330 | static int *selected_streams; |
| | 331 | |
| | 332 | #if HAVE_THREADS |
| | 333 | pthread_mutex_t log_mutex; |
| | 334 | #endif |
| | 335 | typedef struct LogBuffer { |
| | 336 | char *context_name; |
| | 337 | int log_level; |
| | 338 | char *log_message; |
| | 339 | AVClassCategory category; |
| | 340 | char *parent_name; |
| | 341 | AVClassCategory parent_category; |
| | 342 | }LogBuffer; |
| | 343 | |
| | 344 | static LogBuffer *log_buffer; |
| | 345 | static int log_buffer_size; |
| | 346 | |
| | 347 | static void log_callback(void *ptr, int level, const char *fmt, va_list vl) |
| | 348 | { |
| | 349 | AVClass* avc = ptr ? *(AVClass **) ptr : NULL; |
| | 350 | va_list vl2; |
| | 351 | char line[1024]; |
| | 352 | static int print_prefix = 1; |
| | 353 | void *new_log_buffer; |
| | 354 | |
| | 355 | va_copy(vl2, vl); |
| | 356 | av_log_default_callback(ptr, level, fmt, vl); |
| | 357 | av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix); |
| | 358 | va_end(vl2); |
| | 359 | |
| | 360 | #if HAVE_THREADS |
| | 361 | pthread_mutex_lock(&log_mutex); |
| | 362 | |
| | 363 | new_log_buffer = av_realloc_array(log_buffer, log_buffer_size + 1, sizeof(*log_buffer)); |
| | 364 | if (new_log_buffer) { |
| | 365 | char *msg; |
| | 366 | int i; |
| | 367 | |
| | 368 | log_buffer = new_log_buffer; |
| | 369 | memset(&log_buffer[log_buffer_size], 0, sizeof(log_buffer[log_buffer_size])); |
| | 370 | log_buffer[log_buffer_size].context_name= avc ? av_strdup(avc->item_name(ptr)) : NULL; |
| | 371 | if (avc) { |
| | 372 | if (avc->get_category) log_buffer[log_buffer_size].category = avc->get_category(ptr); |
| | 373 | else log_buffer[log_buffer_size].category = avc->category; |
| | 374 | } |
| | 375 | log_buffer[log_buffer_size].log_level = level; |
| | 376 | msg = log_buffer[log_buffer_size].log_message = av_strdup(line); |
| | 377 | for (i=strlen(msg) - 1; i>=0 && msg[i] == '\n'; i--) { |
| | 378 | msg[i] = 0; |
| | 379 | } |
| | 380 | if (avc && avc->parent_log_context_offset) { |
| | 381 | AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) + |
| | 382 | avc->parent_log_context_offset); |
| | 383 | if (parent && *parent) { |
| | 384 | log_buffer[log_buffer_size].parent_name = av_strdup((*parent)->item_name(parent)); |
| | 385 | log_buffer[log_buffer_size].parent_category = |
| | 386 | (*parent)->get_category ? (*parent)->get_category(parent) :(*parent)->category; |
| | 387 | } |
| | 388 | } |
| | 389 | log_buffer_size ++; |
| | 390 | } |
| | 391 | |
| | 392 | pthread_mutex_unlock(&log_mutex); |
| | 393 | #endif |
| | 394 | } |
| | 395 | |
| | 396 | struct unit_value { |
| | 397 | union { double d; long long int i; } val; |
| | 398 | const char *unit; |
| | 399 | }; |
| | 400 | |
| | 401 | static char *value_string(char *buf, int buf_size, struct unit_value uv) |
| | 402 | { |
| | 403 | double vald; |
| | 404 | long long int vali; |
| | 405 | int show_float = 0; |
| | 406 | |
| | 407 | if (uv.unit == unit_second_str) { |
| | 408 | vald = uv.val.d; |
| | 409 | show_float = 1; |
| | 410 | } else { |
| | 411 | vald = vali = uv.val.i; |
| | 412 | } |
| | 413 | |
| | 414 | if (uv.unit == unit_second_str && use_value_sexagesimal_format) { |
| | 415 | double secs; |
| | 416 | int hours, mins; |
| | 417 | secs = vald; |
| | 418 | mins = (int)secs / 60; |
| | 419 | secs = secs - mins * 60; |
| | 420 | hours = mins / 60; |
| | 421 | mins %= 60; |
| | 422 | snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs); |
| | 423 | } else { |
| | 424 | const char *prefix_string = ""; |
| | 425 | |
| | 426 | if (use_value_prefix && vald > 1) { |
| | 427 | long long int index; |
| | 428 | |
| | 429 | if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) { |
| | 430 | index = (long long int) (log2(vald)) / 10; |
| | 431 | index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); |
| | 432 | vald /= si_prefixes[index].bin_val; |
| | 433 | prefix_string = si_prefixes[index].bin_str; |
| | 434 | } else { |
| | 435 | index = (long long int) (log10(vald)) / 3; |
| | 436 | index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); |
| | 437 | vald /= si_prefixes[index].dec_val; |
| | 438 | prefix_string = si_prefixes[index].dec_str; |
| | 439 | } |
| | 440 | vali = vald; |
| | 441 | } |
| | 442 | |
| | 443 | if (show_float || (use_value_prefix && vald != (long long int)vald)) |
| | 444 | snprintf(buf, buf_size, "%f", vald); |
| | 445 | else |
| | 446 | snprintf(buf, buf_size, "%lld", vali); |
| | 447 | av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "", |
| | 448 | prefix_string, show_value_unit ? uv.unit : ""); |
| | 449 | } |
| | 450 | |
| | 451 | return buf; |
| | 452 | } |
| | 453 | |
| | 454 | /* WRITERS API */ |
| | 455 | |
| | 456 | typedef struct WriterContext WriterContext; |
| | 457 | |
| | 458 | #define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1 |
| | 459 | #define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2 |
| | 460 | |
| | 461 | typedef enum { |
| | 462 | WRITER_STRING_VALIDATION_FAIL, |
| | 463 | WRITER_STRING_VALIDATION_REPLACE, |
| | 464 | WRITER_STRING_VALIDATION_IGNORE, |
| | 465 | WRITER_STRING_VALIDATION_NB |
| | 466 | } StringValidation; |
| | 467 | |
| | 468 | typedef struct Writer { |
| | 469 | const AVClass *priv_class; ///< private class of the writer, if any |
| | 470 | int priv_size; ///< private size for the writer context |
| | 471 | const char *name; |
| | 472 | |
| | 473 | int (*init) (WriterContext *wctx); |
| | 474 | void (*uninit)(WriterContext *wctx); |
| | 475 | |
| | 476 | void (*print_section_header)(WriterContext *wctx, void *data); |
| | 477 | void (*print_section_footer)(WriterContext *wctx); |
| | 478 | void (*print_integer) (WriterContext *wctx, const char *, long long int); |
| | 479 | void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep); |
| | 480 | void (*print_string) (WriterContext *wctx, const char *, const char *); |
| | 481 | int flags; ///< a combination or WRITER_FLAG_* |
| | 482 | } Writer; |
| | 483 | |
| | 484 | #define SECTION_MAX_NB_LEVELS 10 |
| | 485 | |
| | 486 | struct WriterContext { |
| | 487 | const AVClass *class; ///< class of the writer |
| | 488 | const Writer *writer; ///< the Writer of which this is an instance |
| | 489 | AVIOContext *avio; ///< the I/O context used to write |
| | 490 | |
| | 491 | void (* writer_w8)(WriterContext *wctx, int b); |
| | 492 | void (* writer_put_str)(WriterContext *wctx, const char *str); |
| | 493 | void (* writer_printf)(WriterContext *wctx, const char *fmt, ...); |
| | 494 | |
| | 495 | char *name; ///< name of this writer instance |
| | 496 | void *priv; ///< private data for use by the filter |
| | 497 | |
| | 498 | const struct section *sections; ///< array containing all sections |
| | 499 | int nb_sections; ///< number of sections |
| | 500 | |
| | 501 | int level; ///< current level, starting from 0 |
| | 502 | |
| | 503 | /** number of the item printed in the given section, starting from 0 */ |
| | 504 | unsigned int nb_item[SECTION_MAX_NB_LEVELS]; |
| | 505 | |
| | 506 | /** section per each level */ |
| | 507 | const struct section *section[SECTION_MAX_NB_LEVELS]; |
| | 508 | AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section, |
| | 509 | /// used by various writers |
| | 510 | |
| | 511 | unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section |
| | 512 | unsigned int nb_section_frame; ///< number of the frame section in case we are in "packets_and_frames" section |
| | 513 | unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames |
| | 514 | |
| | 515 | int string_validation; |
| | 516 | char *string_validation_replacement; |
| | 517 | unsigned int string_validation_utf8_flags; |
| | 518 | }; |
| | 519 | |
| | 520 | static const char *writer_get_name(void *p) |
| | 521 | { |
| | 522 | WriterContext *wctx = p; |
| | 523 | return wctx->writer->name; |
| | 524 | } |
| | 525 | |
| | 526 | #define OFFSET(x) offsetof(WriterContext, x) |
| | 527 | |
| | 528 | static const AVOption writer_options[] = { |
| | 529 | { "string_validation", "set string validation mode", |
| | 530 | OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, |
| | 531 | { "sv", "set string validation mode", |
| | 532 | OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" }, |
| | 533 | { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE}, .unit = "sv" }, |
| | 534 | { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" }, |
| | 535 | { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL}, .unit = "sv" }, |
| | 536 | { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}}, |
| | 537 | { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}}, |
| | 538 | { NULL } |
| | 539 | }; |
| | 540 | |
| | 541 | static void *writer_child_next(void *obj, void *prev) |
| | 542 | { |
| | 543 | WriterContext *ctx = obj; |
| | 544 | if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv) |
| | 545 | return ctx->priv; |
| | 546 | return NULL; |
| | 547 | } |
| | 548 | |
| | 549 | static const AVClass writer_class = { |
| | 550 | .class_name = "Writer", |
| | 551 | .item_name = writer_get_name, |
| | 552 | .option = writer_options, |
| | 553 | .version = LIBAVUTIL_VERSION_INT, |
| | 554 | .child_next = writer_child_next, |
| | 555 | }; |
| | 556 | |
| | 557 | static int writer_close(WriterContext **wctx) |
| | 558 | { |
| | 559 | int i; |
| | 560 | int ret = 0; |
| | 561 | |
| | 562 | if (!*wctx) |
| | 563 | return -1; |
| | 564 | |
| | 565 | if ((*wctx)->writer->uninit) |
| | 566 | (*wctx)->writer->uninit(*wctx); |
| | 567 | for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) |
| | 568 | av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL); |
| | 569 | if ((*wctx)->writer->priv_class) |
| | 570 | av_opt_free((*wctx)->priv); |
| | 571 | av_freep(&((*wctx)->priv)); |
| | 572 | av_opt_free(*wctx); |
| | 573 | if ((*wctx)->avio) { |
| | 574 | avio_flush((*wctx)->avio); |
| | 575 | ret = avio_close((*wctx)->avio); |
| | 576 | } |
| | 577 | av_freep(wctx); |
| | 578 | return ret; |
| | 579 | } |
| | 580 | |
| | 581 | static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size) |
| | 582 | { |
| | 583 | int i; |
| | 584 | av_bprintf(bp, "0X"); |
| | 585 | for (i = 0; i < ubuf_size; i++) |
| | 586 | av_bprintf(bp, "%02X", ubuf[i]); |
| | 587 | } |
| | 588 | |
| | 589 | static inline void writer_w8_avio(WriterContext *wctx, int b) |
| | 590 | { |
| | 591 | avio_w8(wctx->avio, b); |
| | 592 | } |
| | 593 | |
| | 594 | static inline void writer_put_str_avio(WriterContext *wctx, const char *str) |
| | 595 | { |
| | 596 | avio_write(wctx->avio, str, strlen(str)); |
| | 597 | } |
| | 598 | |
| | 599 | static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...) |
| | 600 | { |
| | 601 | va_list ap; |
| | 602 | |
| | 603 | va_start(ap, fmt); |
| | 604 | avio_vprintf(wctx->avio, fmt, ap); |
| | 605 | va_end(ap); |
| | 606 | } |
| | 607 | |
| | 608 | static inline void writer_w8_printf(WriterContext *wctx, int b) |
| | 609 | { |
| | 610 | printf("%c", b); |
| | 611 | } |
| | 612 | |
| | 613 | static inline void writer_put_str_printf(WriterContext *wctx, const char *str) |
| | 614 | { |
| | 615 | printf("%s", str); |
| | 616 | } |
| | 617 | |
| | 618 | static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ...) |
| | 619 | { |
| | 620 | va_list ap; |
| | 621 | |
| | 622 | va_start(ap, fmt); |
| | 623 | vprintf(fmt, ap); |
| | 624 | va_end(ap); |
| | 625 | } |
| | 626 | |
| | 627 | static int writer_open(WriterContext **wctx, const Writer *writer, const char *args, |
| | 628 | const struct section *sections, int nb_sections, const char *output) |
| | 629 | { |
| | 630 | int i, ret = 0; |
| | 631 | |
| | 632 | if (!(*wctx = av_mallocz(sizeof(WriterContext)))) { |
| | 633 | ret = AVERROR(ENOMEM); |
| | 634 | goto fail; |
| | 635 | } |
| | 636 | |
| | 637 | if (!((*wctx)->priv = av_mallocz(writer->priv_size))) { |
| | 638 | ret = AVERROR(ENOMEM); |
| | 639 | goto fail; |
| | 640 | } |
| | 641 | |
| | 642 | (*wctx)->class = &writer_class; |
| | 643 | (*wctx)->writer = writer; |
| | 644 | (*wctx)->level = -1; |
| | 645 | (*wctx)->sections = sections; |
| | 646 | (*wctx)->nb_sections = nb_sections; |
| | 647 | |
| | 648 | av_opt_set_defaults(*wctx); |
| | 649 | |
| | 650 | if (writer->priv_class) { |
| | 651 | void *priv_ctx = (*wctx)->priv; |
| | 652 | *((const AVClass **)priv_ctx) = writer->priv_class; |
| | 653 | av_opt_set_defaults(priv_ctx); |
| | 654 | } |
| | 655 | |
| | 656 | /* convert options to dictionary */ |
| | 657 | if (args) { |
| | 658 | AVDictionary *opts = NULL; |
| | 659 | const AVDictionaryEntry *opt = NULL; |
| | 660 | |
| | 661 | if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) { |
| | 662 | av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args); |
| | 663 | av_dict_free(&opts); |
| | 664 | goto fail; |
| | 665 | } |
| | 666 | |
| | 667 | while ((opt = av_dict_iterate(opts, opt))) { |
| | 668 | if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) { |
| | 669 | av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n", |
| | 670 | opt->key, opt->value); |
| | 671 | av_dict_free(&opts); |
| | 672 | goto fail; |
| | 673 | } |
| | 674 | } |
| | 675 | |
| | 676 | av_dict_free(&opts); |
| | 677 | } |
| | 678 | |
| | 679 | /* validate replace string */ |
| | 680 | { |
| | 681 | const uint8_t *p = (*wctx)->string_validation_replacement; |
| | 682 | const uint8_t *endp = p + strlen(p); |
| | 683 | while (*p) { |
| | 684 | const uint8_t *p0 = p; |
| | 685 | int32_t code; |
| | 686 | ret = av_utf8_decode(&code, &p, endp, (*wctx)->string_validation_utf8_flags); |
| | 687 | if (ret < 0) { |
| | 688 | AVBPrint bp; |
| | 689 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); |
| | 690 | bprint_bytes(&bp, p0, p-p0), |
| | 691 | av_log(wctx, AV_LOG_ERROR, |
| | 692 | "Invalid UTF8 sequence %s found in string validation replace '%s'\n", |
| | 693 | bp.str, (*wctx)->string_validation_replacement); |
| | 694 | return ret; |
| | 695 | } |
| | 696 | } |
| | 697 | } |
| | 698 | |
| | 699 | if (!output_filename) { |
| | 700 | (*wctx)->writer_w8 = writer_w8_printf; |
| | 701 | (*wctx)->writer_put_str = writer_put_str_printf; |
| | 702 | (*wctx)->writer_printf = writer_printf_printf; |
| | 703 | } else { |
| | 704 | if ((ret = avio_open(&(*wctx)->avio, output, AVIO_FLAG_WRITE)) < 0) { |
| | 705 | av_log(*wctx, AV_LOG_ERROR, |
| | 706 | "Failed to open output '%s' with error: %s\n", output, av_err2str(ret)); |
| | 707 | goto fail; |
| | 708 | } |
| | 709 | (*wctx)->writer_w8 = writer_w8_avio; |
| | 710 | (*wctx)->writer_put_str = writer_put_str_avio; |
| | 711 | (*wctx)->writer_printf = writer_printf_avio; |
| | 712 | } |
| | 713 | |
| | 714 | for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) |
| | 715 | av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 716 | |
| | 717 | if ((*wctx)->writer->init) |
| | 718 | ret = (*wctx)->writer->init(*wctx); |
| | 719 | if (ret < 0) |
| | 720 | goto fail; |
| | 721 | |
| | 722 | return 0; |
| | 723 | |
| | 724 | fail: |
| | 725 | writer_close(wctx); |
| | 726 | return ret; |
| | 727 | } |
| | 728 | |
| | 729 | static inline void writer_print_section_header(WriterContext *wctx, |
| | 730 | void *data, |
| | 731 | int section_id) |
| | 732 | { |
| | 733 | int parent_section_id; |
| | 734 | wctx->level++; |
| | 735 | av_assert0(wctx->level < SECTION_MAX_NB_LEVELS); |
| | 736 | parent_section_id = wctx->level ? |
| | 737 | (wctx->section[wctx->level-1])->id : SECTION_ID_NONE; |
| | 738 | |
| | 739 | wctx->nb_item[wctx->level] = 0; |
| | 740 | wctx->section[wctx->level] = &wctx->sections[section_id]; |
| | 741 | |
| | 742 | if (section_id == SECTION_ID_PACKETS_AND_FRAMES) { |
| | 743 | wctx->nb_section_packet = wctx->nb_section_frame = |
| | 744 | wctx->nb_section_packet_frame = 0; |
| | 745 | } else if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) { |
| | 746 | wctx->nb_section_packet_frame = section_id == SECTION_ID_PACKET ? |
| | 747 | wctx->nb_section_packet : wctx->nb_section_frame; |
| | 748 | } |
| | 749 | |
| | 750 | if (wctx->writer->print_section_header) |
| | 751 | wctx->writer->print_section_header(wctx, data); |
| | 752 | } |
| | 753 | |
| | 754 | static inline void writer_print_section_footer(WriterContext *wctx) |
| | 755 | { |
| | 756 | int section_id = wctx->section[wctx->level]->id; |
| | 757 | int parent_section_id = wctx->level ? |
| | 758 | wctx->section[wctx->level-1]->id : SECTION_ID_NONE; |
| | 759 | |
| | 760 | if (parent_section_id != SECTION_ID_NONE) |
| | 761 | wctx->nb_item[wctx->level-1]++; |
| | 762 | if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) { |
| | 763 | if (section_id == SECTION_ID_PACKET) wctx->nb_section_packet++; |
| | 764 | else wctx->nb_section_frame++; |
| | 765 | } |
| | 766 | if (wctx->writer->print_section_footer) |
| | 767 | wctx->writer->print_section_footer(wctx); |
| | 768 | wctx->level--; |
| | 769 | } |
| | 770 | |
| | 771 | static inline void writer_print_integer(WriterContext *wctx, |
| | 772 | const char *key, long long int val) |
| | 773 | { |
| | 774 | const struct section *section = wctx->section[wctx->level]; |
| | 775 | |
| | 776 | if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) { |
| | 777 | wctx->writer->print_integer(wctx, key, val); |
| | 778 | wctx->nb_item[wctx->level]++; |
| | 779 | } |
| | 780 | } |
| | 781 | |
| | 782 | static inline int validate_string(WriterContext *wctx, char **dstp, const char *src) |
| | 783 | { |
| | 784 | const uint8_t *p, *endp; |
| | 785 | AVBPrint dstbuf; |
| | 786 | int invalid_chars_nb = 0, ret = 0; |
| | 787 | |
| | 788 | av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED); |
| | 789 | |
| | 790 | endp = src + strlen(src); |
| | 791 | for (p = (uint8_t *)src; *p;) { |
| | 792 | uint32_t code; |
| | 793 | int invalid = 0; |
| | 794 | const uint8_t *p0 = p; |
| | 795 | |
| | 796 | if (av_utf8_decode(&code, &p, endp, wctx->string_validation_utf8_flags) < 0) { |
| | 797 | AVBPrint bp; |
| | 798 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); |
| | 799 | bprint_bytes(&bp, p0, p-p0); |
| | 800 | av_log(wctx, AV_LOG_DEBUG, |
| | 801 | "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src); |
| | 802 | invalid = 1; |
| | 803 | } |
| | 804 | |
| | 805 | if (invalid) { |
| | 806 | invalid_chars_nb++; |
| | 807 | |
| | 808 | switch (wctx->string_validation) { |
| | 809 | case WRITER_STRING_VALIDATION_FAIL: |
| | 810 | av_log(wctx, AV_LOG_ERROR, |
| | 811 | "Invalid UTF-8 sequence found in string '%s'\n", src); |
| | 812 | ret = AVERROR_INVALIDDATA; |
| | 813 | goto end; |
| | 814 | break; |
| | 815 | |
| | 816 | case WRITER_STRING_VALIDATION_REPLACE: |
| | 817 | av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement); |
| | 818 | break; |
| | 819 | } |
| | 820 | } |
| | 821 | |
| | 822 | if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE) |
| | 823 | av_bprint_append_data(&dstbuf, p0, p-p0); |
| | 824 | } |
| | 825 | |
| | 826 | if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) { |
| | 827 | av_log(wctx, AV_LOG_WARNING, |
| | 828 | "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n", |
| | 829 | invalid_chars_nb, src, wctx->string_validation_replacement); |
| | 830 | } |
| | 831 | |
| | 832 | end: |
| | 833 | av_bprint_finalize(&dstbuf, dstp); |
| | 834 | return ret; |
| | 835 | } |
| | 836 | |
| | 837 | #define PRINT_STRING_OPT 1 |
| | 838 | #define PRINT_STRING_VALIDATE 2 |
| | 839 | |
| | 840 | static inline int writer_print_string(WriterContext *wctx, |
| | 841 | const char *key, const char *val, int flags) |
| | 842 | { |
| | 843 | const struct section *section = wctx->section[wctx->level]; |
| | 844 | int ret = 0; |
| | 845 | |
| | 846 | if (show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER || |
| | 847 | (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO |
| | 848 | && (flags & PRINT_STRING_OPT) |
| | 849 | && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))) |
| | 850 | return 0; |
| | 851 | |
| | 852 | if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) { |
| | 853 | if (flags & PRINT_STRING_VALIDATE) { |
| | 854 | char *key1 = NULL, *val1 = NULL; |
| | 855 | ret = validate_string(wctx, &key1, key); |
| | 856 | if (ret < 0) goto end; |
| | 857 | ret = validate_string(wctx, &val1, val); |
| | 858 | if (ret < 0) goto end; |
| | 859 | wctx->writer->print_string(wctx, key1, val1); |
| | 860 | end: |
| | 861 | if (ret < 0) { |
| | 862 | av_log(wctx, AV_LOG_ERROR, |
| | 863 | "Invalid key=value string combination %s=%s in section %s\n", |
| | 864 | key, val, section->unique_name); |
| | 865 | } |
| | 866 | av_free(key1); |
| | 867 | av_free(val1); |
| | 868 | } else { |
| | 869 | wctx->writer->print_string(wctx, key, val); |
| | 870 | } |
| | 871 | |
| | 872 | wctx->nb_item[wctx->level]++; |
| | 873 | } |
| | 874 | |
| | 875 | return ret; |
| | 876 | } |
| | 877 | |
| | 878 | static inline void writer_print_rational(WriterContext *wctx, |
| | 879 | const char *key, AVRational q, char sep) |
| | 880 | { |
| | 881 | AVBPrint buf; |
| | 882 | av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); |
| | 883 | av_bprintf(&buf, "%d%c%d", q.num, sep, q.den); |
| | 884 | writer_print_string(wctx, key, buf.str, 0); |
| | 885 | } |
| | 886 | |
| | 887 | static void writer_print_time(WriterContext *wctx, const char *key, |
| | 888 | int64_t ts, const AVRational *time_base, int is_duration) |
| | 889 | { |
| | 890 | char buf[128]; |
| | 891 | |
| | 892 | if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { |
| | 893 | writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT); |
| | 894 | } else { |
| | 895 | double d = ts * av_q2d(*time_base); |
| | 896 | struct unit_value uv; |
| | 897 | uv.val.d = d; |
| | 898 | uv.unit = unit_second_str; |
| | 899 | value_string(buf, sizeof(buf), uv); |
| | 900 | writer_print_string(wctx, key, buf, 0); |
| | 901 | } |
| | 902 | } |
| | 903 | |
| | 904 | static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration) |
| | 905 | { |
| | 906 | if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { |
| | 907 | writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT); |
| | 908 | } else { |
| | 909 | writer_print_integer(wctx, key, ts); |
| | 910 | } |
| | 911 | } |
| | 912 | |
| | 913 | static void writer_print_data(WriterContext *wctx, const char *name, |
| | 914 | const uint8_t *data, int size) |
| | 915 | { |
| | 916 | AVBPrint bp; |
| | 917 | int offset = 0, l, i; |
| | 918 | |
| | 919 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); |
| | 920 | av_bprintf(&bp, "\n"); |
| | 921 | while (size) { |
| | 922 | av_bprintf(&bp, "%08x: ", offset); |
| | 923 | l = FFMIN(size, 16); |
| | 924 | for (i = 0; i < l; i++) { |
| | 925 | av_bprintf(&bp, "%02x", data[i]); |
| | 926 | if (i & 1) |
| | 927 | av_bprintf(&bp, " "); |
| | 928 | } |
| | 929 | av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2); |
| | 930 | for (i = 0; i < l; i++) |
| | 931 | av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1); |
| | 932 | av_bprintf(&bp, "\n"); |
| | 933 | offset += l; |
| | 934 | data += l; |
| | 935 | size -= l; |
| | 936 | } |
| | 937 | writer_print_string(wctx, name, bp.str, 0); |
| | 938 | av_bprint_finalize(&bp, NULL); |
| | 939 | } |
| | 940 | |
| | 941 | static void writer_print_data_hash(WriterContext *wctx, const char *name, |
| | 942 | const uint8_t *data, int size) |
| | 943 | { |
| | 944 | char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 }; |
| | 945 | |
| | 946 | if (!hash) |
| | 947 | return; |
| | 948 | av_hash_init(hash); |
| | 949 | av_hash_update(hash, data, size); |
| | 950 | snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(hash)); |
| | 951 | p = buf + strlen(buf); |
| | 952 | av_hash_final_hex(hash, p, buf + sizeof(buf) - p); |
| | 953 | writer_print_string(wctx, name, buf, 0); |
| | 954 | } |
| | 955 | |
| | 956 | static void writer_print_integers(WriterContext *wctx, const char *name, |
| | 957 | uint8_t *data, int size, const char *format, |
| | 958 | int columns, int bytes, int offset_add) |
| | 959 | { |
| | 960 | AVBPrint bp; |
| | 961 | int offset = 0, l, i; |
| | 962 | |
| | 963 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); |
| | 964 | av_bprintf(&bp, "\n"); |
| | 965 | while (size) { |
| | 966 | av_bprintf(&bp, "%08x: ", offset); |
| | 967 | l = FFMIN(size, columns); |
| | 968 | for (i = 0; i < l; i++) { |
| | 969 | if (bytes == 1) av_bprintf(&bp, format, *data); |
| | 970 | else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data)); |
| | 971 | else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data)); |
| | 972 | data += bytes; |
| | 973 | size --; |
| | 974 | } |
| | 975 | av_bprintf(&bp, "\n"); |
| | 976 | offset += offset_add; |
| | 977 | } |
| | 978 | writer_print_string(wctx, name, bp.str, 0); |
| | 979 | av_bprint_finalize(&bp, NULL); |
| | 980 | } |
| | 981 | |
| | 982 | #define writer_w8(wctx_, b_) (wctx_)->writer_w8(wctx_, b_) |
| | 983 | #define writer_put_str(wctx_, str_) (wctx_)->writer_put_str(wctx_, str_) |
| | 984 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer_printf(wctx_, fmt_, __VA_ARGS__) |
| | 985 | |
| | 986 | #define MAX_REGISTERED_WRITERS_NB 64 |
| | 987 | |
| | 988 | static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1]; |
| | 989 | |
| | 990 | static int writer_register(const Writer *writer) |
| | 991 | { |
| | 992 | static int next_registered_writer_idx = 0; |
| | 993 | |
| | 994 | if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB) |
| | 995 | return AVERROR(ENOMEM); |
| | 996 | |
| | 997 | registered_writers[next_registered_writer_idx++] = writer; |
| | 998 | return 0; |
| | 999 | } |
| | 1000 | |
| | 1001 | static const Writer *writer_get_by_name(const char *name) |
| | 1002 | { |
| | 1003 | int i; |
| | 1004 | |
| | 1005 | for (i = 0; registered_writers[i]; i++) |
| | 1006 | if (!strcmp(registered_writers[i]->name, name)) |
| | 1007 | return registered_writers[i]; |
| | 1008 | |
| | 1009 | return NULL; |
| | 1010 | } |
| | 1011 | |
| | 1012 | |
| | 1013 | /* WRITERS */ |
| | 1014 | |
| | 1015 | #define DEFINE_WRITER_CLASS(name) \ |
| | 1016 | static const char *name##_get_name(void *ctx) \ |
| | 1017 | { \ |
| | 1018 | return #name ; \ |
| | 1019 | } \ |
| | 1020 | static const AVClass name##_class = { \ |
| | 1021 | .class_name = #name, \ |
| | 1022 | .item_name = name##_get_name, \ |
| | 1023 | .option = name##_options \ |
| | 1024 | } |
| | 1025 | |
| | 1026 | /* Default output */ |
| | 1027 | |
| | 1028 | typedef struct DefaultContext { |
| | 1029 | const AVClass *class; |
| | 1030 | int nokey; |
| | 1031 | int noprint_wrappers; |
| | 1032 | int nested_section[SECTION_MAX_NB_LEVELS]; |
| | 1033 | } DefaultContext; |
| | 1034 | |
| | 1035 | #undef OFFSET |
| | 1036 | #define OFFSET(x) offsetof(DefaultContext, x) |
| | 1037 | |
| | 1038 | static const AVOption default_options[] = { |
| | 1039 | { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1040 | { "nw", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1041 | { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1042 | { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1043 | {NULL}, |
| | 1044 | }; |
| | 1045 | |
| | 1046 | DEFINE_WRITER_CLASS(default); |
| | 1047 | |
| | 1048 | /* lame uppercasing routine, assumes the string is lower case ASCII */ |
| | 1049 | static inline char *upcase_string(char *dst, size_t dst_size, const char *src) |
| | 1050 | { |
| | 1051 | int i; |
| | 1052 | for (i = 0; src[i] && i < dst_size-1; i++) |
| | 1053 | dst[i] = av_toupper(src[i]); |
| | 1054 | dst[i] = 0; |
| | 1055 | return dst; |
| | 1056 | } |
| | 1057 | |
| | 1058 | static void default_print_section_header(WriterContext *wctx, void *data) |
| | 1059 | { |
| | 1060 | DefaultContext *def = wctx->priv; |
| | 1061 | char buf[32]; |
| | 1062 | const struct section *section = wctx->section[wctx->level]; |
| | 1063 | const struct section *parent_section = wctx->level ? |
| | 1064 | wctx->section[wctx->level-1] : NULL; |
| | 1065 | |
| | 1066 | av_bprint_clear(&wctx->section_pbuf[wctx->level]); |
| | 1067 | if (parent_section && |
| | 1068 | !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) { |
| | 1069 | def->nested_section[wctx->level] = 1; |
| | 1070 | av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:", |
| | 1071 | wctx->section_pbuf[wctx->level-1].str, |
| | 1072 | upcase_string(buf, sizeof(buf), |
| | 1073 | av_x_if_null(section->element_name, section->name))); |
| | 1074 | } |
| | 1075 | |
| | 1076 | if (def->noprint_wrappers || def->nested_section[wctx->level]) |
| | 1077 | return; |
| | 1078 | |
| | 1079 | if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) |
| | 1080 | writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name)); |
| | 1081 | } |
| | 1082 | |
| | 1083 | static void default_print_section_footer(WriterContext *wctx) |
| | 1084 | { |
| | 1085 | DefaultContext *def = wctx->priv; |
| | 1086 | const struct section *section = wctx->section[wctx->level]; |
| | 1087 | char buf[32]; |
| | 1088 | |
| | 1089 | if (def->noprint_wrappers || def->nested_section[wctx->level]) |
| | 1090 | return; |
| | 1091 | |
| | 1092 | if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) |
| | 1093 | writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name)); |
| | 1094 | } |
| | 1095 | |
| | 1096 | static void default_print_str(WriterContext *wctx, const char *key, const char *value) |
| | 1097 | { |
| | 1098 | DefaultContext *def = wctx->priv; |
| | 1099 | |
| | 1100 | if (!def->nokey) |
| | 1101 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); |
| | 1102 | writer_printf(wctx, "%s\n", value); |
| | 1103 | } |
| | 1104 | |
| | 1105 | static void default_print_int(WriterContext *wctx, const char *key, long long int value) |
| | 1106 | { |
| | 1107 | DefaultContext *def = wctx->priv; |
| | 1108 | |
| | 1109 | if (!def->nokey) |
| | 1110 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); |
| | 1111 | writer_printf(wctx, "%lld\n", value); |
| | 1112 | } |
| | 1113 | |
| | 1114 | static const Writer default_writer = { |
| | 1115 | .name = "default", |
| | 1116 | .priv_size = sizeof(DefaultContext), |
| | 1117 | .print_section_header = default_print_section_header, |
| | 1118 | .print_section_footer = default_print_section_footer, |
| | 1119 | .print_integer = default_print_int, |
| | 1120 | .print_string = default_print_str, |
| | 1121 | .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, |
| | 1122 | .priv_class = &default_class, |
| | 1123 | }; |
| | 1124 | |
| | 1125 | /* Compact output */ |
| | 1126 | |
| | 1127 | /** |
| | 1128 | * Apply C-language-like string escaping. |
| | 1129 | */ |
| | 1130 | static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) |
| | 1131 | { |
| | 1132 | const char *p; |
| | 1133 | |
| | 1134 | for (p = src; *p; p++) { |
| | 1135 | switch (*p) { |
| | 1136 | case '\b': av_bprintf(dst, "%s", "\\b"); break; |
| | 1137 | case '\f': av_bprintf(dst, "%s", "\\f"); break; |
| | 1138 | case '\n': av_bprintf(dst, "%s", "\\n"); break; |
| | 1139 | case '\r': av_bprintf(dst, "%s", "\\r"); break; |
| | 1140 | case '\\': av_bprintf(dst, "%s", "\\\\"); break; |
| | 1141 | default: |
| | 1142 | if (*p == sep) |
| | 1143 | av_bprint_chars(dst, '\\', 1); |
| | 1144 | av_bprint_chars(dst, *p, 1); |
| | 1145 | } |
| | 1146 | } |
| | 1147 | return dst->str; |
| | 1148 | } |
| | 1149 | |
| | 1150 | /** |
| | 1151 | * Quote fields containing special characters, check RFC4180. |
| | 1152 | */ |
| | 1153 | static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) |
| | 1154 | { |
| | 1155 | char meta_chars[] = { sep, '"', '\n', '\r', '\0' }; |
| | 1156 | int needs_quoting = !!src[strcspn(src, meta_chars)]; |
| | 1157 | |
| | 1158 | if (needs_quoting) |
| | 1159 | av_bprint_chars(dst, '"', 1); |
| | 1160 | |
| | 1161 | for (; *src; src++) { |
| | 1162 | if (*src == '"') |
| | 1163 | av_bprint_chars(dst, '"', 1); |
| | 1164 | av_bprint_chars(dst, *src, 1); |
| | 1165 | } |
| | 1166 | if (needs_quoting) |
| | 1167 | av_bprint_chars(dst, '"', 1); |
| | 1168 | return dst->str; |
| | 1169 | } |
| | 1170 | |
| | 1171 | static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) |
| | 1172 | { |
| | 1173 | return src; |
| | 1174 | } |
| | 1175 | |
| | 1176 | typedef struct CompactContext { |
| | 1177 | const AVClass *class; |
| | 1178 | char *item_sep_str; |
| | 1179 | char item_sep; |
| | 1180 | int nokey; |
| | 1181 | int print_section; |
| | 1182 | char *escape_mode_str; |
| | 1183 | const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx); |
| | 1184 | int nested_section[SECTION_MAX_NB_LEVELS]; |
| | 1185 | int has_nested_elems[SECTION_MAX_NB_LEVELS]; |
| | 1186 | int terminate_line[SECTION_MAX_NB_LEVELS]; |
| | 1187 | } CompactContext; |
| | 1188 | |
| | 1189 | #undef OFFSET |
| | 1190 | #define OFFSET(x) offsetof(CompactContext, x) |
| | 1191 | |
| | 1192 | static const AVOption compact_options[]= { |
| | 1193 | {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, |
| | 1194 | {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, |
| | 1195 | {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1196 | {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1197 | {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, |
| | 1198 | {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, |
| | 1199 | {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1200 | {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1201 | {NULL}, |
| | 1202 | }; |
| | 1203 | |
| | 1204 | DEFINE_WRITER_CLASS(compact); |
| | 1205 | |
| | 1206 | static av_cold int compact_init(WriterContext *wctx) |
| | 1207 | { |
| | 1208 | CompactContext *compact = wctx->priv; |
| | 1209 | |
| | 1210 | if (strlen(compact->item_sep_str) != 1) { |
| | 1211 | av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", |
| | 1212 | compact->item_sep_str); |
| | 1213 | return AVERROR(EINVAL); |
| | 1214 | } |
| | 1215 | compact->item_sep = compact->item_sep_str[0]; |
| | 1216 | |
| | 1217 | if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str; |
| | 1218 | else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str; |
| | 1219 | else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str; |
| | 1220 | else { |
| | 1221 | av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str); |
| | 1222 | return AVERROR(EINVAL); |
| | 1223 | } |
| | 1224 | |
| | 1225 | return 0; |
| | 1226 | } |
| | 1227 | |
| | 1228 | static void compact_print_section_header(WriterContext *wctx, void *data) |
| | 1229 | { |
| | 1230 | CompactContext *compact = wctx->priv; |
| | 1231 | const struct section *section = wctx->section[wctx->level]; |
| | 1232 | const struct section *parent_section = wctx->level ? |
| | 1233 | wctx->section[wctx->level-1] : NULL; |
| | 1234 | compact->terminate_line[wctx->level] = 1; |
| | 1235 | compact->has_nested_elems[wctx->level] = 0; |
| | 1236 | |
| | 1237 | av_bprint_clear(&wctx->section_pbuf[wctx->level]); |
| | 1238 | if (parent_section && |
| | 1239 | (section->flags & SECTION_FLAG_HAS_TYPE || |
| | 1240 | (!(section->flags & SECTION_FLAG_IS_ARRAY) && |
| | 1241 | !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))))) { |
| | 1242 | |
| | 1243 | /* define a prefix for elements not contained in an array or |
| | 1244 | in a wrapper, or for array elements with a type */ |
| | 1245 | const char *element_name = (char *)av_x_if_null(section->element_name, section->name); |
| | 1246 | AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level]; |
| | 1247 | |
| | 1248 | compact->nested_section[wctx->level] = 1; |
| | 1249 | compact->has_nested_elems[wctx->level-1] = 1; |
| | 1250 | |
| | 1251 | av_bprintf(section_pbuf, "%s%s", |
| | 1252 | wctx->section_pbuf[wctx->level-1].str, element_name); |
| | 1253 | |
| | 1254 | if (section->flags & SECTION_FLAG_HAS_TYPE) { |
| | 1255 | // add /TYPE to prefix |
| | 1256 | av_bprint_chars(section_pbuf, '/', 1); |
| | 1257 | |
| | 1258 | // normalize section type, replace special characters and lower case |
| | 1259 | for (const char *p = section->get_type(data); *p; p++) { |
| | 1260 | char c = |
| | 1261 | (*p >= '0' && *p <= '9') || |
| | 1262 | (*p >= 'a' && *p <= 'z') || |
| | 1263 | (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_'; |
| | 1264 | av_bprint_chars(section_pbuf, c, 1); |
| | 1265 | } |
| | 1266 | } |
| | 1267 | av_bprint_chars(section_pbuf, ':', 1); |
| | 1268 | |
| | 1269 | wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1]; |
| | 1270 | } else { |
| | 1271 | if (parent_section && !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)) && |
| | 1272 | wctx->level && wctx->nb_item[wctx->level-1]) |
| | 1273 | writer_w8(wctx, compact->item_sep); |
| | 1274 | if (compact->print_section && |
| | 1275 | !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) |
| | 1276 | writer_printf(wctx, "%s%c", section->name, compact->item_sep); |
| | 1277 | } |
| | 1278 | } |
| | 1279 | |
| | 1280 | static void compact_print_section_footer(WriterContext *wctx) |
| | 1281 | { |
| | 1282 | CompactContext *compact = wctx->priv; |
| | 1283 | |
| | 1284 | if (!compact->nested_section[wctx->level] && |
| | 1285 | compact->terminate_line[wctx->level] && |
| | 1286 | !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) |
| | 1287 | writer_w8(wctx, '\n'); |
| | 1288 | } |
| | 1289 | |
| | 1290 | static void compact_print_str(WriterContext *wctx, const char *key, const char *value) |
| | 1291 | { |
| | 1292 | CompactContext *compact = wctx->priv; |
| | 1293 | AVBPrint buf; |
| | 1294 | |
| | 1295 | if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); |
| | 1296 | if (!compact->nokey) |
| | 1297 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); |
| | 1298 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1299 | writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx)); |
| | 1300 | av_bprint_finalize(&buf, NULL); |
| | 1301 | } |
| | 1302 | |
| | 1303 | static void compact_print_int(WriterContext *wctx, const char *key, long long int value) |
| | 1304 | { |
| | 1305 | CompactContext *compact = wctx->priv; |
| | 1306 | |
| | 1307 | if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); |
| | 1308 | if (!compact->nokey) |
| | 1309 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); |
| | 1310 | writer_printf(wctx, "%lld", value); |
| | 1311 | } |
| | 1312 | |
| | 1313 | static const Writer compact_writer = { |
| | 1314 | .name = "compact", |
| | 1315 | .priv_size = sizeof(CompactContext), |
| | 1316 | .init = compact_init, |
| | 1317 | .print_section_header = compact_print_section_header, |
| | 1318 | .print_section_footer = compact_print_section_footer, |
| | 1319 | .print_integer = compact_print_int, |
| | 1320 | .print_string = compact_print_str, |
| | 1321 | .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, |
| | 1322 | .priv_class = &compact_class, |
| | 1323 | }; |
| | 1324 | |
| | 1325 | /* CSV output */ |
| | 1326 | |
| | 1327 | #undef OFFSET |
| | 1328 | #define OFFSET(x) offsetof(CompactContext, x) |
| | 1329 | |
| | 1330 | static const AVOption csv_options[] = { |
| | 1331 | {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, |
| | 1332 | {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, |
| | 1333 | {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1334 | {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1335 | {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, |
| | 1336 | {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, |
| | 1337 | {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1338 | {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1339 | {NULL}, |
| | 1340 | }; |
| | 1341 | |
| | 1342 | DEFINE_WRITER_CLASS(csv); |
| | 1343 | |
| | 1344 | static const Writer csv_writer = { |
| | 1345 | .name = "csv", |
| | 1346 | .priv_size = sizeof(CompactContext), |
| | 1347 | .init = compact_init, |
| | 1348 | .print_section_header = compact_print_section_header, |
| | 1349 | .print_section_footer = compact_print_section_footer, |
| | 1350 | .print_integer = compact_print_int, |
| | 1351 | .print_string = compact_print_str, |
| | 1352 | .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, |
| | 1353 | .priv_class = &csv_class, |
| | 1354 | }; |
| | 1355 | |
| | 1356 | /* Flat output */ |
| | 1357 | |
| | 1358 | typedef struct FlatContext { |
| | 1359 | const AVClass *class; |
| | 1360 | const char *sep_str; |
| | 1361 | char sep; |
| | 1362 | int hierarchical; |
| | 1363 | } FlatContext; |
| | 1364 | |
| | 1365 | #undef OFFSET |
| | 1366 | #define OFFSET(x) offsetof(FlatContext, x) |
| | 1367 | |
| | 1368 | static const AVOption flat_options[]= { |
| | 1369 | {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, |
| | 1370 | {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, |
| | 1371 | {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1372 | {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1373 | {NULL}, |
| | 1374 | }; |
| | 1375 | |
| | 1376 | DEFINE_WRITER_CLASS(flat); |
| | 1377 | |
| | 1378 | static av_cold int flat_init(WriterContext *wctx) |
| | 1379 | { |
| | 1380 | FlatContext *flat = wctx->priv; |
| | 1381 | |
| | 1382 | if (strlen(flat->sep_str) != 1) { |
| | 1383 | av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", |
| | 1384 | flat->sep_str); |
| | 1385 | return AVERROR(EINVAL); |
| | 1386 | } |
| | 1387 | flat->sep = flat->sep_str[0]; |
| | 1388 | |
| | 1389 | return 0; |
| | 1390 | } |
| | 1391 | |
| | 1392 | static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep) |
| | 1393 | { |
| | 1394 | const char *p; |
| | 1395 | |
| | 1396 | for (p = src; *p; p++) { |
| | 1397 | if (!((*p >= '0' && *p <= '9') || |
| | 1398 | (*p >= 'a' && *p <= 'z') || |
| | 1399 | (*p >= 'A' && *p <= 'Z'))) |
| | 1400 | av_bprint_chars(dst, '_', 1); |
| | 1401 | else |
| | 1402 | av_bprint_chars(dst, *p, 1); |
| | 1403 | } |
| | 1404 | return dst->str; |
| | 1405 | } |
| | 1406 | |
| | 1407 | static const char *flat_escape_value_str(AVBPrint *dst, const char *src) |
| | 1408 | { |
| | 1409 | const char *p; |
| | 1410 | |
| | 1411 | for (p = src; *p; p++) { |
| | 1412 | switch (*p) { |
| | 1413 | case '\n': av_bprintf(dst, "%s", "\\n"); break; |
| | 1414 | case '\r': av_bprintf(dst, "%s", "\\r"); break; |
| | 1415 | case '\\': av_bprintf(dst, "%s", "\\\\"); break; |
| | 1416 | case '"': av_bprintf(dst, "%s", "\\\""); break; |
| | 1417 | case '`': av_bprintf(dst, "%s", "\\`"); break; |
| | 1418 | case '$': av_bprintf(dst, "%s", "\\$"); break; |
| | 1419 | default: av_bprint_chars(dst, *p, 1); break; |
| | 1420 | } |
| | 1421 | } |
| | 1422 | return dst->str; |
| | 1423 | } |
| | 1424 | |
| | 1425 | static void flat_print_section_header(WriterContext *wctx, void *data) |
| | 1426 | { |
| | 1427 | FlatContext *flat = wctx->priv; |
| | 1428 | AVBPrint *buf = &wctx->section_pbuf[wctx->level]; |
| | 1429 | const struct section *section = wctx->section[wctx->level]; |
| | 1430 | const struct section *parent_section = wctx->level ? |
| | 1431 | wctx->section[wctx->level-1] : NULL; |
| | 1432 | |
| | 1433 | /* build section header */ |
| | 1434 | av_bprint_clear(buf); |
| | 1435 | if (!parent_section) |
| | 1436 | return; |
| | 1437 | av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str); |
| | 1438 | |
| | 1439 | if (flat->hierarchical || |
| | 1440 | !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) { |
| | 1441 | av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str); |
| | 1442 | |
| | 1443 | if (parent_section->flags & SECTION_FLAG_IS_ARRAY) { |
| | 1444 | int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ? |
| | 1445 | wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1]; |
| | 1446 | av_bprintf(buf, "%d%s", n, flat->sep_str); |
| | 1447 | } |
| | 1448 | } |
| | 1449 | } |
| | 1450 | |
| | 1451 | static void flat_print_int(WriterContext *wctx, const char *key, long long int value) |
| | 1452 | { |
| | 1453 | writer_printf(wctx, "%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value); |
| | 1454 | } |
| | 1455 | |
| | 1456 | static void flat_print_str(WriterContext *wctx, const char *key, const char *value) |
| | 1457 | { |
| | 1458 | FlatContext *flat = wctx->priv; |
| | 1459 | AVBPrint buf; |
| | 1460 | |
| | 1461 | writer_put_str(wctx, wctx->section_pbuf[wctx->level].str); |
| | 1462 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1463 | writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep)); |
| | 1464 | av_bprint_clear(&buf); |
| | 1465 | writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value)); |
| | 1466 | av_bprint_finalize(&buf, NULL); |
| | 1467 | } |
| | 1468 | |
| | 1469 | static const Writer flat_writer = { |
| | 1470 | .name = "flat", |
| | 1471 | .priv_size = sizeof(FlatContext), |
| | 1472 | .init = flat_init, |
| | 1473 | .print_section_header = flat_print_section_header, |
| | 1474 | .print_integer = flat_print_int, |
| | 1475 | .print_string = flat_print_str, |
| | 1476 | .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, |
| | 1477 | .priv_class = &flat_class, |
| | 1478 | }; |
| | 1479 | |
| | 1480 | /* INI format output */ |
| | 1481 | |
| | 1482 | typedef struct INIContext { |
| | 1483 | const AVClass *class; |
| | 1484 | int hierarchical; |
| | 1485 | } INIContext; |
| | 1486 | |
| | 1487 | #undef OFFSET |
| | 1488 | #define OFFSET(x) offsetof(INIContext, x) |
| | 1489 | |
| | 1490 | static const AVOption ini_options[] = { |
| | 1491 | {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1492 | {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, |
| | 1493 | {NULL}, |
| | 1494 | }; |
| | 1495 | |
| | 1496 | DEFINE_WRITER_CLASS(ini); |
| | 1497 | |
| | 1498 | static char *ini_escape_str(AVBPrint *dst, const char *src) |
| | 1499 | { |
| | 1500 | int i = 0; |
| | 1501 | char c = 0; |
| | 1502 | |
| | 1503 | while (c = src[i++]) { |
| | 1504 | switch (c) { |
| | 1505 | case '\b': av_bprintf(dst, "%s", "\\b"); break; |
| | 1506 | case '\f': av_bprintf(dst, "%s", "\\f"); break; |
| | 1507 | case '\n': av_bprintf(dst, "%s", "\\n"); break; |
| | 1508 | case '\r': av_bprintf(dst, "%s", "\\r"); break; |
| | 1509 | case '\t': av_bprintf(dst, "%s", "\\t"); break; |
| | 1510 | case '\\': |
| | 1511 | case '#' : |
| | 1512 | case '=' : |
| | 1513 | case ':' : av_bprint_chars(dst, '\\', 1); |
| | 1514 | default: |
| | 1515 | if ((unsigned char)c < 32) |
| | 1516 | av_bprintf(dst, "\\x00%02x", c & 0xff); |
| | 1517 | else |
| | 1518 | av_bprint_chars(dst, c, 1); |
| | 1519 | break; |
| | 1520 | } |
| | 1521 | } |
| | 1522 | return dst->str; |
| | 1523 | } |
| | 1524 | |
| | 1525 | static void ini_print_section_header(WriterContext *wctx, void *data) |
| | 1526 | { |
| | 1527 | INIContext *ini = wctx->priv; |
| | 1528 | AVBPrint *buf = &wctx->section_pbuf[wctx->level]; |
| | 1529 | const struct section *section = wctx->section[wctx->level]; |
| | 1530 | const struct section *parent_section = wctx->level ? |
| | 1531 | wctx->section[wctx->level-1] : NULL; |
| | 1532 | |
| | 1533 | av_bprint_clear(buf); |
| | 1534 | if (!parent_section) { |
| | 1535 | writer_put_str(wctx, "# ffprobe output\n\n"); |
| | 1536 | return; |
| | 1537 | } |
| | 1538 | |
| | 1539 | if (wctx->nb_item[wctx->level-1]) |
| | 1540 | writer_w8(wctx, '\n'); |
| | 1541 | |
| | 1542 | av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str); |
| | 1543 | if (ini->hierarchical || |
| | 1544 | !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) { |
| | 1545 | av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name); |
| | 1546 | |
| | 1547 | if (parent_section->flags & SECTION_FLAG_IS_ARRAY) { |
| | 1548 | int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ? |
| | 1549 | wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1]; |
| | 1550 | av_bprintf(buf, ".%d", n); |
| | 1551 | } |
| | 1552 | } |
| | 1553 | |
| | 1554 | if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) |
| | 1555 | writer_printf(wctx, "[%s]\n", buf->str); |
| | 1556 | } |
| | 1557 | |
| | 1558 | static void ini_print_str(WriterContext *wctx, const char *key, const char *value) |
| | 1559 | { |
| | 1560 | AVBPrint buf; |
| | 1561 | |
| | 1562 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1563 | writer_printf(wctx, "%s=", ini_escape_str(&buf, key)); |
| | 1564 | av_bprint_clear(&buf); |
| | 1565 | writer_printf(wctx, "%s\n", ini_escape_str(&buf, value)); |
| | 1566 | av_bprint_finalize(&buf, NULL); |
| | 1567 | } |
| | 1568 | |
| | 1569 | static void ini_print_int(WriterContext *wctx, const char *key, long long int value) |
| | 1570 | { |
| | 1571 | writer_printf(wctx, "%s=%lld\n", key, value); |
| | 1572 | } |
| | 1573 | |
| | 1574 | static const Writer ini_writer = { |
| | 1575 | .name = "ini", |
| | 1576 | .priv_size = sizeof(INIContext), |
| | 1577 | .print_section_header = ini_print_section_header, |
| | 1578 | .print_integer = ini_print_int, |
| | 1579 | .print_string = ini_print_str, |
| | 1580 | .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, |
| | 1581 | .priv_class = &ini_class, |
| | 1582 | }; |
| | 1583 | |
| | 1584 | /* JSON output */ |
| | 1585 | |
| | 1586 | typedef struct JSONContext { |
| | 1587 | const AVClass *class; |
| | 1588 | int indent_level; |
| | 1589 | int compact; |
| | 1590 | const char *item_sep, *item_start_end; |
| | 1591 | } JSONContext; |
| | 1592 | |
| | 1593 | #undef OFFSET |
| | 1594 | #define OFFSET(x) offsetof(JSONContext, x) |
| | 1595 | |
| | 1596 | static const AVOption json_options[]= { |
| | 1597 | { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1598 | { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1599 | { NULL } |
| | 1600 | }; |
| | 1601 | |
| | 1602 | DEFINE_WRITER_CLASS(json); |
| | 1603 | |
| | 1604 | static av_cold int json_init(WriterContext *wctx) |
| | 1605 | { |
| | 1606 | JSONContext *json = wctx->priv; |
| | 1607 | |
| | 1608 | json->item_sep = json->compact ? ", " : ",\n"; |
| | 1609 | json->item_start_end = json->compact ? " " : "\n"; |
| | 1610 | |
| | 1611 | return 0; |
| | 1612 | } |
| | 1613 | |
| | 1614 | static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx) |
| | 1615 | { |
| | 1616 | static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0}; |
| | 1617 | static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0}; |
| | 1618 | const char *p; |
| | 1619 | |
| | 1620 | for (p = src; *p; p++) { |
| | 1621 | char *s = strchr(json_escape, *p); |
| | 1622 | if (s) { |
| | 1623 | av_bprint_chars(dst, '\\', 1); |
| | 1624 | av_bprint_chars(dst, json_subst[s - json_escape], 1); |
| | 1625 | } else if ((unsigned char)*p < 32) { |
| | 1626 | av_bprintf(dst, "\\u00%02x", *p & 0xff); |
| | 1627 | } else { |
| | 1628 | av_bprint_chars(dst, *p, 1); |
| | 1629 | } |
| | 1630 | } |
| | 1631 | return dst->str; |
| | 1632 | } |
| | 1633 | |
| | 1634 | #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ') |
| | 1635 | |
| | 1636 | static void json_print_section_header(WriterContext *wctx, void *data) |
| | 1637 | { |
| | 1638 | JSONContext *json = wctx->priv; |
| | 1639 | AVBPrint buf; |
| | 1640 | const struct section *section = wctx->section[wctx->level]; |
| | 1641 | const struct section *parent_section = wctx->level ? |
| | 1642 | wctx->section[wctx->level-1] : NULL; |
| | 1643 | |
| | 1644 | if (wctx->level && wctx->nb_item[wctx->level-1]) |
| | 1645 | writer_put_str(wctx, ",\n"); |
| | 1646 | |
| | 1647 | if (section->flags & SECTION_FLAG_IS_WRAPPER) { |
| | 1648 | writer_put_str(wctx, "{\n"); |
| | 1649 | json->indent_level++; |
| | 1650 | } else { |
| | 1651 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1652 | json_escape_str(&buf, section->name, wctx); |
| | 1653 | JSON_INDENT(); |
| | 1654 | |
| | 1655 | json->indent_level++; |
| | 1656 | if (section->flags & SECTION_FLAG_IS_ARRAY) { |
| | 1657 | writer_printf(wctx, "\"%s\": [\n", buf.str); |
| | 1658 | } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) { |
| | 1659 | writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end); |
| | 1660 | } else { |
| | 1661 | writer_printf(wctx, "{%s", json->item_start_end); |
| | 1662 | |
| | 1663 | /* this is required so the parser can distinguish between packets and frames */ |
| | 1664 | if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) { |
| | 1665 | if (!json->compact) |
| | 1666 | JSON_INDENT(); |
| | 1667 | writer_printf(wctx, "\"type\": \"%s\"", section->name); |
| | 1668 | wctx->nb_item[wctx->level]++; |
| | 1669 | } |
| | 1670 | } |
| | 1671 | av_bprint_finalize(&buf, NULL); |
| | 1672 | } |
| | 1673 | } |
| | 1674 | |
| | 1675 | static void json_print_section_footer(WriterContext *wctx) |
| | 1676 | { |
| | 1677 | JSONContext *json = wctx->priv; |
| | 1678 | const struct section *section = wctx->section[wctx->level]; |
| | 1679 | |
| | 1680 | if (wctx->level == 0) { |
| | 1681 | json->indent_level--; |
| | 1682 | writer_put_str(wctx, "\n}\n"); |
| | 1683 | } else if (section->flags & SECTION_FLAG_IS_ARRAY) { |
| | 1684 | writer_w8(wctx, '\n'); |
| | 1685 | json->indent_level--; |
| | 1686 | JSON_INDENT(); |
| | 1687 | writer_w8(wctx, ']'); |
| | 1688 | } else { |
| | 1689 | writer_put_str(wctx, json->item_start_end); |
| | 1690 | json->indent_level--; |
| | 1691 | if (!json->compact) |
| | 1692 | JSON_INDENT(); |
| | 1693 | writer_w8(wctx, '}'); |
| | 1694 | } |
| | 1695 | } |
| | 1696 | |
| | 1697 | static inline void json_print_item_str(WriterContext *wctx, |
| | 1698 | const char *key, const char *value) |
| | 1699 | { |
| | 1700 | AVBPrint buf; |
| | 1701 | |
| | 1702 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1703 | writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx)); |
| | 1704 | av_bprint_clear(&buf); |
| | 1705 | writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx)); |
| | 1706 | av_bprint_finalize(&buf, NULL); |
| | 1707 | } |
| | 1708 | |
| | 1709 | static void json_print_str(WriterContext *wctx, const char *key, const char *value) |
| | 1710 | { |
| | 1711 | JSONContext *json = wctx->priv; |
| | 1712 | const struct section *parent_section = wctx->level ? |
| | 1713 | wctx->section[wctx->level-1] : NULL; |
| | 1714 | |
| | 1715 | if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) |
| | 1716 | writer_put_str(wctx, json->item_sep); |
| | 1717 | if (!json->compact) |
| | 1718 | JSON_INDENT(); |
| | 1719 | json_print_item_str(wctx, key, value); |
| | 1720 | } |
| | 1721 | |
| | 1722 | static void json_print_int(WriterContext *wctx, const char *key, long long int value) |
| | 1723 | { |
| | 1724 | JSONContext *json = wctx->priv; |
| | 1725 | const struct section *parent_section = wctx->level ? |
| | 1726 | wctx->section[wctx->level-1] : NULL; |
| | 1727 | AVBPrint buf; |
| | 1728 | |
| | 1729 | if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) |
| | 1730 | writer_put_str(wctx, json->item_sep); |
| | 1731 | if (!json->compact) |
| | 1732 | JSON_INDENT(); |
| | 1733 | |
| | 1734 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1735 | writer_printf(wctx, "\"%s\": %lld", json_escape_str(&buf, key, wctx), value); |
| | 1736 | av_bprint_finalize(&buf, NULL); |
| | 1737 | } |
| | 1738 | |
| | 1739 | static const Writer json_writer = { |
| | 1740 | .name = "json", |
| | 1741 | .priv_size = sizeof(JSONContext), |
| | 1742 | .init = json_init, |
| | 1743 | .print_section_header = json_print_section_header, |
| | 1744 | .print_section_footer = json_print_section_footer, |
| | 1745 | .print_integer = json_print_int, |
| | 1746 | .print_string = json_print_str, |
| | 1747 | .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, |
| | 1748 | .priv_class = &json_class, |
| | 1749 | }; |
| | 1750 | |
| | 1751 | /* XML output */ |
| | 1752 | |
| | 1753 | typedef struct XMLContext { |
| | 1754 | const AVClass *class; |
| | 1755 | int within_tag; |
| | 1756 | int indent_level; |
| | 1757 | int fully_qualified; |
| | 1758 | int xsd_strict; |
| | 1759 | } XMLContext; |
| | 1760 | |
| | 1761 | #undef OFFSET |
| | 1762 | #define OFFSET(x) offsetof(XMLContext, x) |
| | 1763 | |
| | 1764 | static const AVOption xml_options[] = { |
| | 1765 | {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1766 | {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1767 | {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1768 | {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, |
| | 1769 | {NULL}, |
| | 1770 | }; |
| | 1771 | |
| | 1772 | DEFINE_WRITER_CLASS(xml); |
| | 1773 | |
| | 1774 | static av_cold int xml_init(WriterContext *wctx) |
| | 1775 | { |
| | 1776 | XMLContext *xml = wctx->priv; |
| | 1777 | |
| | 1778 | if (xml->xsd_strict) { |
| | 1779 | xml->fully_qualified = 1; |
| | 1780 | #define CHECK_COMPLIANCE(opt, opt_name) \ |
| | 1781 | if (opt) { \ |
| | 1782 | av_log(wctx, AV_LOG_ERROR, \ |
| | 1783 | "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \ |
| | 1784 | "You need to disable such option with '-no%s'\n", opt_name, opt_name); \ |
| | 1785 | return AVERROR(EINVAL); \ |
| | 1786 | } |
| | 1787 | CHECK_COMPLIANCE(show_private_data, "private"); |
| | 1788 | CHECK_COMPLIANCE(show_value_unit, "unit"); |
| | 1789 | CHECK_COMPLIANCE(use_value_prefix, "prefix"); |
| | 1790 | } |
| | 1791 | |
| | 1792 | return 0; |
| | 1793 | } |
| | 1794 | |
| | 1795 | #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ') |
| | 1796 | |
| | 1797 | static void xml_print_section_header(WriterContext *wctx, void *data) |
| | 1798 | { |
| | 1799 | XMLContext *xml = wctx->priv; |
| | 1800 | const struct section *section = wctx->section[wctx->level]; |
| | 1801 | const struct section *parent_section = wctx->level ? |
| | 1802 | wctx->section[wctx->level-1] : NULL; |
| | 1803 | |
| | 1804 | if (wctx->level == 0) { |
| | 1805 | const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " |
| | 1806 | "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" " |
| | 1807 | "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\""; |
| | 1808 | |
| | 1809 | writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| | 1810 | writer_printf(wctx, "<%sffprobe%s>\n", |
| | 1811 | xml->fully_qualified ? "ffprobe:" : "", |
| | 1812 | xml->fully_qualified ? qual : ""); |
| | 1813 | return; |
| | 1814 | } |
| | 1815 | |
| | 1816 | if (xml->within_tag) { |
| | 1817 | xml->within_tag = 0; |
| | 1818 | writer_put_str(wctx, ">\n"); |
| | 1819 | } |
| | 1820 | |
| | 1821 | if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) && |
| | 1822 | wctx->level && wctx->nb_item[wctx->level-1]) |
| | 1823 | writer_w8(wctx, '\n'); |
| | 1824 | xml->indent_level++; |
| | 1825 | |
| | 1826 | if (section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_HAS_VARIABLE_FIELDS)) { |
| | 1827 | XML_INDENT(); writer_printf(wctx, "<%s", section->name); |
| | 1828 | |
| | 1829 | if (section->flags & SECTION_FLAG_HAS_TYPE) { |
| | 1830 | AVBPrint buf; |
| | 1831 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1832 | av_bprint_escape(&buf, section->get_type(data), NULL, |
| | 1833 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); |
| | 1834 | writer_printf(wctx, " type=\"%s\"", buf.str); |
| | 1835 | } |
| | 1836 | writer_printf(wctx, ">\n", section->name); |
| | 1837 | } else { |
| | 1838 | XML_INDENT(); writer_printf(wctx, "<%s ", section->name); |
| | 1839 | xml->within_tag = 1; |
| | 1840 | } |
| | 1841 | } |
| | 1842 | |
| | 1843 | static void xml_print_section_footer(WriterContext *wctx) |
| | 1844 | { |
| | 1845 | XMLContext *xml = wctx->priv; |
| | 1846 | const struct section *section = wctx->section[wctx->level]; |
| | 1847 | |
| | 1848 | if (wctx->level == 0) { |
| | 1849 | writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : ""); |
| | 1850 | } else if (xml->within_tag) { |
| | 1851 | xml->within_tag = 0; |
| | 1852 | writer_put_str(wctx, "/>\n"); |
| | 1853 | xml->indent_level--; |
| | 1854 | } else { |
| | 1855 | XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name); |
| | 1856 | xml->indent_level--; |
| | 1857 | } |
| | 1858 | } |
| | 1859 | |
| | 1860 | static void xml_print_value(WriterContext *wctx, const char *key, const void *value, const int is_int) |
| | 1861 | { |
| | 1862 | AVBPrint buf; |
| | 1863 | XMLContext *xml = wctx->priv; |
| | 1864 | const struct section *section = wctx->section[wctx->level]; |
| | 1865 | |
| | 1866 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 1867 | |
| | 1868 | if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { |
| | 1869 | xml->indent_level++; |
| | 1870 | XML_INDENT(); |
| | 1871 | av_bprint_escape(&buf, key, NULL, |
| | 1872 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); |
| | 1873 | writer_printf(wctx, "<%s key=\"%s\"", |
| | 1874 | section->element_name, buf.str); |
| | 1875 | av_bprint_clear(&buf); |
| | 1876 | |
| | 1877 | if (is_int) { |
| | 1878 | writer_printf(wctx, " value=\"%lld\"/>\n", *(long long int *)value); |
| | 1879 | } else { |
| | 1880 | av_bprint_escape(&buf, (const char *)value, NULL, |
| | 1881 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); |
| | 1882 | writer_printf(wctx, " value=\"%s\"/>\n", buf.str); |
| | 1883 | } |
| | 1884 | xml->indent_level--; |
| | 1885 | } else { |
| | 1886 | if (wctx->nb_item[wctx->level]) |
| | 1887 | writer_w8(wctx, ' '); |
| | 1888 | |
| | 1889 | if (is_int) { |
| | 1890 | writer_printf(wctx, "%s=\"%lld\"", key, *(long long int *)value); |
| | 1891 | } else { |
| | 1892 | av_bprint_escape(&buf, (const char *)value, NULL, |
| | 1893 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); |
| | 1894 | writer_printf(wctx, "%s=\"%s\"", key, buf.str); |
| | 1895 | } |
| | 1896 | } |
| | 1897 | |
| | 1898 | av_bprint_finalize(&buf, NULL); |
| | 1899 | } |
| | 1900 | |
| | 1901 | static inline void xml_print_str(WriterContext *wctx, const char *key, const char *value) { |
| | 1902 | xml_print_value(wctx, key, (const void *)value, 0); |
| | 1903 | } |
| | 1904 | |
| | 1905 | static inline void xml_print_int(WriterContext *wctx, const char *key, long long int value) { |
| | 1906 | xml_print_value(wctx, key, (const void *)&value, 1); |
| | 1907 | } |
| | 1908 | |
| | 1909 | static Writer xml_writer = { |
| | 1910 | .name = "xml", |
| | 1911 | .priv_size = sizeof(XMLContext), |
| | 1912 | .init = xml_init, |
| | 1913 | .print_section_header = xml_print_section_header, |
| | 1914 | .print_section_footer = xml_print_section_footer, |
| | 1915 | .print_integer = xml_print_int, |
| | 1916 | .print_string = xml_print_str, |
| | 1917 | .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, |
| | 1918 | .priv_class = &xml_class, |
| | 1919 | }; |
| | 1920 | |
| | 1921 | static void writer_register_all(void) |
| | 1922 | { |
| | 1923 | static int initialized; |
| | 1924 | |
| | 1925 | if (initialized) |
| | 1926 | return; |
| | 1927 | initialized = 1; |
| | 1928 | |
| | 1929 | writer_register(&default_writer); |
| | 1930 | writer_register(&compact_writer); |
| | 1931 | writer_register(&csv_writer); |
| | 1932 | writer_register(&flat_writer); |
| | 1933 | writer_register(&ini_writer); |
| | 1934 | writer_register(&json_writer); |
| | 1935 | writer_register(&xml_writer); |
| | 1936 | } |
| | 1937 | |
| | 1938 | #define print_fmt(k, f, ...) do { \ |
| | 1939 | av_bprint_clear(&pbuf); \ |
| | 1940 | av_bprintf(&pbuf, f, __VA_ARGS__); \ |
| | 1941 | writer_print_string(w, k, pbuf.str, 0); \ |
| | 1942 | } while (0) |
| | 1943 | |
| | 1944 | #define print_list_fmt(k, f, n, m, ...) do { \ |
| | 1945 | av_bprint_clear(&pbuf); \ |
| | 1946 | for (int idx = 0; idx < n; idx++) { \ |
| | 1947 | for (int idx2 = 0; idx2 < m; idx2++) { \ |
| | 1948 | if (idx > 0 || idx2 > 0) \ |
| | 1949 | av_bprint_chars(&pbuf, ' ', 1); \ |
| | 1950 | av_bprintf(&pbuf, f, __VA_ARGS__); \ |
| | 1951 | } \ |
| | 1952 | } \ |
| | 1953 | writer_print_string(w, k, pbuf.str, 0); \ |
| | 1954 | } while (0) |
| | 1955 | |
| | 1956 | #define print_int(k, v) writer_print_integer(w, k, v) |
| | 1957 | #define print_q(k, v, s) writer_print_rational(w, k, v, s) |
| | 1958 | #define print_str(k, v) writer_print_string(w, k, v, 0) |
| | 1959 | #define print_str_opt(k, v) writer_print_string(w, k, v, PRINT_STRING_OPT) |
| | 1960 | #define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE) |
| | 1961 | #define print_time(k, v, tb) writer_print_time(w, k, v, tb, 0) |
| | 1962 | #define print_ts(k, v) writer_print_ts(w, k, v, 0) |
| | 1963 | #define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1) |
| | 1964 | #define print_duration_ts(k, v) writer_print_ts(w, k, v, 1) |
| | 1965 | #define print_val(k, v, u) do { \ |
| | 1966 | struct unit_value uv; \ |
| | 1967 | uv.val.i = v; \ |
| | 1968 | uv.unit = u; \ |
| | 1969 | writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \ |
| | 1970 | } while (0) |
| | 1971 | |
| | 1972 | #define print_section_header(s) writer_print_section_header(w, NULL, s) |
| | 1973 | #define print_section_header_data(s, d) writer_print_section_header(w, d, s) |
| | 1974 | #define print_section_footer(s) writer_print_section_footer(w, s) |
| | 1975 | |
| | 1976 | #define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n) \ |
| | 1977 | { \ |
| | 1978 | ret = av_reallocp_array(&(ptr), (new_n), sizeof(*(ptr))); \ |
| | 1979 | if (ret < 0) \ |
| | 1980 | goto end; \ |
| | 1981 | memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \ |
| | 1982 | } |
| | 1983 | |
| | 1984 | static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id) |
| | 1985 | { |
| | 1986 | const AVDictionaryEntry *tag = NULL; |
| | 1987 | int ret = 0; |
| | 1988 | |
| | 1989 | if (!tags) |
| | 1990 | return 0; |
| | 1991 | writer_print_section_header(w, NULL, section_id); |
| | 1992 | |
| | 1993 | while ((tag = av_dict_iterate(tags, tag))) { |
| | 1994 | if ((ret = print_str_validate(tag->key, tag->value)) < 0) |
| | 1995 | break; |
| | 1996 | } |
| | 1997 | writer_print_section_footer(w); |
| | 1998 | |
| | 1999 | return ret; |
| | 2000 | } |
| | 2001 | |
| | 2002 | static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi) |
| | 2003 | { |
| | 2004 | if (!dovi) |
| | 2005 | return; |
| | 2006 | |
| | 2007 | { |
| | 2008 | const AVDOVIRpuDataHeader *hdr = av_dovi_get_header(dovi); |
| | 2009 | const AVDOVIDataMapping *mapping = av_dovi_get_mapping(dovi); |
| | 2010 | const AVDOVIColorMetadata *color = av_dovi_get_color(dovi); |
| | 2011 | AVBPrint pbuf; |
| | 2012 | |
| | 2013 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 2014 | |
| | 2015 | // header |
| | 2016 | print_int("rpu_type", hdr->rpu_type); |
| | 2017 | print_int("rpu_format", hdr->rpu_format); |
| | 2018 | print_int("vdr_rpu_profile", hdr->vdr_rpu_profile); |
| | 2019 | print_int("vdr_rpu_level", hdr->vdr_rpu_level); |
| | 2020 | print_int("chroma_resampling_explicit_filter_flag", |
| | 2021 | hdr->chroma_resampling_explicit_filter_flag); |
| | 2022 | print_int("coef_data_type", hdr->coef_data_type); |
| | 2023 | print_int("coef_log2_denom", hdr->coef_log2_denom); |
| | 2024 | print_int("vdr_rpu_normalized_idc", hdr->vdr_rpu_normalized_idc); |
| | 2025 | print_int("bl_video_full_range_flag", hdr->bl_video_full_range_flag); |
| | 2026 | print_int("bl_bit_depth", hdr->bl_bit_depth); |
| | 2027 | print_int("el_bit_depth", hdr->el_bit_depth); |
| | 2028 | print_int("vdr_bit_depth", hdr->vdr_bit_depth); |
| | 2029 | print_int("spatial_resampling_filter_flag", |
| | 2030 | hdr->spatial_resampling_filter_flag); |
| | 2031 | print_int("el_spatial_resampling_filter_flag", |
| | 2032 | hdr->el_spatial_resampling_filter_flag); |
| | 2033 | print_int("disable_residual_flag", hdr->disable_residual_flag); |
| | 2034 | |
| | 2035 | // data mapping values |
| | 2036 | print_int("vdr_rpu_id", mapping->vdr_rpu_id); |
| | 2037 | print_int("mapping_color_space", mapping->mapping_color_space); |
| | 2038 | print_int("mapping_chroma_format_idc", |
| | 2039 | mapping->mapping_chroma_format_idc); |
| | 2040 | |
| | 2041 | print_int("nlq_method_idc", mapping->nlq_method_idc); |
| | 2042 | switch (mapping->nlq_method_idc) { |
| | 2043 | case AV_DOVI_NLQ_NONE: |
| | 2044 | print_str("nlq_method_idc_name", "none"); |
| | 2045 | break; |
| | 2046 | case AV_DOVI_NLQ_LINEAR_DZ: |
| | 2047 | print_str("nlq_method_idc_name", "linear_dz"); |
| | 2048 | break; |
| | 2049 | default: |
| | 2050 | print_str("nlq_method_idc_name", "unknown"); |
| | 2051 | break; |
| | 2052 | } |
| | 2053 | |
| | 2054 | print_int("num_x_partitions", mapping->num_x_partitions); |
| | 2055 | print_int("num_y_partitions", mapping->num_y_partitions); |
| | 2056 | |
| | 2057 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST); |
| | 2058 | |
| | 2059 | for (int c = 0; c < 3; c++) { |
| | 2060 | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; |
| | 2061 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT); |
| | 2062 | |
| | 2063 | print_list_fmt("pivots", "%"PRIu16, curve->num_pivots, 1, curve->pivots[idx]); |
| | 2064 | |
| | 2065 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST); |
| | 2066 | for (int i = 0; i < curve->num_pivots - 1; i++) { |
| | 2067 | |
| | 2068 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE); |
| | 2069 | print_int("mapping_idc", curve->mapping_idc[i]); |
| | 2070 | switch (curve->mapping_idc[i]) { |
| | 2071 | case AV_DOVI_MAPPING_POLYNOMIAL: |
| | 2072 | print_str("mapping_idc_name", "polynomial"); |
| | 2073 | print_int("poly_order", curve->poly_order[i]); |
| | 2074 | print_list_fmt("poly_coef", "%"PRIi64, |
| | 2075 | curve->poly_order[i] + 1, 1, |
| | 2076 | curve->poly_coef[i][idx]); |
| | 2077 | break; |
| | 2078 | case AV_DOVI_MAPPING_MMR: |
| | 2079 | print_str("mapping_idc_name", "mmr"); |
| | 2080 | print_int("mmr_order", curve->mmr_order[i]); |
| | 2081 | print_int("mmr_constant", curve->mmr_constant[i]); |
| | 2082 | print_list_fmt("mmr_coef", "%"PRIi64, |
| | 2083 | curve->mmr_order[i], 7, |
| | 2084 | curve->mmr_coef[i][idx][idx2]); |
| | 2085 | break; |
| | 2086 | default: |
| | 2087 | print_str("mapping_idc_name", "unknown"); |
| | 2088 | break; |
| | 2089 | } |
| | 2090 | |
| | 2091 | // SECTION_ID_FRAME_SIDE_DATA_PIECE |
| | 2092 | writer_print_section_footer(w); |
| | 2093 | } |
| | 2094 | |
| | 2095 | // SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST |
| | 2096 | writer_print_section_footer(w); |
| | 2097 | |
| | 2098 | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { |
| | 2099 | const AVDOVINLQParams *nlq = &mapping->nlq[c]; |
| | 2100 | print_int("nlq_offset", nlq->nlq_offset); |
| | 2101 | print_int("vdr_in_max", nlq->vdr_in_max); |
| | 2102 | |
| | 2103 | switch (mapping->nlq_method_idc) { |
| | 2104 | case AV_DOVI_NLQ_LINEAR_DZ: |
| | 2105 | print_int("linear_deadzone_slope", nlq->linear_deadzone_slope); |
| | 2106 | print_int("linear_deadzone_threshold", nlq->linear_deadzone_threshold); |
| | 2107 | break; |
| | 2108 | } |
| | 2109 | } |
| | 2110 | |
| | 2111 | // SECTION_ID_FRAME_SIDE_DATA_COMPONENT |
| | 2112 | writer_print_section_footer(w); |
| | 2113 | } |
| | 2114 | |
| | 2115 | // SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST |
| | 2116 | writer_print_section_footer(w); |
| | 2117 | |
| | 2118 | // color metadata |
| | 2119 | print_int("dm_metadata_id", color->dm_metadata_id); |
| | 2120 | print_int("scene_refresh_flag", color->scene_refresh_flag); |
| | 2121 | print_list_fmt("ycc_to_rgb_matrix", "%d/%d", |
| | 2122 | FF_ARRAY_ELEMS(color->ycc_to_rgb_matrix), 1, |
| | 2123 | color->ycc_to_rgb_matrix[idx].num, |
| | 2124 | color->ycc_to_rgb_matrix[idx].den); |
| | 2125 | print_list_fmt("ycc_to_rgb_offset", "%d/%d", |
| | 2126 | FF_ARRAY_ELEMS(color->ycc_to_rgb_offset), 1, |
| | 2127 | color->ycc_to_rgb_offset[idx].num, |
| | 2128 | color->ycc_to_rgb_offset[idx].den); |
| | 2129 | print_list_fmt("rgb_to_lms_matrix", "%d/%d", |
| | 2130 | FF_ARRAY_ELEMS(color->rgb_to_lms_matrix), 1, |
| | 2131 | color->rgb_to_lms_matrix[idx].num, |
| | 2132 | color->rgb_to_lms_matrix[idx].den); |
| | 2133 | print_int("signal_eotf", color->signal_eotf); |
| | 2134 | print_int("signal_eotf_param0", color->signal_eotf_param0); |
| | 2135 | print_int("signal_eotf_param1", color->signal_eotf_param1); |
| | 2136 | print_int("signal_eotf_param2", color->signal_eotf_param2); |
| | 2137 | print_int("signal_bit_depth", color->signal_bit_depth); |
| | 2138 | print_int("signal_color_space", color->signal_color_space); |
| | 2139 | print_int("signal_chroma_format", color->signal_chroma_format); |
| | 2140 | print_int("signal_full_range_flag", color->signal_full_range_flag); |
| | 2141 | print_int("source_min_pq", color->source_min_pq); |
| | 2142 | print_int("source_max_pq", color->source_max_pq); |
| | 2143 | print_int("source_diagonal", color->source_diagonal); |
| | 2144 | |
| | 2145 | av_bprint_finalize(&pbuf, NULL); |
| | 2146 | } |
| | 2147 | } |
| | 2148 | |
| | 2149 | static void print_dynamic_hdr10_plus(WriterContext *w, const AVDynamicHDRPlus *metadata) |
| | 2150 | { |
| | 2151 | if (!metadata) |
| | 2152 | return; |
| | 2153 | print_int("application version", metadata->application_version); |
| | 2154 | print_int("num_windows", metadata->num_windows); |
| | 2155 | for (int n = 1; n < metadata->num_windows; n++) { |
| | 2156 | const AVHDRPlusColorTransformParams *params = &metadata->params[n]; |
| | 2157 | print_q("window_upper_left_corner_x", |
| | 2158 | params->window_upper_left_corner_x,'/'); |
| | 2159 | print_q("window_upper_left_corner_y", |
| | 2160 | params->window_upper_left_corner_y,'/'); |
| | 2161 | print_q("window_lower_right_corner_x", |
| | 2162 | params->window_lower_right_corner_x,'/'); |
| | 2163 | print_q("window_lower_right_corner_y", |
| | 2164 | params->window_lower_right_corner_y,'/'); |
| | 2165 | print_q("window_upper_left_corner_x", |
| | 2166 | params->window_upper_left_corner_x,'/'); |
| | 2167 | print_q("window_upper_left_corner_y", |
| | 2168 | params->window_upper_left_corner_y,'/'); |
| | 2169 | print_int("center_of_ellipse_x", |
| | 2170 | params->center_of_ellipse_x ) ; |
| | 2171 | print_int("center_of_ellipse_y", |
| | 2172 | params->center_of_ellipse_y ); |
| | 2173 | print_int("rotation_angle", |
| | 2174 | params->rotation_angle); |
| | 2175 | print_int("semimajor_axis_internal_ellipse", |
| | 2176 | params->semimajor_axis_internal_ellipse); |
| | 2177 | print_int("semimajor_axis_external_ellipse", |
| | 2178 | params->semimajor_axis_external_ellipse); |
| | 2179 | print_int("semiminor_axis_external_ellipse", |
| | 2180 | params->semiminor_axis_external_ellipse); |
| | 2181 | print_int("overlap_process_option", |
| | 2182 | params->overlap_process_option); |
| | 2183 | } |
| | 2184 | print_q("targeted_system_display_maximum_luminance", |
| | 2185 | metadata->targeted_system_display_maximum_luminance,'/'); |
| | 2186 | if (metadata->targeted_system_display_actual_peak_luminance_flag) { |
| | 2187 | print_int("num_rows_targeted_system_display_actual_peak_luminance", |
| | 2188 | metadata->num_rows_targeted_system_display_actual_peak_luminance); |
| | 2189 | print_int("num_cols_targeted_system_display_actual_peak_luminance", |
| | 2190 | metadata->num_cols_targeted_system_display_actual_peak_luminance); |
| | 2191 | for (int i = 0; i < metadata->num_rows_targeted_system_display_actual_peak_luminance; i++) { |
| | 2192 | for (int j = 0; j < metadata->num_cols_targeted_system_display_actual_peak_luminance; j++) { |
| | 2193 | print_q("targeted_system_display_actual_peak_luminance", |
| | 2194 | metadata->targeted_system_display_actual_peak_luminance[i][j],'/'); |
| | 2195 | } |
| | 2196 | } |
| | 2197 | } |
| | 2198 | for (int n = 0; n < metadata->num_windows; n++) { |
| | 2199 | const AVHDRPlusColorTransformParams *params = &metadata->params[n]; |
| | 2200 | for (int i = 0; i < 3; i++) { |
| | 2201 | print_q("maxscl",params->maxscl[i],'/'); |
| | 2202 | } |
| | 2203 | print_q("average_maxrgb", |
| | 2204 | params->average_maxrgb,'/'); |
| | 2205 | print_int("num_distribution_maxrgb_percentiles", |
| | 2206 | params->num_distribution_maxrgb_percentiles); |
| | 2207 | for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) { |
| | 2208 | print_int("distribution_maxrgb_percentage", |
| | 2209 | params->distribution_maxrgb[i].percentage); |
| | 2210 | print_q("distribution_maxrgb_percentile", |
| | 2211 | params->distribution_maxrgb[i].percentile,'/'); |
| | 2212 | } |
| | 2213 | print_q("fraction_bright_pixels", |
| | 2214 | params->fraction_bright_pixels,'/'); |
| | 2215 | } |
| | 2216 | if (metadata->mastering_display_actual_peak_luminance_flag) { |
| | 2217 | print_int("num_rows_mastering_display_actual_peak_luminance", |
| | 2218 | metadata->num_rows_mastering_display_actual_peak_luminance); |
| | 2219 | print_int("num_cols_mastering_display_actual_peak_luminance", |
| | 2220 | metadata->num_cols_mastering_display_actual_peak_luminance); |
| | 2221 | for (int i = 0; i < metadata->num_rows_mastering_display_actual_peak_luminance; i++) { |
| | 2222 | for (int j = 0; j < metadata->num_cols_mastering_display_actual_peak_luminance; j++) { |
| | 2223 | print_q("mastering_display_actual_peak_luminance", |
| | 2224 | metadata->mastering_display_actual_peak_luminance[i][j],'/'); |
| | 2225 | } |
| | 2226 | } |
| | 2227 | } |
| | 2228 | |
| | 2229 | for (int n = 0; n < metadata->num_windows; n++) { |
| | 2230 | const AVHDRPlusColorTransformParams *params = &metadata->params[n]; |
| | 2231 | if (params->tone_mapping_flag) { |
| | 2232 | print_q("knee_point_x", params->knee_point_x,'/'); |
| | 2233 | print_q("knee_point_y", params->knee_point_y,'/'); |
| | 2234 | print_int("num_bezier_curve_anchors", |
| | 2235 | params->num_bezier_curve_anchors ); |
| | 2236 | for (int i = 0; i < params->num_bezier_curve_anchors; i++) { |
| | 2237 | print_q("bezier_curve_anchors", |
| | 2238 | params->bezier_curve_anchors[i],'/'); |
| | 2239 | } |
| | 2240 | } |
| | 2241 | if (params->color_saturation_mapping_flag) { |
| | 2242 | print_q("color_saturation_weight", |
| | 2243 | params->color_saturation_weight,'/'); |
| | 2244 | } |
| | 2245 | } |
| | 2246 | } |
| | 2247 | |
| | 2248 | static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *metadata) |
| | 2249 | { |
| | 2250 | if (!metadata) |
| | 2251 | return; |
| | 2252 | print_int("system_start_code", metadata->system_start_code); |
| | 2253 | print_int("num_windows", metadata->num_windows); |
| | 2254 | |
| | 2255 | for (int n = 0; n < metadata->num_windows; n++) { |
| | 2256 | const AVHDRVividColorTransformParams *params = &metadata->params[n]; |
| | 2257 | |
| | 2258 | print_q("minimum_maxrgb", params->minimum_maxrgb, '/'); |
| | 2259 | print_q("average_maxrgb", params->average_maxrgb, '/'); |
| | 2260 | print_q("variance_maxrgb", params->variance_maxrgb, '/'); |
| | 2261 | print_q("maximum_maxrgb", params->maximum_maxrgb, '/'); |
| | 2262 | } |
| | 2263 | |
| | 2264 | for (int n = 0; n < metadata->num_windows; n++) { |
| | 2265 | const AVHDRVividColorTransformParams *params = &metadata->params[n]; |
| | 2266 | |
| | 2267 | print_int("tone_mapping_mode_flag", params->tone_mapping_mode_flag); |
| | 2268 | if (params->tone_mapping_mode_flag) { |
| | 2269 | print_int("tone_mapping_param_num", params->tone_mapping_param_num); |
| | 2270 | for (int i = 0; i < params->tone_mapping_param_num; i++) { |
| | 2271 | const AVHDRVividColorToneMappingParams *tm_params = ¶ms->tm_params[i]; |
| | 2272 | |
| | 2273 | print_q("targeted_system_display_maximum_luminance", |
| | 2274 | tm_params->targeted_system_display_maximum_luminance, '/'); |
| | 2275 | print_int("base_enable_flag", tm_params->base_enable_flag); |
| | 2276 | if (tm_params->base_enable_flag) { |
| | 2277 | print_q("base_param_m_p", tm_params->base_param_m_p, '/'); |
| | 2278 | print_q("base_param_m_m", tm_params->base_param_m_m, '/'); |
| | 2279 | print_q("base_param_m_a", tm_params->base_param_m_a, '/'); |
| | 2280 | print_q("base_param_m_b", tm_params->base_param_m_b, '/'); |
| | 2281 | print_q("base_param_m_n", tm_params->base_param_m_n, '/'); |
| | 2282 | |
| | 2283 | print_int("base_param_k1", tm_params->base_param_k1); |
| | 2284 | print_int("base_param_k2", tm_params->base_param_k2); |
| | 2285 | print_int("base_param_k3", tm_params->base_param_k3); |
| | 2286 | print_int("base_param_Delta_enable_mode", |
| | 2287 | tm_params->base_param_Delta_enable_mode); |
| | 2288 | print_q("base_param_Delta", tm_params->base_param_Delta, '/'); |
| | 2289 | } |
| | 2290 | print_int("3Spline_enable_flag", tm_params->three_Spline_enable_flag); |
| | 2291 | if (tm_params->three_Spline_enable_flag) { |
| | 2292 | print_int("3Spline_num", tm_params->three_Spline_num); |
| | 2293 | |
| | 2294 | for (int j = 0; j < tm_params->three_Spline_num; j++) { |
| | 2295 | const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j]; |
| | 2296 | print_int("3Spline_TH_mode", three_spline->th_mode); |
| | 2297 | if (three_spline->th_mode == 0 || three_spline->th_mode == 2) |
| | 2298 | print_q("3Spline_TH_enable_MB", three_spline->th_enable_mb, '/'); |
| | 2299 | print_q("3Spline_TH_enable", three_spline->th_enable, '/'); |
| | 2300 | print_q("3Spline_TH_Delta1", three_spline->th_delta1, '/'); |
| | 2301 | print_q("3Spline_TH_Delta2", three_spline->th_delta2, '/'); |
| | 2302 | print_q("3Spline_enable_Strength", three_spline->enable_strength, '/'); |
| | 2303 | } |
| | 2304 | } |
| | 2305 | } |
| | 2306 | } |
| | 2307 | |
| | 2308 | print_int("color_saturation_mapping_flag", params->color_saturation_mapping_flag); |
| | 2309 | if (params->color_saturation_mapping_flag) { |
| | 2310 | print_int("color_saturation_num", params->color_saturation_num); |
| | 2311 | for (int i = 0; i < params->color_saturation_num; i++) { |
| | 2312 | print_q("color_saturation_gain", params->color_saturation_gain[i], '/'); |
| | 2313 | } |
| | 2314 | } |
| | 2315 | } |
| | 2316 | } |
| | 2317 | |
| | 2318 | static void print_ambient_viewing_environment(WriterContext *w, |
| | 2319 | const AVAmbientViewingEnvironment *env) |
| | 2320 | { |
| | 2321 | if (!env) |
| | 2322 | return; |
| | 2323 | |
| | 2324 | print_q("ambient_illuminance", env->ambient_illuminance, '/'); |
| | 2325 | print_q("ambient_light_x", env->ambient_light_x, '/'); |
| | 2326 | print_q("ambient_light_y", env->ambient_light_y, '/'); |
| | 2327 | } |
| | 2328 | |
| | 2329 | static void print_pkt_side_data(WriterContext *w, |
| | 2330 | AVCodecParameters *par, |
| | 2331 | const AVPacketSideData *sd, |
| | 2332 | SectionID id_data) |
| | 2333 | { |
| | 2334 | const char *name = av_packet_side_data_name(sd->type); |
| | 2335 | |
| | 2336 | writer_print_section_header(w, (void *)sd, id_data); |
| | 2337 | print_str("side_data_type", name ? name : "unknown"); |
| | 2338 | if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) { |
| | 2339 | double rotation = av_display_rotation_get((int32_t *)sd->data); |
| | 2340 | if (isnan(rotation)) |
| | 2341 | rotation = 0; |
| | 2342 | writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1); |
| | 2343 | print_int("rotation", rotation); |
| | 2344 | } else if (sd->type == AV_PKT_DATA_STEREO3D) { |
| | 2345 | const AVStereo3D *stereo = (AVStereo3D *)sd->data; |
| | 2346 | print_str("type", av_stereo3d_type_name(stereo->type)); |
| | 2347 | print_int("inverted", !!(stereo->flags & AV_STEREO3D_FLAG_INVERT)); |
| | 2348 | } else if (sd->type == AV_PKT_DATA_SPHERICAL) { |
| | 2349 | const AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data; |
| | 2350 | print_str("projection", av_spherical_projection_name(spherical->projection)); |
| | 2351 | if (spherical->projection == AV_SPHERICAL_CUBEMAP) { |
| | 2352 | print_int("padding", spherical->padding); |
| | 2353 | } else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) { |
| | 2354 | size_t l, t, r, b; |
| | 2355 | av_spherical_tile_bounds(spherical, par->width, par->height, |
| | 2356 | &l, &t, &r, &b); |
| | 2357 | print_int("bound_left", l); |
| | 2358 | print_int("bound_top", t); |
| | 2359 | print_int("bound_right", r); |
| | 2360 | print_int("bound_bottom", b); |
| | 2361 | } |
| | 2362 | |
| | 2363 | print_int("yaw", (double) spherical->yaw / (1 << 16)); |
| | 2364 | print_int("pitch", (double) spherical->pitch / (1 << 16)); |
| | 2365 | print_int("roll", (double) spherical->roll / (1 << 16)); |
| | 2366 | } else if (sd->type == AV_PKT_DATA_SKIP_SAMPLES && sd->size == 10) { |
| | 2367 | print_int("skip_samples", AV_RL32(sd->data)); |
| | 2368 | print_int("discard_padding", AV_RL32(sd->data + 4)); |
| | 2369 | print_int("skip_reason", AV_RL8(sd->data + 8)); |
| | 2370 | print_int("discard_reason", AV_RL8(sd->data + 9)); |
| | 2371 | } else if (sd->type == AV_PKT_DATA_MASTERING_DISPLAY_METADATA) { |
| | 2372 | AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; |
| | 2373 | |
| | 2374 | if (metadata->has_primaries) { |
| | 2375 | print_q("red_x", metadata->display_primaries[0][0], '/'); |
| | 2376 | print_q("red_y", metadata->display_primaries[0][1], '/'); |
| | 2377 | print_q("green_x", metadata->display_primaries[1][0], '/'); |
| | 2378 | print_q("green_y", metadata->display_primaries[1][1], '/'); |
| | 2379 | print_q("blue_x", metadata->display_primaries[2][0], '/'); |
| | 2380 | print_q("blue_y", metadata->display_primaries[2][1], '/'); |
| | 2381 | |
| | 2382 | print_q("white_point_x", metadata->white_point[0], '/'); |
| | 2383 | print_q("white_point_y", metadata->white_point[1], '/'); |
| | 2384 | } |
| | 2385 | |
| | 2386 | if (metadata->has_luminance) { |
| | 2387 | print_q("min_luminance", metadata->min_luminance, '/'); |
| | 2388 | print_q("max_luminance", metadata->max_luminance, '/'); |
| | 2389 | } |
| | 2390 | } else if (sd->type == AV_PKT_DATA_CONTENT_LIGHT_LEVEL) { |
| | 2391 | AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; |
| | 2392 | print_int("max_content", metadata->MaxCLL); |
| | 2393 | print_int("max_average", metadata->MaxFALL); |
| | 2394 | } else if (sd->type == AV_PKT_DATA_DYNAMIC_HDR10_PLUS) { |
| | 2395 | AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; |
| | 2396 | print_dynamic_hdr10_plus(w, metadata); |
| | 2397 | } else if (sd->type == AV_PKT_DATA_DOVI_CONF) { |
| | 2398 | AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data; |
| | 2399 | print_int("dv_version_major", dovi->dv_version_major); |
| | 2400 | print_int("dv_version_minor", dovi->dv_version_minor); |
| | 2401 | print_int("dv_profile", dovi->dv_profile); |
| | 2402 | print_int("dv_level", dovi->dv_level); |
| | 2403 | print_int("rpu_present_flag", dovi->rpu_present_flag); |
| | 2404 | print_int("el_present_flag", dovi->el_present_flag); |
| | 2405 | print_int("bl_present_flag", dovi->bl_present_flag); |
| | 2406 | print_int("dv_bl_signal_compatibility_id", dovi->dv_bl_signal_compatibility_id); |
| | 2407 | } else if (sd->type == AV_PKT_DATA_AUDIO_SERVICE_TYPE) { |
| | 2408 | enum AVAudioServiceType *t = (enum AVAudioServiceType *)sd->data; |
| | 2409 | print_int("service_type", *t); |
| | 2410 | } else if (sd->type == AV_PKT_DATA_MPEGTS_STREAM_ID) { |
| | 2411 | print_int("id", *sd->data); |
| | 2412 | } else if (sd->type == AV_PKT_DATA_CPB_PROPERTIES) { |
| | 2413 | const AVCPBProperties *prop = (AVCPBProperties *)sd->data; |
| | 2414 | print_int("max_bitrate", prop->max_bitrate); |
| | 2415 | print_int("min_bitrate", prop->min_bitrate); |
| | 2416 | print_int("avg_bitrate", prop->avg_bitrate); |
| | 2417 | print_int("buffer_size", prop->buffer_size); |
| | 2418 | print_int("vbv_delay", prop->vbv_delay); |
| | 2419 | } else if (sd->type == AV_PKT_DATA_WEBVTT_IDENTIFIER || |
| | 2420 | sd->type == AV_PKT_DATA_WEBVTT_SETTINGS) { |
| | 2421 | if (do_show_data) |
| | 2422 | writer_print_data(w, "data", sd->data, sd->size); |
| | 2423 | writer_print_data_hash(w, "data_hash", sd->data, sd->size); |
| | 2424 | } else if (sd->type == AV_PKT_DATA_AFD && sd->size > 0) { |
| | 2425 | print_int("active_format", *sd->data); |
| | 2426 | } |
| | 2427 | } |
| | 2428 | |
| | 2429 | static void print_private_data(WriterContext *w, void *priv_data) |
| | 2430 | { |
| | 2431 | const AVOption *opt = NULL; |
| | 2432 | while (opt = av_opt_next(priv_data, opt)) { |
| | 2433 | uint8_t *str; |
| | 2434 | if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue; |
| | 2435 | if (av_opt_get(priv_data, opt->name, 0, &str) >= 0) { |
| | 2436 | print_str(opt->name, str); |
| | 2437 | av_free(str); |
| | 2438 | } |
| | 2439 | } |
| | 2440 | } |
| | 2441 | |
| | 2442 | static void print_color_range(WriterContext *w, enum AVColorRange color_range) |
| | 2443 | { |
| | 2444 | const char *val = av_color_range_name(color_range); |
| | 2445 | if (!val || color_range == AVCOL_RANGE_UNSPECIFIED) { |
| | 2446 | print_str_opt("color_range", "unknown"); |
| | 2447 | } else { |
| | 2448 | print_str("color_range", val); |
| | 2449 | } |
| | 2450 | } |
| | 2451 | |
| | 2452 | static void print_color_space(WriterContext *w, enum AVColorSpace color_space) |
| | 2453 | { |
| | 2454 | const char *val = av_color_space_name(color_space); |
| | 2455 | if (!val || color_space == AVCOL_SPC_UNSPECIFIED) { |
| | 2456 | print_str_opt("color_space", "unknown"); |
| | 2457 | } else { |
| | 2458 | print_str("color_space", val); |
| | 2459 | } |
| | 2460 | } |
| | 2461 | |
| | 2462 | static void print_primaries(WriterContext *w, enum AVColorPrimaries color_primaries) |
| | 2463 | { |
| | 2464 | const char *val = av_color_primaries_name(color_primaries); |
| | 2465 | if (!val || color_primaries == AVCOL_PRI_UNSPECIFIED) { |
| | 2466 | print_str_opt("color_primaries", "unknown"); |
| | 2467 | } else { |
| | 2468 | print_str("color_primaries", val); |
| | 2469 | } |
| | 2470 | } |
| | 2471 | |
| | 2472 | static void print_color_trc(WriterContext *w, enum AVColorTransferCharacteristic color_trc) |
| | 2473 | { |
| | 2474 | const char *val = av_color_transfer_name(color_trc); |
| | 2475 | if (!val || color_trc == AVCOL_TRC_UNSPECIFIED) { |
| | 2476 | print_str_opt("color_transfer", "unknown"); |
| | 2477 | } else { |
| | 2478 | print_str("color_transfer", val); |
| | 2479 | } |
| | 2480 | } |
| | 2481 | |
| | 2482 | static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma_location) |
| | 2483 | { |
| | 2484 | const char *val = av_chroma_location_name(chroma_location); |
| | 2485 | if (!val || chroma_location == AVCHROMA_LOC_UNSPECIFIED) { |
| | 2486 | print_str_opt("chroma_location", "unspecified"); |
| | 2487 | } else { |
| | 2488 | print_str("chroma_location", val); |
| | 2489 | } |
| | 2490 | } |
| | 2491 | |
| | 2492 | static void clear_log(int need_lock) |
| | 2493 | { |
| | 2494 | int i; |
| | 2495 | |
| | 2496 | if (need_lock) |
| | 2497 | pthread_mutex_lock(&log_mutex); |
| | 2498 | for (i=0; i<log_buffer_size; i++) { |
| | 2499 | av_freep(&log_buffer[i].context_name); |
| | 2500 | av_freep(&log_buffer[i].parent_name); |
| | 2501 | av_freep(&log_buffer[i].log_message); |
| | 2502 | } |
| | 2503 | log_buffer_size = 0; |
| | 2504 | if(need_lock) |
| | 2505 | pthread_mutex_unlock(&log_mutex); |
| | 2506 | } |
| | 2507 | |
| | 2508 | static int show_log(WriterContext *w, int section_ids, int section_id, int log_level) |
| | 2509 | { |
| | 2510 | int i; |
| | 2511 | pthread_mutex_lock(&log_mutex); |
| | 2512 | if (!log_buffer_size) { |
| | 2513 | pthread_mutex_unlock(&log_mutex); |
| | 2514 | return 0; |
| | 2515 | } |
| | 2516 | writer_print_section_header(w, NULL, section_ids); |
| | 2517 | |
| | 2518 | for (i=0; i<log_buffer_size; i++) { |
| | 2519 | if (log_buffer[i].log_level <= log_level) { |
| | 2520 | writer_print_section_header(w, NULL, section_id); |
| | 2521 | print_str("context", log_buffer[i].context_name); |
| | 2522 | print_int("level", log_buffer[i].log_level); |
| | 2523 | print_int("category", log_buffer[i].category); |
| | 2524 | if (log_buffer[i].parent_name) { |
| | 2525 | print_str("parent_context", log_buffer[i].parent_name); |
| | 2526 | print_int("parent_category", log_buffer[i].parent_category); |
| | 2527 | } else { |
| | 2528 | print_str_opt("parent_context", "N/A"); |
| | 2529 | print_str_opt("parent_category", "N/A"); |
| | 2530 | } |
| | 2531 | print_str("message", log_buffer[i].log_message); |
| | 2532 | writer_print_section_footer(w); |
| | 2533 | } |
| | 2534 | } |
| | 2535 | clear_log(0); |
| | 2536 | pthread_mutex_unlock(&log_mutex); |
| | 2537 | |
| | 2538 | writer_print_section_footer(w); |
| | 2539 | |
| | 2540 | return 0; |
| | 2541 | } |
| | 2542 | |
| | 2543 | static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx) |
| | 2544 | { |
| | 2545 | char val_str[128]; |
| | 2546 | AVStream *st = ifile->streams[pkt->stream_index].st; |
| | 2547 | AVBPrint pbuf; |
| | 2548 | const char *s; |
| | 2549 | |
| | 2550 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 2551 | |
| | 2552 | writer_print_section_header(w, NULL, SECTION_ID_PACKET); |
| | 2553 | |
| | 2554 | s = av_get_media_type_string(st->codecpar->codec_type); |
| | 2555 | if (s) print_str ("codec_type", s); |
| | 2556 | else print_str_opt("codec_type", "unknown"); |
| | 2557 | print_int("stream_index", pkt->stream_index); |
| | 2558 | print_ts ("pts", pkt->pts); |
| | 2559 | print_time("pts_time", pkt->pts, &st->time_base); |
| | 2560 | print_ts ("dts", pkt->dts); |
| | 2561 | print_time("dts_time", pkt->dts, &st->time_base); |
| | 2562 | print_duration_ts("duration", pkt->duration); |
| | 2563 | print_duration_time("duration_time", pkt->duration, &st->time_base); |
| | 2564 | print_val("size", pkt->size, unit_byte_str); |
| | 2565 | if (pkt->pos != -1) print_fmt ("pos", "%"PRId64, pkt->pos); |
| | 2566 | else print_str_opt("pos", "N/A"); |
| | 2567 | print_fmt("flags", "%c%c%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_', |
| | 2568 | pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_', |
| | 2569 | pkt->flags & AV_PKT_FLAG_CORRUPT ? 'C' : '_'); |
| | 2570 | if (do_show_data) |
| | 2571 | writer_print_data(w, "data", pkt->data, pkt->size); |
| | 2572 | writer_print_data_hash(w, "data_hash", pkt->data, pkt->size); |
| | 2573 | |
| | 2574 | if (pkt->side_data_elems) { |
| | 2575 | size_t size; |
| | 2576 | const uint8_t *side_metadata; |
| | 2577 | |
| | 2578 | side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size); |
| | 2579 | if (side_metadata && size && do_show_packet_tags) { |
| | 2580 | AVDictionary *dict = NULL; |
| | 2581 | if (av_packet_unpack_dictionary(side_metadata, size, &dict) >= 0) |
| | 2582 | show_tags(w, dict, SECTION_ID_PACKET_TAGS); |
| | 2583 | av_dict_free(&dict); |
| | 2584 | } |
| | 2585 | |
| | 2586 | writer_print_section_header(w, NULL, SECTION_ID_PACKET_SIDE_DATA_LIST); |
| | 2587 | for (int i = 0; i < pkt->side_data_elems; i++) { |
| | 2588 | print_pkt_side_data(w, st->codecpar, &pkt->side_data[i], |
| | 2589 | SECTION_ID_PACKET_SIDE_DATA); |
| | 2590 | writer_print_section_footer(w); |
| | 2591 | } |
| | 2592 | writer_print_section_footer(w); |
| | 2593 | } |
| | 2594 | |
| | 2595 | writer_print_section_footer(w); |
| | 2596 | |
| | 2597 | av_bprint_finalize(&pbuf, NULL); |
| | 2598 | fflush(stdout); |
| | 2599 | } |
| | 2600 | |
| | 2601 | static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream, |
| | 2602 | AVFormatContext *fmt_ctx) |
| | 2603 | { |
| | 2604 | AVBPrint pbuf; |
| | 2605 | |
| | 2606 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 2607 | |
| | 2608 | writer_print_section_header(w, NULL, SECTION_ID_SUBTITLE); |
| | 2609 | |
| | 2610 | print_str ("media_type", "subtitle"); |
| | 2611 | print_ts ("pts", sub->pts); |
| | 2612 | print_time("pts_time", sub->pts, &AV_TIME_BASE_Q); |
| | 2613 | print_int ("format", sub->format); |
| | 2614 | print_int ("start_display_time", sub->start_display_time); |
| | 2615 | print_int ("end_display_time", sub->end_display_time); |
| | 2616 | print_int ("num_rects", sub->num_rects); |
| | 2617 | |
| | 2618 | writer_print_section_footer(w); |
| | 2619 | |
| | 2620 | av_bprint_finalize(&pbuf, NULL); |
| | 2621 | fflush(stdout); |
| | 2622 | } |
| | 2623 | |
| | 2624 | static void print_frame_side_data(WriterContext *w, |
| | 2625 | const AVFrame *frame, |
| | 2626 | const AVStream *stream) |
| | 2627 | { |
| | 2628 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_LIST); |
| | 2629 | |
| | 2630 | for (int i = 0; i < frame->nb_side_data; i++) { |
| | 2631 | const AVFrameSideData *sd = frame->side_data[i]; |
| | 2632 | const char *name; |
| | 2633 | |
| | 2634 | writer_print_section_header(w, (void *)sd, SECTION_ID_FRAME_SIDE_DATA); |
| | 2635 | name = av_frame_side_data_name(sd->type); |
| | 2636 | print_str("side_data_type", name ? name : "unknown"); |
| | 2637 | if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) { |
| | 2638 | double rotation = av_display_rotation_get((int32_t *)sd->data); |
| | 2639 | if (isnan(rotation)) |
| | 2640 | rotation = 0; |
| | 2641 | writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1); |
| | 2642 | print_int("rotation", rotation); |
| | 2643 | } else if (sd->type == AV_FRAME_DATA_AFD && sd->size > 0) { |
| | 2644 | print_int("active_format", *sd->data); |
| | 2645 | } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) { |
| | 2646 | char tcbuf[AV_TIMECODE_STR_SIZE]; |
| | 2647 | av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data)); |
| | 2648 | print_str("timecode", tcbuf); |
| | 2649 | } else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) { |
| | 2650 | uint32_t *tc = (uint32_t*)sd->data; |
| | 2651 | int m = FFMIN(tc[0],3); |
| | 2652 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST); |
| | 2653 | for (int j = 1; j <= m ; j++) { |
| | 2654 | char tcbuf[AV_TIMECODE_STR_SIZE]; |
| | 2655 | av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0); |
| | 2656 | writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE); |
| | 2657 | print_str("value", tcbuf); |
| | 2658 | writer_print_section_footer(w); |
| | 2659 | } |
| | 2660 | writer_print_section_footer(w); |
| | 2661 | } else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) { |
| | 2662 | AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; |
| | 2663 | |
| | 2664 | if (metadata->has_primaries) { |
| | 2665 | print_q("red_x", metadata->display_primaries[0][0], '/'); |
| | 2666 | print_q("red_y", metadata->display_primaries[0][1], '/'); |
| | 2667 | print_q("green_x", metadata->display_primaries[1][0], '/'); |
| | 2668 | print_q("green_y", metadata->display_primaries[1][1], '/'); |
| | 2669 | print_q("blue_x", metadata->display_primaries[2][0], '/'); |
| | 2670 | print_q("blue_y", metadata->display_primaries[2][1], '/'); |
| | 2671 | |
| | 2672 | print_q("white_point_x", metadata->white_point[0], '/'); |
| | 2673 | print_q("white_point_y", metadata->white_point[1], '/'); |
| | 2674 | } |
| | 2675 | |
| | 2676 | if (metadata->has_luminance) { |
| | 2677 | print_q("min_luminance", metadata->min_luminance, '/'); |
| | 2678 | print_q("max_luminance", metadata->max_luminance, '/'); |
| | 2679 | } |
| | 2680 | } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_PLUS) { |
| | 2681 | AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; |
| | 2682 | print_dynamic_hdr10_plus(w, metadata); |
| | 2683 | } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) { |
| | 2684 | AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; |
| | 2685 | print_int("max_content", metadata->MaxCLL); |
| | 2686 | print_int("max_average", metadata->MaxFALL); |
| | 2687 | } else if (sd->type == AV_FRAME_DATA_ICC_PROFILE) { |
| | 2688 | const AVDictionaryEntry *tag = av_dict_get(sd->metadata, "name", NULL, AV_DICT_MATCH_CASE); |
| | 2689 | if (tag) |
| | 2690 | print_str(tag->key, tag->value); |
| | 2691 | print_int("size", sd->size); |
| | 2692 | } else if (sd->type == AV_FRAME_DATA_DOVI_METADATA) { |
| | 2693 | print_dovi_metadata(w, (const AVDOVIMetadata *)sd->data); |
| | 2694 | } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_VIVID) { |
| | 2695 | AVDynamicHDRVivid *metadata = (AVDynamicHDRVivid *)sd->data; |
| | 2696 | print_dynamic_hdr_vivid(w, metadata); |
| | 2697 | } else if (sd->type == AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT) { |
| | 2698 | print_ambient_viewing_environment(w, (const AVAmbientViewingEnvironment *)sd->data); |
| | 2699 | } |
| | 2700 | writer_print_section_footer(w); |
| | 2701 | } |
| | 2702 | writer_print_section_footer(w); |
| | 2703 | } |
| | 2704 | |
| | 2705 | static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, |
| | 2706 | AVFormatContext *fmt_ctx) |
| | 2707 | { |
| | 2708 | FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; |
| | 2709 | AVBPrint pbuf; |
| | 2710 | char val_str[128]; |
| | 2711 | const char *s; |
| | 2712 | |
| | 2713 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 2714 | |
| | 2715 | writer_print_section_header(w, NULL, SECTION_ID_FRAME); |
| | 2716 | |
| | 2717 | s = av_get_media_type_string(stream->codecpar->codec_type); |
| | 2718 | if (s) print_str ("media_type", s); |
| | 2719 | else print_str_opt("media_type", "unknown"); |
| | 2720 | print_int("stream_index", stream->index); |
| | 2721 | print_int("key_frame", !!(frame->flags & AV_FRAME_FLAG_KEY)); |
| | 2722 | print_ts ("pts", frame->pts); |
| | 2723 | print_time("pts_time", frame->pts, &stream->time_base); |
| | 2724 | print_ts ("pkt_dts", frame->pkt_dts); |
| | 2725 | print_time("pkt_dts_time", frame->pkt_dts, &stream->time_base); |
| | 2726 | print_ts ("best_effort_timestamp", frame->best_effort_timestamp); |
| | 2727 | print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base); |
| | 2728 | #if LIBAVUTIL_VERSION_MAJOR < 59 |
| | 2729 | AV_NOWARN_DEPRECATED( |
| | 2730 | print_duration_ts ("pkt_duration", frame->pkt_duration); |
| | 2731 | print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base); |
| | 2732 | ) |
| | 2733 | #endif |
| | 2734 | print_duration_ts ("duration", frame->duration); |
| | 2735 | print_duration_time("duration_time", frame->duration, &stream->time_base); |
| | 2736 | if (fd && fd->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, fd->pkt_pos); |
| | 2737 | else print_str_opt("pkt_pos", "N/A"); |
| | 2738 | if (fd && fd->pkt_size != -1) print_val ("pkt_size", fd->pkt_size, unit_byte_str); |
| | 2739 | else print_str_opt("pkt_size", "N/A"); |
| | 2740 | |
| | 2741 | switch (stream->codecpar->codec_type) { |
| | 2742 | AVRational sar; |
| | 2743 | |
| | 2744 | case AVMEDIA_TYPE_VIDEO: |
| | 2745 | print_int("width", frame->width); |
| | 2746 | print_int("height", frame->height); |
| | 2747 | print_int("crop_top", frame->crop_top); |
| | 2748 | print_int("crop_bottom", frame->crop_bottom); |
| | 2749 | print_int("crop_left", frame->crop_left); |
| | 2750 | print_int("crop_right", frame->crop_right); |
| | 2751 | s = av_get_pix_fmt_name(frame->format); |
| | 2752 | if (s) print_str ("pix_fmt", s); |
| | 2753 | else print_str_opt("pix_fmt", "unknown"); |
| | 2754 | sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame); |
| | 2755 | if (sar.num) { |
| | 2756 | print_q("sample_aspect_ratio", sar, ':'); |
| | 2757 | } else { |
| | 2758 | print_str_opt("sample_aspect_ratio", "N/A"); |
| | 2759 | } |
| | 2760 | print_fmt("pict_type", "%c", av_get_picture_type_char(frame->pict_type)); |
| | 2761 | #if LIBAVUTIL_VERSION_MAJOR < 59 |
| | 2762 | AV_NOWARN_DEPRECATED( |
| | 2763 | print_int("coded_picture_number", frame->coded_picture_number); |
| | 2764 | print_int("display_picture_number", frame->display_picture_number); |
| | 2765 | ) |
| | 2766 | #endif |
| | 2767 | print_int("interlaced_frame", !!(frame->flags & AV_FRAME_FLAG_INTERLACED)); |
| | 2768 | print_int("top_field_first", !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); |
| | 2769 | print_int("repeat_pict", frame->repeat_pict); |
| | 2770 | |
| | 2771 | print_color_range(w, frame->color_range); |
| | 2772 | print_color_space(w, frame->colorspace); |
| | 2773 | print_primaries(w, frame->color_primaries); |
| | 2774 | print_color_trc(w, frame->color_trc); |
| | 2775 | print_chroma_location(w, frame->chroma_location); |
| | 2776 | break; |
| | 2777 | |
| | 2778 | case AVMEDIA_TYPE_AUDIO: |
| | 2779 | s = av_get_sample_fmt_name(frame->format); |
| | 2780 | if (s) print_str ("sample_fmt", s); |
| | 2781 | else print_str_opt("sample_fmt", "unknown"); |
| | 2782 | print_int("nb_samples", frame->nb_samples); |
| | 2783 | print_int("channels", frame->ch_layout.nb_channels); |
| | 2784 | if (frame->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { |
| | 2785 | av_channel_layout_describe(&frame->ch_layout, val_str, sizeof(val_str)); |
| | 2786 | print_str ("channel_layout", val_str); |
| | 2787 | } else |
| | 2788 | print_str_opt("channel_layout", "unknown"); |
| | 2789 | break; |
| | 2790 | } |
| | 2791 | if (do_show_frame_tags) |
| | 2792 | show_tags(w, frame->metadata, SECTION_ID_FRAME_TAGS); |
| | 2793 | if (do_show_log) |
| | 2794 | show_log(w, SECTION_ID_FRAME_LOGS, SECTION_ID_FRAME_LOG, do_show_log); |
| | 2795 | if (frame->nb_side_data) |
| | 2796 | print_frame_side_data(w, frame, stream); |
| | 2797 | |
| | 2798 | writer_print_section_footer(w); |
| | 2799 | |
| | 2800 | av_bprint_finalize(&pbuf, NULL); |
| | 2801 | fflush(stdout); |
| | 2802 | } |
| | 2803 | |
| | 2804 | static av_always_inline int process_frame(WriterContext *w, |
| | 2805 | InputFile *ifile, |
| | 2806 | AVFrame *frame, const AVPacket *pkt, |
| | 2807 | int *packet_new) |
| | 2808 | { |
| | 2809 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 2810 | AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx; |
| | 2811 | AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar; |
| | 2812 | AVSubtitle sub; |
| | 2813 | int ret = 0, got_frame = 0; |
| | 2814 | |
| | 2815 | clear_log(1); |
| | 2816 | if (dec_ctx) { |
| | 2817 | switch (par->codec_type) { |
| | 2818 | case AVMEDIA_TYPE_VIDEO: |
| | 2819 | case AVMEDIA_TYPE_AUDIO: |
| | 2820 | if (*packet_new) { |
| | 2821 | ret = avcodec_send_packet(dec_ctx, pkt); |
| | 2822 | if (ret == AVERROR(EAGAIN)) { |
| | 2823 | ret = 0; |
| | 2824 | } else if (ret >= 0 || ret == AVERROR_EOF) { |
| | 2825 | ret = 0; |
| | 2826 | *packet_new = 0; |
| | 2827 | } |
| | 2828 | } |
| | 2829 | if (ret >= 0) { |
| | 2830 | ret = avcodec_receive_frame(dec_ctx, frame); |
| | 2831 | if (ret >= 0) { |
| | 2832 | got_frame = 1; |
| | 2833 | } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { |
| | 2834 | ret = 0; |
| | 2835 | } |
| | 2836 | } |
| | 2837 | break; |
| | 2838 | |
| | 2839 | case AVMEDIA_TYPE_SUBTITLE: |
| | 2840 | if (*packet_new) |
| | 2841 | ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt); |
| | 2842 | *packet_new = 0; |
| | 2843 | break; |
| | 2844 | default: |
| | 2845 | *packet_new = 0; |
| | 2846 | } |
| | 2847 | } else { |
| | 2848 | *packet_new = 0; |
| | 2849 | } |
| | 2850 | |
| | 2851 | if (ret < 0) |
| | 2852 | return ret; |
| | 2853 | if (got_frame) { |
| | 2854 | int is_sub = (par->codec_type == AVMEDIA_TYPE_SUBTITLE); |
| | 2855 | nb_streams_frames[pkt->stream_index]++; |
| | 2856 | if (do_show_frames) |
| | 2857 | if (is_sub) |
| | 2858 | show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx); |
| | 2859 | else |
| | 2860 | show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx); |
| | 2861 | if (is_sub) |
| | 2862 | avsubtitle_free(&sub); |
| | 2863 | } |
| | 2864 | return got_frame || *packet_new; |
| | 2865 | } |
| | 2866 | |
| | 2867 | static void log_read_interval(const ReadInterval *interval, void *log_ctx, int log_level) |
| | 2868 | { |
| | 2869 | av_log(log_ctx, log_level, "id:%d", interval->id); |
| | 2870 | |
| | 2871 | if (interval->has_start) { |
| | 2872 | av_log(log_ctx, log_level, " start:%s%s", interval->start_is_offset ? "+" : "", |
| | 2873 | av_ts2timestr(interval->start, &AV_TIME_BASE_Q)); |
| | 2874 | } else { |
| | 2875 | av_log(log_ctx, log_level, " start:N/A"); |
| | 2876 | } |
| | 2877 | |
| | 2878 | if (interval->has_end) { |
| | 2879 | av_log(log_ctx, log_level, " end:%s", interval->end_is_offset ? "+" : ""); |
| | 2880 | if (interval->duration_frames) |
| | 2881 | av_log(log_ctx, log_level, "#%"PRId64, interval->end); |
| | 2882 | else |
| | 2883 | av_log(log_ctx, log_level, "%s", av_ts2timestr(interval->end, &AV_TIME_BASE_Q)); |
| | 2884 | } else { |
| | 2885 | av_log(log_ctx, log_level, " end:N/A"); |
| | 2886 | } |
| | 2887 | |
| | 2888 | av_log(log_ctx, log_level, "\n"); |
| | 2889 | } |
| | 2890 | |
| | 2891 | static int read_interval_packets(WriterContext *w, InputFile *ifile, |
| | 2892 | const ReadInterval *interval, int64_t *cur_ts) |
| | 2893 | { |
| | 2894 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 2895 | AVPacket *pkt = NULL; |
| | 2896 | AVFrame *frame = NULL; |
| | 2897 | int ret = 0, i = 0, frame_count = 0; |
| | 2898 | int64_t start = -INT64_MAX, end = interval->end; |
| | 2899 | int has_start = 0, has_end = interval->has_end && !interval->end_is_offset; |
| | 2900 | |
| | 2901 | av_log(NULL, AV_LOG_VERBOSE, "Processing read interval "); |
| | 2902 | log_read_interval(interval, NULL, AV_LOG_VERBOSE); |
| | 2903 | |
| | 2904 | if (interval->has_start) { |
| | 2905 | int64_t target; |
| | 2906 | if (interval->start_is_offset) { |
| | 2907 | if (*cur_ts == AV_NOPTS_VALUE) { |
| | 2908 | av_log(NULL, AV_LOG_ERROR, |
| | 2909 | "Could not seek to relative position since current " |
| | 2910 | "timestamp is not defined\n"); |
| | 2911 | ret = AVERROR(EINVAL); |
| | 2912 | goto end; |
| | 2913 | } |
| | 2914 | target = *cur_ts + interval->start; |
| | 2915 | } else { |
| | 2916 | target = interval->start; |
| | 2917 | } |
| | 2918 | |
| | 2919 | av_log(NULL, AV_LOG_VERBOSE, "Seeking to read interval start point %s\n", |
| | 2920 | av_ts2timestr(target, &AV_TIME_BASE_Q)); |
| | 2921 | if ((ret = avformat_seek_file(fmt_ctx, -1, -INT64_MAX, target, INT64_MAX, 0)) < 0) { |
| | 2922 | av_log(NULL, AV_LOG_ERROR, "Could not seek to position %"PRId64": %s\n", |
| | 2923 | interval->start, av_err2str(ret)); |
| | 2924 | goto end; |
| | 2925 | } |
| | 2926 | } |
| | 2927 | |
| | 2928 | frame = av_frame_alloc(); |
| | 2929 | if (!frame) { |
| | 2930 | ret = AVERROR(ENOMEM); |
| | 2931 | goto end; |
| | 2932 | } |
| | 2933 | pkt = av_packet_alloc(); |
| | 2934 | if (!pkt) { |
| | 2935 | ret = AVERROR(ENOMEM); |
| | 2936 | goto end; |
| | 2937 | } |
| | 2938 | while (!av_read_frame(fmt_ctx, pkt)) { |
| | 2939 | if (fmt_ctx->nb_streams > nb_streams) { |
| | 2940 | REALLOCZ_ARRAY_STREAM(nb_streams_frames, nb_streams, fmt_ctx->nb_streams); |
| | 2941 | REALLOCZ_ARRAY_STREAM(nb_streams_packets, nb_streams, fmt_ctx->nb_streams); |
| | 2942 | REALLOCZ_ARRAY_STREAM(selected_streams, nb_streams, fmt_ctx->nb_streams); |
| | 2943 | nb_streams = fmt_ctx->nb_streams; |
| | 2944 | } |
| | 2945 | if (selected_streams[pkt->stream_index]) { |
| | 2946 | AVRational tb = ifile->streams[pkt->stream_index].st->time_base; |
| | 2947 | int64_t pts = pkt->pts != AV_NOPTS_VALUE ? pkt->pts : pkt->dts; |
| | 2948 | |
| | 2949 | if (pts != AV_NOPTS_VALUE) |
| | 2950 | *cur_ts = av_rescale_q(pts, tb, AV_TIME_BASE_Q); |
| | 2951 | |
| | 2952 | if (!has_start && *cur_ts != AV_NOPTS_VALUE) { |
| | 2953 | start = *cur_ts; |
| | 2954 | has_start = 1; |
| | 2955 | } |
| | 2956 | |
| | 2957 | if (has_start && !has_end && interval->end_is_offset) { |
| | 2958 | end = start + interval->end; |
| | 2959 | has_end = 1; |
| | 2960 | } |
| | 2961 | |
| | 2962 | if (interval->end_is_offset && interval->duration_frames) { |
| | 2963 | if (frame_count >= interval->end) |
| | 2964 | break; |
| | 2965 | } else if (has_end && *cur_ts != AV_NOPTS_VALUE && *cur_ts >= end) { |
| | 2966 | break; |
| | 2967 | } |
| | 2968 | |
| | 2969 | frame_count++; |
| | 2970 | if (do_read_packets) { |
| | 2971 | if (do_show_packets) |
| | 2972 | show_packet(w, ifile, pkt, i++); |
| | 2973 | nb_streams_packets[pkt->stream_index]++; |
| | 2974 | } |
| | 2975 | if (do_read_frames) { |
| | 2976 | int packet_new = 1; |
| | 2977 | FrameData *fd; |
| | 2978 | |
| | 2979 | pkt->opaque_ref = av_buffer_allocz(sizeof(*fd)); |
| | 2980 | if (!pkt->opaque_ref) { |
| | 2981 | ret = AVERROR(ENOMEM); |
| | 2982 | goto end; |
| | 2983 | } |
| | 2984 | fd = (FrameData*)pkt->opaque_ref->data; |
| | 2985 | fd->pkt_pos = pkt->pos; |
| | 2986 | fd->pkt_size = pkt->size; |
| | 2987 | |
| | 2988 | while (process_frame(w, ifile, frame, pkt, &packet_new) > 0); |
| | 2989 | } |
| | 2990 | } |
| | 2991 | av_packet_unref(pkt); |
| | 2992 | } |
| | 2993 | av_packet_unref(pkt); |
| | 2994 | //Flush remaining frames that are cached in the decoder |
| | 2995 | for (i = 0; i < ifile->nb_streams; i++) { |
| | 2996 | pkt->stream_index = i; |
| | 2997 | if (do_read_frames) { |
| | 2998 | while (process_frame(w, ifile, frame, pkt, &(int){1}) > 0); |
| | 2999 | if (ifile->streams[i].dec_ctx) |
| | 3000 | avcodec_flush_buffers(ifile->streams[i].dec_ctx); |
| | 3001 | } |
| | 3002 | } |
| | 3003 | |
| | 3004 | end: |
| | 3005 | av_frame_free(&frame); |
| | 3006 | av_packet_free(&pkt); |
| | 3007 | if (ret < 0) { |
| | 3008 | av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval "); |
| | 3009 | log_read_interval(interval, NULL, AV_LOG_ERROR); |
| | 3010 | } |
| | 3011 | return ret; |
| | 3012 | } |
| | 3013 | |
| | 3014 | static int read_packets(WriterContext *w, InputFile *ifile) |
| | 3015 | { |
| | 3016 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3017 | int i, ret = 0; |
| | 3018 | int64_t cur_ts = fmt_ctx->start_time; |
| | 3019 | |
| | 3020 | if (read_intervals_nb == 0) { |
| | 3021 | ReadInterval interval = (ReadInterval) { .has_start = 0, .has_end = 0 }; |
| | 3022 | ret = read_interval_packets(w, ifile, &interval, &cur_ts); |
| | 3023 | } else { |
| | 3024 | for (i = 0; i < read_intervals_nb; i++) { |
| | 3025 | ret = read_interval_packets(w, ifile, &read_intervals[i], &cur_ts); |
| | 3026 | if (ret < 0) |
| | 3027 | break; |
| | 3028 | } |
| | 3029 | } |
| | 3030 | |
| | 3031 | return ret; |
| | 3032 | } |
| | 3033 | |
| | 3034 | static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program) |
| | 3035 | { |
| | 3036 | AVStream *stream = ist->st; |
| | 3037 | AVCodecParameters *par; |
| | 3038 | AVCodecContext *dec_ctx; |
| | 3039 | char val_str[128]; |
| | 3040 | const char *s; |
| | 3041 | AVRational sar, dar; |
| | 3042 | AVBPrint pbuf; |
| | 3043 | const AVCodecDescriptor *cd; |
| | 3044 | int ret = 0; |
| | 3045 | const char *profile = NULL; |
| | 3046 | |
| | 3047 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 3048 | |
| | 3049 | writer_print_section_header(w, NULL, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM); |
| | 3050 | |
| | 3051 | print_int("index", stream->index); |
| | 3052 | |
| | 3053 | par = stream->codecpar; |
| | 3054 | dec_ctx = ist->dec_ctx; |
| | 3055 | if (cd = avcodec_descriptor_get(par->codec_id)) { |
| | 3056 | print_str("codec_name", cd->name); |
| | 3057 | if (!do_bitexact) { |
| | 3058 | print_str("codec_long_name", |
| | 3059 | cd->long_name ? cd->long_name : "unknown"); |
| | 3060 | } |
| | 3061 | } else { |
| | 3062 | print_str_opt("codec_name", "unknown"); |
| | 3063 | if (!do_bitexact) { |
| | 3064 | print_str_opt("codec_long_name", "unknown"); |
| | 3065 | } |
| | 3066 | } |
| | 3067 | |
| | 3068 | if (!do_bitexact && (profile = avcodec_profile_name(par->codec_id, par->profile))) |
| | 3069 | print_str("profile", profile); |
| | 3070 | else { |
| | 3071 | if (par->profile != AV_PROFILE_UNKNOWN) { |
| | 3072 | char profile_num[12]; |
| | 3073 | snprintf(profile_num, sizeof(profile_num), "%d", par->profile); |
| | 3074 | print_str("profile", profile_num); |
| | 3075 | } else |
| | 3076 | print_str_opt("profile", "unknown"); |
| | 3077 | } |
| | 3078 | |
| | 3079 | s = av_get_media_type_string(par->codec_type); |
| | 3080 | if (s) print_str ("codec_type", s); |
| | 3081 | else print_str_opt("codec_type", "unknown"); |
| | 3082 | |
| | 3083 | /* print AVI/FourCC tag */ |
| | 3084 | print_str("codec_tag_string", av_fourcc2str(par->codec_tag)); |
| | 3085 | print_fmt("codec_tag", "0x%04"PRIx32, par->codec_tag); |
| | 3086 | |
| | 3087 | switch (par->codec_type) { |
| | 3088 | case AVMEDIA_TYPE_VIDEO: |
| | 3089 | print_int("width", par->width); |
| | 3090 | print_int("height", par->height); |
| | 3091 | if (dec_ctx) { |
| | 3092 | print_int("coded_width", dec_ctx->coded_width); |
| | 3093 | print_int("coded_height", dec_ctx->coded_height); |
| | 3094 | print_int("closed_captions", !!(dec_ctx->properties & FF_CODEC_PROPERTY_CLOSED_CAPTIONS)); |
| | 3095 | print_int("film_grain", !!(dec_ctx->properties & FF_CODEC_PROPERTY_FILM_GRAIN)); |
| | 3096 | } |
| | 3097 | print_int("has_b_frames", par->video_delay); |
| | 3098 | sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, NULL); |
| | 3099 | if (sar.num) { |
| | 3100 | print_q("sample_aspect_ratio", sar, ':'); |
| | 3101 | av_reduce(&dar.num, &dar.den, |
| | 3102 | par->width * sar.num, |
| | 3103 | par->height * sar.den, |
| | 3104 | 1024*1024); |
| | 3105 | print_q("display_aspect_ratio", dar, ':'); |
| | 3106 | } else { |
| | 3107 | print_str_opt("sample_aspect_ratio", "N/A"); |
| | 3108 | print_str_opt("display_aspect_ratio", "N/A"); |
| | 3109 | } |
| | 3110 | s = av_get_pix_fmt_name(par->format); |
| | 3111 | if (s) print_str ("pix_fmt", s); |
| | 3112 | else print_str_opt("pix_fmt", "unknown"); |
| | 3113 | print_int("level", par->level); |
| | 3114 | |
| | 3115 | print_color_range(w, par->color_range); |
| | 3116 | print_color_space(w, par->color_space); |
| | 3117 | print_color_trc(w, par->color_trc); |
| | 3118 | print_primaries(w, par->color_primaries); |
| | 3119 | print_chroma_location(w, par->chroma_location); |
| | 3120 | |
| | 3121 | if (par->field_order == AV_FIELD_PROGRESSIVE) |
| | 3122 | print_str("field_order", "progressive"); |
| | 3123 | else if (par->field_order == AV_FIELD_TT) |
| | 3124 | print_str("field_order", "tt"); |
| | 3125 | else if (par->field_order == AV_FIELD_BB) |
| | 3126 | print_str("field_order", "bb"); |
| | 3127 | else if (par->field_order == AV_FIELD_TB) |
| | 3128 | print_str("field_order", "tb"); |
| | 3129 | else if (par->field_order == AV_FIELD_BT) |
| | 3130 | print_str("field_order", "bt"); |
| | 3131 | else |
| | 3132 | print_str_opt("field_order", "unknown"); |
| | 3133 | |
| | 3134 | if (dec_ctx) |
| | 3135 | print_int("refs", dec_ctx->refs); |
| | 3136 | break; |
| | 3137 | |
| | 3138 | case AVMEDIA_TYPE_AUDIO: |
| | 3139 | s = av_get_sample_fmt_name(par->format); |
| | 3140 | if (s) print_str ("sample_fmt", s); |
| | 3141 | else print_str_opt("sample_fmt", "unknown"); |
| | 3142 | print_val("sample_rate", par->sample_rate, unit_hertz_str); |
| | 3143 | print_int("channels", par->ch_layout.nb_channels); |
| | 3144 | |
| | 3145 | if (par->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { |
| | 3146 | av_channel_layout_describe(&par->ch_layout, val_str, sizeof(val_str)); |
| | 3147 | print_str ("channel_layout", val_str); |
| | 3148 | } else { |
| | 3149 | print_str_opt("channel_layout", "unknown"); |
| | 3150 | } |
| | 3151 | |
| | 3152 | print_int("bits_per_sample", av_get_bits_per_sample(par->codec_id)); |
| | 3153 | |
| | 3154 | print_int("initial_padding", par->initial_padding); |
| | 3155 | break; |
| | 3156 | |
| | 3157 | case AVMEDIA_TYPE_SUBTITLE: |
| | 3158 | if (par->width) |
| | 3159 | print_int("width", par->width); |
| | 3160 | else |
| | 3161 | print_str_opt("width", "N/A"); |
| | 3162 | if (par->height) |
| | 3163 | print_int("height", par->height); |
| | 3164 | else |
| | 3165 | print_str_opt("height", "N/A"); |
| | 3166 | break; |
| | 3167 | } |
| | 3168 | |
| | 3169 | if (show_private_data) { |
| | 3170 | if (dec_ctx && dec_ctx->codec->priv_class) |
| | 3171 | print_private_data(w, dec_ctx->priv_data); |
| | 3172 | if (fmt_ctx->iformat->priv_class) |
| | 3173 | print_private_data(w, fmt_ctx->priv_data); |
| | 3174 | } |
| | 3175 | |
| | 3176 | if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%x", stream->id); |
| | 3177 | else print_str_opt("id", "N/A"); |
| | 3178 | print_q("r_frame_rate", stream->r_frame_rate, '/'); |
| | 3179 | print_q("avg_frame_rate", stream->avg_frame_rate, '/'); |
| | 3180 | print_q("time_base", stream->time_base, '/'); |
| | 3181 | print_ts ("start_pts", stream->start_time); |
| | 3182 | print_time("start_time", stream->start_time, &stream->time_base); |
| | 3183 | print_ts ("duration_ts", stream->duration); |
| | 3184 | print_time("duration", stream->duration, &stream->time_base); |
| | 3185 | if (par->bit_rate > 0) print_val ("bit_rate", par->bit_rate, unit_bit_per_second_str); |
| | 3186 | else print_str_opt("bit_rate", "N/A"); |
| | 3187 | if (dec_ctx && dec_ctx->rc_max_rate > 0) |
| | 3188 | print_val ("max_bit_rate", dec_ctx->rc_max_rate, unit_bit_per_second_str); |
| | 3189 | else |
| | 3190 | print_str_opt("max_bit_rate", "N/A"); |
| | 3191 | if (dec_ctx && dec_ctx->bits_per_raw_sample > 0) print_fmt("bits_per_raw_sample", "%d", dec_ctx->bits_per_raw_sample); |
| | 3192 | else print_str_opt("bits_per_raw_sample", "N/A"); |
| | 3193 | if (stream->nb_frames) print_fmt ("nb_frames", "%"PRId64, stream->nb_frames); |
| | 3194 | else print_str_opt("nb_frames", "N/A"); |
| | 3195 | if (nb_streams_frames[stream_idx]) print_fmt ("nb_read_frames", "%"PRIu64, nb_streams_frames[stream_idx]); |
| | 3196 | else print_str_opt("nb_read_frames", "N/A"); |
| | 3197 | if (nb_streams_packets[stream_idx]) print_fmt ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]); |
| | 3198 | else print_str_opt("nb_read_packets", "N/A"); |
| | 3199 | if (do_show_data) |
| | 3200 | writer_print_data(w, "extradata", par->extradata, |
| | 3201 | par->extradata_size); |
| | 3202 | |
| | 3203 | if (par->extradata_size > 0) { |
| | 3204 | print_int("extradata_size", par->extradata_size); |
| | 3205 | writer_print_data_hash(w, "extradata_hash", par->extradata, |
| | 3206 | par->extradata_size); |
| | 3207 | } |
| | 3208 | |
| | 3209 | /* Print disposition information */ |
| | 3210 | #define PRINT_DISPOSITION(flagname, name) do { \ |
| | 3211 | print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \ |
| | 3212 | } while (0) |
| | 3213 | |
| | 3214 | if (do_show_stream_disposition) { |
| | 3215 | writer_print_section_header(w, NULL, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION); |
| | 3216 | PRINT_DISPOSITION(DEFAULT, "default"); |
| | 3217 | PRINT_DISPOSITION(DUB, "dub"); |
| | 3218 | PRINT_DISPOSITION(ORIGINAL, "original"); |
| | 3219 | PRINT_DISPOSITION(COMMENT, "comment"); |
| | 3220 | PRINT_DISPOSITION(LYRICS, "lyrics"); |
| | 3221 | PRINT_DISPOSITION(KARAOKE, "karaoke"); |
| | 3222 | PRINT_DISPOSITION(FORCED, "forced"); |
| | 3223 | PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired"); |
| | 3224 | PRINT_DISPOSITION(VISUAL_IMPAIRED, "visual_impaired"); |
| | 3225 | PRINT_DISPOSITION(CLEAN_EFFECTS, "clean_effects"); |
| | 3226 | PRINT_DISPOSITION(ATTACHED_PIC, "attached_pic"); |
| | 3227 | PRINT_DISPOSITION(TIMED_THUMBNAILS, "timed_thumbnails"); |
| | 3228 | PRINT_DISPOSITION(NON_DIEGETIC, "non_diegetic"); |
| | 3229 | PRINT_DISPOSITION(CAPTIONS, "captions"); |
| | 3230 | PRINT_DISPOSITION(DESCRIPTIONS, "descriptions"); |
| | 3231 | PRINT_DISPOSITION(METADATA, "metadata"); |
| | 3232 | PRINT_DISPOSITION(DEPENDENT, "dependent"); |
| | 3233 | PRINT_DISPOSITION(STILL_IMAGE, "still_image"); |
| | 3234 | writer_print_section_footer(w); |
| | 3235 | } |
| | 3236 | |
| | 3237 | if (do_show_stream_tags) |
| | 3238 | ret = show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS); |
| | 3239 | |
| | 3240 | if (stream->codecpar->nb_coded_side_data) { |
| | 3241 | writer_print_section_header(w, NULL, SECTION_ID_STREAM_SIDE_DATA_LIST); |
| | 3242 | for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) { |
| | 3243 | print_pkt_side_data(w, stream->codecpar, &stream->codecpar->coded_side_data[i], |
| | 3244 | SECTION_ID_STREAM_SIDE_DATA); |
| | 3245 | writer_print_section_footer(w); |
| | 3246 | } |
| | 3247 | writer_print_section_footer(w); |
| | 3248 | } |
| | 3249 | |
| | 3250 | writer_print_section_footer(w); |
| | 3251 | av_bprint_finalize(&pbuf, NULL); |
| | 3252 | fflush(stdout); |
| | 3253 | |
| | 3254 | return ret; |
| | 3255 | } |
| | 3256 | |
| | 3257 | static int show_streams(WriterContext *w, InputFile *ifile) |
| | 3258 | { |
| | 3259 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3260 | int i, ret = 0; |
| | 3261 | |
| | 3262 | writer_print_section_header(w, NULL, SECTION_ID_STREAMS); |
| | 3263 | for (i = 0; i < ifile->nb_streams; i++) |
| | 3264 | if (selected_streams[i]) { |
| | 3265 | ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0); |
| | 3266 | if (ret < 0) |
| | 3267 | break; |
| | 3268 | } |
| | 3269 | writer_print_section_footer(w); |
| | 3270 | |
| | 3271 | return ret; |
| | 3272 | } |
| | 3273 | |
| | 3274 | static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program) |
| | 3275 | { |
| | 3276 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3277 | int i, ret = 0; |
| | 3278 | |
| | 3279 | writer_print_section_header(w, NULL, SECTION_ID_PROGRAM); |
| | 3280 | print_int("program_id", program->id); |
| | 3281 | print_int("program_num", program->program_num); |
| | 3282 | print_int("nb_streams", program->nb_stream_indexes); |
| | 3283 | print_int("pmt_pid", program->pmt_pid); |
| | 3284 | print_int("pcr_pid", program->pcr_pid); |
| | 3285 | if (do_show_program_tags) |
| | 3286 | ret = show_tags(w, program->metadata, SECTION_ID_PROGRAM_TAGS); |
| | 3287 | if (ret < 0) |
| | 3288 | goto end; |
| | 3289 | |
| | 3290 | writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS); |
| | 3291 | for (i = 0; i < program->nb_stream_indexes; i++) { |
| | 3292 | if (selected_streams[program->stream_index[i]]) { |
| | 3293 | ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], 1); |
| | 3294 | if (ret < 0) |
| | 3295 | break; |
| | 3296 | } |
| | 3297 | } |
| | 3298 | writer_print_section_footer(w); |
| | 3299 | |
| | 3300 | end: |
| | 3301 | writer_print_section_footer(w); |
| | 3302 | return ret; |
| | 3303 | } |
| | 3304 | |
| | 3305 | static int show_programs(WriterContext *w, InputFile *ifile) |
| | 3306 | { |
| | 3307 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3308 | int i, ret = 0; |
| | 3309 | |
| | 3310 | writer_print_section_header(w, NULL, SECTION_ID_PROGRAMS); |
| | 3311 | for (i = 0; i < fmt_ctx->nb_programs; i++) { |
| | 3312 | AVProgram *program = fmt_ctx->programs[i]; |
| | 3313 | if (!program) |
| | 3314 | continue; |
| | 3315 | ret = show_program(w, ifile, program); |
| | 3316 | if (ret < 0) |
| | 3317 | break; |
| | 3318 | } |
| | 3319 | writer_print_section_footer(w); |
| | 3320 | return ret; |
| | 3321 | } |
| | 3322 | |
| | 3323 | static int show_chapters(WriterContext *w, InputFile *ifile) |
| | 3324 | { |
| | 3325 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3326 | int i, ret = 0; |
| | 3327 | |
| | 3328 | writer_print_section_header(w, NULL, SECTION_ID_CHAPTERS); |
| | 3329 | for (i = 0; i < fmt_ctx->nb_chapters; i++) { |
| | 3330 | AVChapter *chapter = fmt_ctx->chapters[i]; |
| | 3331 | |
| | 3332 | writer_print_section_header(w, NULL, SECTION_ID_CHAPTER); |
| | 3333 | print_int("id", chapter->id); |
| | 3334 | print_q ("time_base", chapter->time_base, '/'); |
| | 3335 | print_int("start", chapter->start); |
| | 3336 | print_time("start_time", chapter->start, &chapter->time_base); |
| | 3337 | print_int("end", chapter->end); |
| | 3338 | print_time("end_time", chapter->end, &chapter->time_base); |
| | 3339 | if (do_show_chapter_tags) |
| | 3340 | ret = show_tags(w, chapter->metadata, SECTION_ID_CHAPTER_TAGS); |
| | 3341 | writer_print_section_footer(w); |
| | 3342 | } |
| | 3343 | writer_print_section_footer(w); |
| | 3344 | |
| | 3345 | return ret; |
| | 3346 | } |
| | 3347 | |
| | 3348 | static int show_format(WriterContext *w, InputFile *ifile) |
| | 3349 | { |
| | 3350 | AVFormatContext *fmt_ctx = ifile->fmt_ctx; |
| | 3351 | char val_str[128]; |
| | 3352 | int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1; |
| | 3353 | int ret = 0; |
| | 3354 | |
| | 3355 | writer_print_section_header(w, NULL, SECTION_ID_FORMAT); |
| | 3356 | print_str_validate("filename", fmt_ctx->url); |
| | 3357 | print_int("nb_streams", fmt_ctx->nb_streams); |
| | 3358 | print_int("nb_programs", fmt_ctx->nb_programs); |
| | 3359 | print_str("format_name", fmt_ctx->iformat->name); |
| | 3360 | if (!do_bitexact) { |
| | 3361 | if (fmt_ctx->iformat->long_name) print_str ("format_long_name", fmt_ctx->iformat->long_name); |
| | 3362 | else print_str_opt("format_long_name", "unknown"); |
| | 3363 | } |
| | 3364 | print_time("start_time", fmt_ctx->start_time, &AV_TIME_BASE_Q); |
| | 3365 | print_time("duration", fmt_ctx->duration, &AV_TIME_BASE_Q); |
| | 3366 | if (size >= 0) print_val ("size", size, unit_byte_str); |
| | 3367 | else print_str_opt("size", "N/A"); |
| | 3368 | if (fmt_ctx->bit_rate > 0) print_val ("bit_rate", fmt_ctx->bit_rate, unit_bit_per_second_str); |
| | 3369 | else print_str_opt("bit_rate", "N/A"); |
| | 3370 | print_int("probe_score", fmt_ctx->probe_score); |
| | 3371 | if (do_show_format_tags) |
| | 3372 | ret = show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS); |
| | 3373 | |
| | 3374 | writer_print_section_footer(w); |
| | 3375 | fflush(stdout); |
| | 3376 | return ret; |
| | 3377 | } |
| | 3378 | |
| | 3379 | static void show_error(WriterContext *w, int err) |
| | 3380 | { |
| | 3381 | writer_print_section_header(w, NULL, SECTION_ID_ERROR); |
| | 3382 | print_int("code", err); |
| | 3383 | print_str("string", av_err2str(err)); |
| | 3384 | writer_print_section_footer(w); |
| | 3385 | } |
| | 3386 | |
| | 3387 | static int open_input_file(InputFile *ifile, const char *filename, |
| | 3388 | const char *print_filename) |
| | 3389 | { |
| | 3390 | int err, i; |
| | 3391 | AVFormatContext *fmt_ctx = NULL; |
| | 3392 | const AVDictionaryEntry *t = NULL; |
| | 3393 | int scan_all_pmts_set = 0; |
| | 3394 | |
| | 3395 | fmt_ctx = avformat_alloc_context(); |
| | 3396 | if (!fmt_ctx) |
| | 3397 | return AVERROR(ENOMEM); |
| | 3398 | |
| | 3399 | if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) { |
| | 3400 | av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE); |
| | 3401 | scan_all_pmts_set = 1; |
| | 3402 | } |
| | 3403 | if ((err = avformat_open_input(&fmt_ctx, filename, |
| | 3404 | iformat, &format_opts)) < 0) { |
| | 3405 | print_error(filename, err); |
| | 3406 | return err; |
| | 3407 | } |
| | 3408 | if (print_filename) { |
| | 3409 | av_freep(&fmt_ctx->url); |
| | 3410 | fmt_ctx->url = av_strdup(print_filename); |
| | 3411 | } |
| | 3412 | ifile->fmt_ctx = fmt_ctx; |
| | 3413 | if (scan_all_pmts_set) |
| | 3414 | av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE); |
| | 3415 | while ((t = av_dict_iterate(format_opts, t))) |
| | 3416 | av_log(NULL, AV_LOG_WARNING, "Option %s skipped - not known to demuxer.\n", t->key); |
| | 3417 | |
| | 3418 | if (find_stream_info) { |
| | 3419 | AVDictionary **opts; |
| | 3420 | int orig_nb_streams = fmt_ctx->nb_streams; |
| | 3421 | |
| | 3422 | err = setup_find_stream_info_opts(fmt_ctx, codec_opts, &opts); |
| | 3423 | if (err < 0) |
| | 3424 | return err; |
| | 3425 | |
| | 3426 | err = avformat_find_stream_info(fmt_ctx, opts); |
| | 3427 | |
| | 3428 | for (i = 0; i < orig_nb_streams; i++) |
| | 3429 | av_dict_free(&opts[i]); |
| | 3430 | av_freep(&opts); |
| | 3431 | |
| | 3432 | if (err < 0) { |
| | 3433 | print_error(filename, err); |
| | 3434 | return err; |
| | 3435 | } |
| | 3436 | } |
| | 3437 | |
| | 3438 | av_dump_format(fmt_ctx, 0, filename, 0); |
| | 3439 | |
| | 3440 | ifile->streams = av_calloc(fmt_ctx->nb_streams, sizeof(*ifile->streams)); |
| | 3441 | if (!ifile->streams) |
| | 3442 | exit(1); |
| | 3443 | ifile->nb_streams = fmt_ctx->nb_streams; |
| | 3444 | |
| | 3445 | /* bind a decoder to each input stream */ |
| | 3446 | for (i = 0; i < fmt_ctx->nb_streams; i++) { |
| | 3447 | InputStream *ist = &ifile->streams[i]; |
| | 3448 | AVStream *stream = fmt_ctx->streams[i]; |
| | 3449 | const AVCodec *codec; |
| | 3450 | |
| | 3451 | ist->st = stream; |
| | 3452 | |
| | 3453 | if (stream->codecpar->codec_id == AV_CODEC_ID_PROBE) { |
| | 3454 | av_log(NULL, AV_LOG_WARNING, |
| | 3455 | "Failed to probe codec for input stream %d\n", |
| | 3456 | stream->index); |
| | 3457 | continue; |
| | 3458 | } |
| | 3459 | |
| | 3460 | codec = avcodec_find_decoder(stream->codecpar->codec_id); |
| | 3461 | if (!codec) { |
| | 3462 | av_log(NULL, AV_LOG_WARNING, |
| | 3463 | "Unsupported codec with id %d for input stream %d\n", |
| | 3464 | stream->codecpar->codec_id, stream->index); |
| | 3465 | continue; |
| | 3466 | } |
| | 3467 | { |
| | 3468 | AVDictionary *opts; |
| | 3469 | |
| | 3470 | err = filter_codec_opts(codec_opts, stream->codecpar->codec_id, |
| | 3471 | fmt_ctx, stream, codec, &opts); |
| | 3472 | if (err < 0) |
| | 3473 | exit(1); |
| | 3474 | |
| | 3475 | ist->dec_ctx = avcodec_alloc_context3(codec); |
| | 3476 | if (!ist->dec_ctx) |
| | 3477 | exit(1); |
| | 3478 | |
| | 3479 | err = avcodec_parameters_to_context(ist->dec_ctx, stream->codecpar); |
| | 3480 | if (err < 0) |
| | 3481 | exit(1); |
| | 3482 | |
| | 3483 | if (do_show_log) { |
| | 3484 | // For loging it is needed to disable at least frame threads as otherwise |
| | 3485 | // the log information would need to be reordered and matches up to contexts and frames |
| | 3486 | // That is in fact possible but not trivial |
| | 3487 | av_dict_set(&codec_opts, "threads", "1", 0); |
| | 3488 | } |
| | 3489 | |
| | 3490 | av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); |
| | 3491 | |
| | 3492 | ist->dec_ctx->pkt_timebase = stream->time_base; |
| | 3493 | |
| | 3494 | if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) { |
| | 3495 | av_log(NULL, AV_LOG_WARNING, "Could not open codec for input stream %d\n", |
| | 3496 | stream->index); |
| | 3497 | exit(1); |
| | 3498 | } |
| | 3499 | |
| | 3500 | if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) { |
| | 3501 | av_log(NULL, AV_LOG_ERROR, "Option %s for input stream %d not found\n", |
| | 3502 | t->key, stream->index); |
| | 3503 | return AVERROR_OPTION_NOT_FOUND; |
| | 3504 | } |
| | 3505 | } |
| | 3506 | } |
| | 3507 | |
| | 3508 | ifile->fmt_ctx = fmt_ctx; |
| | 3509 | return 0; |
| | 3510 | } |
| | 3511 | |
| | 3512 | static void close_input_file(InputFile *ifile) |
| | 3513 | { |
| | 3514 | int i; |
| | 3515 | |
| | 3516 | /* close decoder for each stream */ |
| | 3517 | for (i = 0; i < ifile->nb_streams; i++) |
| | 3518 | avcodec_free_context(&ifile->streams[i].dec_ctx); |
| | 3519 | |
| | 3520 | av_freep(&ifile->streams); |
| | 3521 | ifile->nb_streams = 0; |
| | 3522 | |
| | 3523 | avformat_close_input(&ifile->fmt_ctx); |
| | 3524 | } |
| | 3525 | |
| | 3526 | static int probe_file(WriterContext *wctx, const char *filename, |
| | 3527 | const char *print_filename) |
| | 3528 | { |
| | 3529 | InputFile ifile = { 0 }; |
| | 3530 | int ret, i; |
| | 3531 | int section_id; |
| | 3532 | |
| | 3533 | do_read_frames = do_show_frames || do_count_frames; |
| | 3534 | do_read_packets = do_show_packets || do_count_packets; |
| | 3535 | |
| | 3536 | ret = open_input_file(&ifile, filename, print_filename); |
| | 3537 | if (ret < 0) |
| | 3538 | goto end; |
| | 3539 | |
| | 3540 | #define CHECK_END if (ret < 0) goto end |
| | 3541 | |
| | 3542 | nb_streams = ifile.fmt_ctx->nb_streams; |
| | 3543 | REALLOCZ_ARRAY_STREAM(nb_streams_frames,0,ifile.fmt_ctx->nb_streams); |
| | 3544 | REALLOCZ_ARRAY_STREAM(nb_streams_packets,0,ifile.fmt_ctx->nb_streams); |
| | 3545 | REALLOCZ_ARRAY_STREAM(selected_streams,0,ifile.fmt_ctx->nb_streams); |
| | 3546 | |
| | 3547 | for (i = 0; i < ifile.fmt_ctx->nb_streams; i++) { |
| | 3548 | if (stream_specifier) { |
| | 3549 | ret = avformat_match_stream_specifier(ifile.fmt_ctx, |
| | 3550 | ifile.fmt_ctx->streams[i], |
| | 3551 | stream_specifier); |
| | 3552 | CHECK_END; |
| | 3553 | else |
| | 3554 | selected_streams[i] = ret; |
| | 3555 | ret = 0; |
| | 3556 | } else { |
| | 3557 | selected_streams[i] = 1; |
| | 3558 | } |
| | 3559 | if (!selected_streams[i]) |
| | 3560 | ifile.fmt_ctx->streams[i]->discard = AVDISCARD_ALL; |
| | 3561 | } |
| | 3562 | |
| | 3563 | if (do_read_frames || do_read_packets) { |
| | 3564 | if (do_show_frames && do_show_packets && |
| | 3565 | wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER) |
| | 3566 | section_id = SECTION_ID_PACKETS_AND_FRAMES; |
| | 3567 | else if (do_show_packets && !do_show_frames) |
| | 3568 | section_id = SECTION_ID_PACKETS; |
| | 3569 | else // (!do_show_packets && do_show_frames) |
| | 3570 | section_id = SECTION_ID_FRAMES; |
| | 3571 | if (do_show_frames || do_show_packets) |
| | 3572 | writer_print_section_header(wctx, NULL, section_id); |
| | 3573 | ret = read_packets(wctx, &ifile); |
| | 3574 | if (do_show_frames || do_show_packets) |
| | 3575 | writer_print_section_footer(wctx); |
| | 3576 | CHECK_END; |
| | 3577 | } |
| | 3578 | |
| | 3579 | if (do_show_programs) { |
| | 3580 | ret = show_programs(wctx, &ifile); |
| | 3581 | CHECK_END; |
| | 3582 | } |
| | 3583 | |
| | 3584 | if (do_show_streams) { |
| | 3585 | ret = show_streams(wctx, &ifile); |
| | 3586 | CHECK_END; |
| | 3587 | } |
| | 3588 | if (do_show_chapters) { |
| | 3589 | ret = show_chapters(wctx, &ifile); |
| | 3590 | CHECK_END; |
| | 3591 | } |
| | 3592 | if (do_show_format) { |
| | 3593 | ret = show_format(wctx, &ifile); |
| | 3594 | CHECK_END; |
| | 3595 | } |
| | 3596 | |
| | 3597 | end: |
| | 3598 | if (ifile.fmt_ctx) |
| | 3599 | close_input_file(&ifile); |
| | 3600 | av_freep(&nb_streams_frames); |
| | 3601 | av_freep(&nb_streams_packets); |
| | 3602 | av_freep(&selected_streams); |
| | 3603 | |
| | 3604 | return ret; |
| | 3605 | } |
| | 3606 | |
| | 3607 | static void show_usage(void) |
| | 3608 | { |
| | 3609 | av_log(NULL, AV_LOG_INFO, "Simple multimedia streams analyzer\n"); |
| | 3610 | av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] INPUT_FILE\n", program_name); |
| | 3611 | av_log(NULL, AV_LOG_INFO, "\n"); |
| | 3612 | } |
| | 3613 | |
| | 3614 | static void ffprobe_show_program_version(WriterContext *w) |
| | 3615 | { |
| | 3616 | AVBPrint pbuf; |
| | 3617 | av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); |
| | 3618 | |
| | 3619 | writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_VERSION); |
| | 3620 | print_str("version", FFMPEG_VERSION); |
| | 3621 | print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers", |
| | 3622 | program_birth_year, CONFIG_THIS_YEAR); |
| | 3623 | print_str("compiler_ident", CC_IDENT); |
| | 3624 | print_str("configuration", FFMPEG_CONFIGURATION); |
| | 3625 | writer_print_section_footer(w); |
| | 3626 | |
| | 3627 | av_bprint_finalize(&pbuf, NULL); |
| | 3628 | } |
| | 3629 | |
| | 3630 | #define SHOW_LIB_VERSION(libname, LIBNAME) \ |
| | 3631 | do { \ |
| | 3632 | if (CONFIG_##LIBNAME) { \ |
| | 3633 | unsigned int version = libname##_version(); \ |
| | 3634 | writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSION); \ |
| | 3635 | print_str("name", "lib" #libname); \ |
| | 3636 | print_int("major", LIB##LIBNAME##_VERSION_MAJOR); \ |
| | 3637 | print_int("minor", LIB##LIBNAME##_VERSION_MINOR); \ |
| | 3638 | print_int("micro", LIB##LIBNAME##_VERSION_MICRO); \ |
| | 3639 | print_int("version", version); \ |
| | 3640 | print_str("ident", LIB##LIBNAME##_IDENT); \ |
| | 3641 | writer_print_section_footer(w); \ |
| | 3642 | } \ |
| | 3643 | } while (0) |
| | 3644 | |
| | 3645 | static void ffprobe_show_library_versions(WriterContext *w) |
| | 3646 | { |
| | 3647 | writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSIONS); |
| | 3648 | SHOW_LIB_VERSION(avutil, AVUTIL); |
| | 3649 | SHOW_LIB_VERSION(avcodec, AVCODEC); |
| | 3650 | SHOW_LIB_VERSION(avformat, AVFORMAT); |
| | 3651 | SHOW_LIB_VERSION(avdevice, AVDEVICE); |
| | 3652 | SHOW_LIB_VERSION(avfilter, AVFILTER); |
| | 3653 | SHOW_LIB_VERSION(swscale, SWSCALE); |
| | 3654 | SHOW_LIB_VERSION(swresample, SWRESAMPLE); |
| | 3655 | SHOW_LIB_VERSION(postproc, POSTPROC); |
| | 3656 | writer_print_section_footer(w); |
| | 3657 | } |
| | 3658 | |
| | 3659 | #define PRINT_PIX_FMT_FLAG(flagname, name) \ |
| | 3660 | do { \ |
| | 3661 | print_int(name, !!(pixdesc->flags & AV_PIX_FMT_FLAG_##flagname)); \ |
| | 3662 | } while (0) |
| | 3663 | |
| | 3664 | static void ffprobe_show_pixel_formats(WriterContext *w) |
| | 3665 | { |
| | 3666 | const AVPixFmtDescriptor *pixdesc = NULL; |
| | 3667 | int i, n; |
| | 3668 | |
| | 3669 | writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMATS); |
| | 3670 | while (pixdesc = av_pix_fmt_desc_next(pixdesc)) { |
| | 3671 | writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT); |
| | 3672 | print_str("name", pixdesc->name); |
| | 3673 | print_int("nb_components", pixdesc->nb_components); |
| | 3674 | if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) { |
| | 3675 | print_int ("log2_chroma_w", pixdesc->log2_chroma_w); |
| | 3676 | print_int ("log2_chroma_h", pixdesc->log2_chroma_h); |
| | 3677 | } else { |
| | 3678 | print_str_opt("log2_chroma_w", "N/A"); |
| | 3679 | print_str_opt("log2_chroma_h", "N/A"); |
| | 3680 | } |
| | 3681 | n = av_get_bits_per_pixel(pixdesc); |
| | 3682 | if (n) print_int ("bits_per_pixel", n); |
| | 3683 | else print_str_opt("bits_per_pixel", "N/A"); |
| | 3684 | if (do_show_pixel_format_flags) { |
| | 3685 | writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_FLAGS); |
| | 3686 | PRINT_PIX_FMT_FLAG(BE, "big_endian"); |
| | 3687 | PRINT_PIX_FMT_FLAG(PAL, "palette"); |
| | 3688 | PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream"); |
| | 3689 | PRINT_PIX_FMT_FLAG(HWACCEL, "hwaccel"); |
| | 3690 | PRINT_PIX_FMT_FLAG(PLANAR, "planar"); |
| | 3691 | PRINT_PIX_FMT_FLAG(RGB, "rgb"); |
| | 3692 | PRINT_PIX_FMT_FLAG(ALPHA, "alpha"); |
| | 3693 | writer_print_section_footer(w); |
| | 3694 | } |
| | 3695 | if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) { |
| | 3696 | writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENTS); |
| | 3697 | for (i = 0; i < pixdesc->nb_components; i++) { |
| | 3698 | writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENT); |
| | 3699 | print_int("index", i + 1); |
| | 3700 | print_int("bit_depth", pixdesc->comp[i].depth); |
| | 3701 | writer_print_section_footer(w); |
| | 3702 | } |
| | 3703 | writer_print_section_footer(w); |
| | 3704 | } |
| | 3705 | writer_print_section_footer(w); |
| | 3706 | } |
| | 3707 | writer_print_section_footer(w); |
| | 3708 | } |
| | 3709 | |
| | 3710 | static int opt_show_optional_fields(void *optctx, const char *opt, const char *arg) |
| | 3711 | { |
| | 3712 | if (!av_strcasecmp(arg, "always")) show_optional_fields = SHOW_OPTIONAL_FIELDS_ALWAYS; |
| | 3713 | else if (!av_strcasecmp(arg, "never")) show_optional_fields = SHOW_OPTIONAL_FIELDS_NEVER; |
| | 3714 | else if (!av_strcasecmp(arg, "auto")) show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; |
| | 3715 | |
| | 3716 | if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) { |
| | 3717 | double num; |
| | 3718 | int ret = parse_number("show_optional_fields", arg, OPT_INT, |
| | 3719 | SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS, &num); |
| | 3720 | if (ret < 0) |
| | 3721 | return ret; |
| | 3722 | show_optional_fields = num; |
| | 3723 | } |
| | 3724 | return 0; |
| | 3725 | } |
| | 3726 | |
| | 3727 | static int opt_format(void *optctx, const char *opt, const char *arg) |
| | 3728 | { |
| | 3729 | iformat = av_find_input_format(arg); |
| | 3730 | if (!iformat) { |
| | 3731 | av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg); |
| | 3732 | return AVERROR(EINVAL); |
| | 3733 | } |
| | 3734 | return 0; |
| | 3735 | } |
| | 3736 | |
| | 3737 | static inline void mark_section_show_entries(SectionID section_id, |
| | 3738 | int show_all_entries, AVDictionary *entries) |
| | 3739 | { |
| | 3740 | struct section *section = §ions[section_id]; |
| | 3741 | |
| | 3742 | section->show_all_entries = show_all_entries; |
| | 3743 | if (show_all_entries) { |
| | 3744 | for (const SectionID *id = section->children_ids; *id != -1; id++) |
| | 3745 | mark_section_show_entries(*id, show_all_entries, entries); |
| | 3746 | } else { |
| | 3747 | av_dict_copy(§ion->entries_to_show, entries, 0); |
| | 3748 | } |
| | 3749 | } |
| | 3750 | |
| | 3751 | static int match_section(const char *section_name, |
| | 3752 | int show_all_entries, AVDictionary *entries) |
| | 3753 | { |
| | 3754 | int i, ret = 0; |
| | 3755 | |
| | 3756 | for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) { |
| | 3757 | const struct section *section = §ions[i]; |
| | 3758 | if (!strcmp(section_name, section->name) || |
| | 3759 | (section->unique_name && !strcmp(section_name, section->unique_name))) { |
| | 3760 | av_log(NULL, AV_LOG_DEBUG, |
| | 3761 | "'%s' matches section with unique name '%s'\n", section_name, |
| | 3762 | (char *)av_x_if_null(section->unique_name, section->name)); |
| | 3763 | ret++; |
| | 3764 | mark_section_show_entries(section->id, show_all_entries, entries); |
| | 3765 | } |
| | 3766 | } |
| | 3767 | return ret; |
| | 3768 | } |
| | 3769 | |
| | 3770 | static int opt_show_entries(void *optctx, const char *opt, const char *arg) |
| | 3771 | { |
| | 3772 | const char *p = arg; |
| | 3773 | int ret = 0; |
| | 3774 | |
| | 3775 | while (*p) { |
| | 3776 | AVDictionary *entries = NULL; |
| | 3777 | char *section_name = av_get_token(&p, "=:"); |
| | 3778 | int show_all_entries = 0; |
| | 3779 | |
| | 3780 | if (!section_name) { |
| | 3781 | av_log(NULL, AV_LOG_ERROR, |
| | 3782 | "Missing section name for option '%s'\n", opt); |
| | 3783 | return AVERROR(EINVAL); |
| | 3784 | } |
| | 3785 | |
| | 3786 | if (*p == '=') { |
| | 3787 | p++; |
| | 3788 | while (*p && *p != ':') { |
| | 3789 | char *entry = av_get_token(&p, ",:"); |
| | 3790 | if (!entry) |
| | 3791 | break; |
| | 3792 | av_log(NULL, AV_LOG_VERBOSE, |
| | 3793 | "Adding '%s' to the entries to show in section '%s'\n", |
| | 3794 | entry, section_name); |
| | 3795 | av_dict_set(&entries, entry, "", AV_DICT_DONT_STRDUP_KEY); |
| | 3796 | if (*p == ',') |
| | 3797 | p++; |
| | 3798 | } |
| | 3799 | } else { |
| | 3800 | show_all_entries = 1; |
| | 3801 | } |
| | 3802 | |
| | 3803 | ret = match_section(section_name, show_all_entries, entries); |
| | 3804 | if (ret == 0) { |
| | 3805 | av_log(NULL, AV_LOG_ERROR, "No match for section '%s'\n", section_name); |
| | 3806 | ret = AVERROR(EINVAL); |
| | 3807 | } |
| | 3808 | av_dict_free(&entries); |
| | 3809 | av_free(section_name); |
| | 3810 | |
| | 3811 | if (ret <= 0) |
| | 3812 | break; |
| | 3813 | if (*p) |
| | 3814 | p++; |
| | 3815 | } |
| | 3816 | |
| | 3817 | return ret; |
| | 3818 | } |
| | 3819 | |
| | 3820 | static int opt_input_file(void *optctx, const char *arg) |
| | 3821 | { |
| | 3822 | if (input_filename) { |
| | 3823 | av_log(NULL, AV_LOG_ERROR, |
| | 3824 | "Argument '%s' provided as input filename, but '%s' was already specified.\n", |
| | 3825 | arg, input_filename); |
| | 3826 | return AVERROR(EINVAL); |
| | 3827 | } |
| | 3828 | if (!strcmp(arg, "-")) |
| | 3829 | arg = "fd:"; |
| | 3830 | input_filename = arg; |
| | 3831 | |
| | 3832 | return 0; |
| | 3833 | } |
| | 3834 | |
| | 3835 | static int opt_input_file_i(void *optctx, const char *opt, const char *arg) |
| | 3836 | { |
| | 3837 | opt_input_file(optctx, arg); |
| | 3838 | return 0; |
| | 3839 | } |
| | 3840 | |
| | 3841 | static int opt_output_file_o(void *optctx, const char *opt, const char *arg) |
| | 3842 | { |
| | 3843 | if (output_filename) { |
| | 3844 | av_log(NULL, AV_LOG_ERROR, |
| | 3845 | "Argument '%s' provided as output filename, but '%s' was already specified.\n", |
| | 3846 | arg, output_filename); |
| | 3847 | return AVERROR(EINVAL); |
| | 3848 | } |
| | 3849 | if (!strcmp(arg, "-")) |
| | 3850 | arg = "fd:"; |
| | 3851 | output_filename = arg; |
| | 3852 | |
| | 3853 | return 0; |
| | 3854 | } |
| | 3855 | |
| | 3856 | static int opt_print_filename(void *optctx, const char *opt, const char *arg) |
| | 3857 | { |
| | 3858 | print_input_filename = arg; |
| | 3859 | return 0; |
| | 3860 | } |
| | 3861 | |
| | 3862 | /** |
| | 3863 | * Parse interval specification, according to the format: |
| | 3864 | * INTERVAL ::= [START|+START_OFFSET][%[END|+END_OFFSET]] |
| | 3865 | * INTERVALS ::= INTERVAL[,INTERVALS] |
| | 3866 | */ |
| | 3867 | static int parse_read_interval(const char *interval_spec, |
| | 3868 | ReadInterval *interval) |
| | 3869 | { |
| | 3870 | int ret = 0; |
| | 3871 | char *next, *p, *spec = av_strdup(interval_spec); |
| | 3872 | if (!spec) |
| | 3873 | return AVERROR(ENOMEM); |
| | 3874 | |
| | 3875 | if (!*spec) { |
| | 3876 | av_log(NULL, AV_LOG_ERROR, "Invalid empty interval specification\n"); |
| | 3877 | ret = AVERROR(EINVAL); |
| | 3878 | goto end; |
| | 3879 | } |
| | 3880 | |
| | 3881 | p = spec; |
| | 3882 | next = strchr(spec, '%'); |
| | 3883 | if (next) |
| | 3884 | *next++ = 0; |
| | 3885 | |
| | 3886 | /* parse first part */ |
| | 3887 | if (*p) { |
| | 3888 | interval->has_start = 1; |
| | 3889 | |
| | 3890 | if (*p == '+') { |
| | 3891 | interval->start_is_offset = 1; |
| | 3892 | p++; |
| | 3893 | } else { |
| | 3894 | interval->start_is_offset = 0; |
| | 3895 | } |
| | 3896 | |
| | 3897 | ret = av_parse_time(&interval->start, p, 1); |
| | 3898 | if (ret < 0) { |
| | 3899 | av_log(NULL, AV_LOG_ERROR, "Invalid interval start specification '%s'\n", p); |
| | 3900 | goto end; |
| | 3901 | } |
| | 3902 | } else { |
| | 3903 | interval->has_start = 0; |
| | 3904 | } |
| | 3905 | |
| | 3906 | /* parse second part */ |
| | 3907 | p = next; |
| | 3908 | if (p && *p) { |
| | 3909 | int64_t us; |
| | 3910 | interval->has_end = 1; |
| | 3911 | |
| | 3912 | if (*p == '+') { |
| | 3913 | interval->end_is_offset = 1; |
| | 3914 | p++; |
| | 3915 | } else { |
| | 3916 | interval->end_is_offset = 0; |
| | 3917 | } |
| | 3918 | |
| | 3919 | if (interval->end_is_offset && *p == '#') { |
| | 3920 | long long int lli; |
| | 3921 | char *tail; |
| | 3922 | interval->duration_frames = 1; |
| | 3923 | p++; |
| | 3924 | lli = strtoll(p, &tail, 10); |
| | 3925 | if (*tail || lli < 0) { |
| | 3926 | av_log(NULL, AV_LOG_ERROR, |
| | 3927 | "Invalid or negative value '%s' for duration number of frames\n", p); |
| | 3928 | goto end; |
| | 3929 | } |
| | 3930 | interval->end = lli; |
| | 3931 | } else { |
| | 3932 | interval->duration_frames = 0; |
| | 3933 | ret = av_parse_time(&us, p, 1); |
| | 3934 | if (ret < 0) { |
| | 3935 | av_log(NULL, AV_LOG_ERROR, "Invalid interval end/duration specification '%s'\n", p); |
| | 3936 | goto end; |
| | 3937 | } |
| | 3938 | interval->end = us; |
| | 3939 | } |
| | 3940 | } else { |
| | 3941 | interval->has_end = 0; |
| | 3942 | } |
| | 3943 | |
| | 3944 | end: |
| | 3945 | av_free(spec); |
| | 3946 | return ret; |
| | 3947 | } |
| | 3948 | |
| | 3949 | static int parse_read_intervals(const char *intervals_spec) |
| | 3950 | { |
| | 3951 | int ret, n, i; |
| | 3952 | char *p, *spec = av_strdup(intervals_spec); |
| | 3953 | if (!spec) |
| | 3954 | return AVERROR(ENOMEM); |
| | 3955 | |
| | 3956 | /* preparse specification, get number of intervals */ |
| | 3957 | for (n = 0, p = spec; *p; p++) |
| | 3958 | if (*p == ',') |
| | 3959 | n++; |
| | 3960 | n++; |
| | 3961 | |
| | 3962 | read_intervals = av_malloc_array(n, sizeof(*read_intervals)); |
| | 3963 | if (!read_intervals) { |
| | 3964 | ret = AVERROR(ENOMEM); |
| | 3965 | goto end; |
| | 3966 | } |
| | 3967 | read_intervals_nb = n; |
| | 3968 | |
| | 3969 | /* parse intervals */ |
| | 3970 | p = spec; |
| | 3971 | for (i = 0; p; i++) { |
| | 3972 | char *next; |
| | 3973 | |
| | 3974 | av_assert0(i < read_intervals_nb); |
| | 3975 | next = strchr(p, ','); |
| | 3976 | if (next) |
| | 3977 | *next++ = 0; |
| | 3978 | |
| | 3979 | read_intervals[i].id = i; |
| | 3980 | ret = parse_read_interval(p, &read_intervals[i]); |
| | 3981 | if (ret < 0) { |
| | 3982 | av_log(NULL, AV_LOG_ERROR, "Error parsing read interval #%d '%s'\n", |
| | 3983 | i, p); |
| | 3984 | goto end; |
| | 3985 | } |
| | 3986 | av_log(NULL, AV_LOG_VERBOSE, "Parsed log interval "); |
| | 3987 | log_read_interval(&read_intervals[i], NULL, AV_LOG_VERBOSE); |
| | 3988 | p = next; |
| | 3989 | } |
| | 3990 | av_assert0(i == read_intervals_nb); |
| | 3991 | |
| | 3992 | end: |
| | 3993 | av_free(spec); |
| | 3994 | return ret; |
| | 3995 | } |
| | 3996 | |
| | 3997 | static int opt_read_intervals(void *optctx, const char *opt, const char *arg) |
| | 3998 | { |
| | 3999 | return parse_read_intervals(arg); |
| | 4000 | } |
| | 4001 | |
| | 4002 | static int opt_pretty(void *optctx, const char *opt, const char *arg) |
| | 4003 | { |
| | 4004 | show_value_unit = 1; |
| | 4005 | use_value_prefix = 1; |
| | 4006 | use_byte_value_binary_prefix = 1; |
| | 4007 | use_value_sexagesimal_format = 1; |
| | 4008 | return 0; |
| | 4009 | } |
| | 4010 | |
| | 4011 | static void print_section(SectionID id, int level) |
| | 4012 | { |
| | 4013 | const SectionID *pid; |
| | 4014 | const struct section *section = §ions[id]; |
| | 4015 | printf("%c%c%c%c", |
| | 4016 | section->flags & SECTION_FLAG_IS_WRAPPER ? 'W' : '.', |
| | 4017 | section->flags & SECTION_FLAG_IS_ARRAY ? 'A' : '.', |
| | 4018 | section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS ? 'V' : '.', |
| | 4019 | section->flags & SECTION_FLAG_HAS_TYPE ? 'T' : '.'); |
| | 4020 | printf("%*c %s", level * 4, ' ', section->name); |
| | 4021 | if (section->unique_name) |
| | 4022 | printf("/%s", section->unique_name); |
| | 4023 | printf("\n"); |
| | 4024 | |
| | 4025 | for (pid = section->children_ids; *pid != -1; pid++) |
| | 4026 | print_section(*pid, level+1); |
| | 4027 | } |
| | 4028 | |
| | 4029 | static int opt_sections(void *optctx, const char *opt, const char *arg) |
| | 4030 | { |
| | 4031 | printf("Sections:\n" |
| | 4032 | "W... = Section is a wrapper (contains other sections, no local entries)\n" |
| | 4033 | ".A.. = Section contains an array of elements of the same type\n" |
| | 4034 | "..V. = Section may contain a variable number of fields with variable keys\n" |
| | 4035 | "...T = Section contain a unique type\n" |
| | 4036 | "FLAGS NAME/UNIQUE_NAME\n" |
| | 4037 | "----\n"); |
| | 4038 | print_section(SECTION_ID_ROOT, 0); |
| | 4039 | return 0; |
| | 4040 | } |
| | 4041 | |
| | 4042 | static int opt_show_versions(void *optctx, const char *opt, const char *arg) |
| | 4043 | { |
| | 4044 | mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL); |
| | 4045 | mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL); |
| | 4046 | return 0; |
| | 4047 | } |
| | 4048 | |
| | 4049 | #define DEFINE_OPT_SHOW_SECTION(section, target_section_id) \ |
| | 4050 | static int opt_show_##section(void *optctx, const char *opt, const char *arg) \ |
| | 4051 | { \ |
| | 4052 | mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \ |
| | 4053 | return 0; \ |
| | 4054 | } |
| | 4055 | |
| | 4056 | DEFINE_OPT_SHOW_SECTION(chapters, CHAPTERS) |
| | 4057 | DEFINE_OPT_SHOW_SECTION(error, ERROR) |
| | 4058 | DEFINE_OPT_SHOW_SECTION(format, FORMAT) |
| | 4059 | DEFINE_OPT_SHOW_SECTION(frames, FRAMES) |
| | 4060 | DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS) |
| | 4061 | DEFINE_OPT_SHOW_SECTION(packets, PACKETS) |
| | 4062 | DEFINE_OPT_SHOW_SECTION(pixel_formats, PIXEL_FORMATS) |
| | 4063 | DEFINE_OPT_SHOW_SECTION(program_version, PROGRAM_VERSION) |
| | 4064 | DEFINE_OPT_SHOW_SECTION(streams, STREAMS) |
| | 4065 | DEFINE_OPT_SHOW_SECTION(programs, PROGRAMS) |
| | 4066 | |
| | 4067 | static const OptionDef real_options[] = { |
| | 4068 | CMDUTILS_COMMON_OPTIONS |
| | 4069 | { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" }, |
| | 4070 | { "unit", OPT_BOOL, {&show_value_unit}, "show unit of the displayed values" }, |
| | 4071 | { "prefix", OPT_BOOL, {&use_value_prefix}, "use SI prefixes for the displayed values" }, |
| | 4072 | { "byte_binary_prefix", OPT_BOOL, {&use_byte_value_binary_prefix}, |
| | 4073 | "use binary prefixes for byte units" }, |
| | 4074 | { "sexagesimal", OPT_BOOL, {&use_value_sexagesimal_format}, |
| | 4075 | "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" }, |
| | 4076 | { "pretty", 0, {.func_arg = opt_pretty}, |
| | 4077 | "prettify the format of displayed values, make it more human readable" }, |
| | 4078 | { "output_format", OPT_STRING | HAS_ARG, { &output_format }, |
| | 4079 | "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" }, |
| | 4080 | { "print_format", OPT_STRING | HAS_ARG, { &output_format }, "alias for -output_format (deprecated)" }, |
| | 4081 | { "of", OPT_STRING | HAS_ARG, { &output_format }, "alias for -output_format", "format" }, |
| | 4082 | { "select_streams", OPT_STRING | HAS_ARG, { &stream_specifier }, "select the specified streams", "stream_specifier" }, |
| | 4083 | { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" }, |
| | 4084 | { "show_data", OPT_BOOL, { &do_show_data }, "show packets data" }, |
| | 4085 | { "show_data_hash", OPT_STRING | HAS_ARG, { &show_data_hash }, "show packets data hash" }, |
| | 4086 | { "show_error", 0, { .func_arg = &opt_show_error }, "show probing error" }, |
| | 4087 | { "show_format", 0, { .func_arg = &opt_show_format }, "show format/container info" }, |
| | 4088 | { "show_frames", 0, { .func_arg = &opt_show_frames }, "show frames info" }, |
| | 4089 | { "show_entries", HAS_ARG, {.func_arg = opt_show_entries}, |
| | 4090 | "show a set of specified entries", "entry_list" }, |
| | 4091 | #if HAVE_THREADS |
| | 4092 | { "show_log", OPT_INT|HAS_ARG, { &do_show_log }, "show log" }, |
| | 4093 | #endif |
| | 4094 | { "show_packets", 0, { .func_arg = &opt_show_packets }, "show packets info" }, |
| | 4095 | { "show_programs", 0, { .func_arg = &opt_show_programs }, "show programs info" }, |
| | 4096 | { "show_streams", 0, { .func_arg = &opt_show_streams }, "show streams info" }, |
| | 4097 | { "show_chapters", 0, { .func_arg = &opt_show_chapters }, "show chapters info" }, |
| | 4098 | { "count_frames", OPT_BOOL, { &do_count_frames }, "count the number of frames per stream" }, |
| | 4099 | { "count_packets", OPT_BOOL, { &do_count_packets }, "count the number of packets per stream" }, |
| | 4100 | { "show_program_version", 0, { .func_arg = &opt_show_program_version }, "show ffprobe version" }, |
| | 4101 | { "show_library_versions", 0, { .func_arg = &opt_show_library_versions }, "show library versions" }, |
| | 4102 | { "show_versions", 0, { .func_arg = &opt_show_versions }, "show program and library versions" }, |
| | 4103 | { "show_pixel_formats", 0, { .func_arg = &opt_show_pixel_formats }, "show pixel format descriptions" }, |
| | 4104 | { "show_optional_fields", HAS_ARG, { .func_arg = &opt_show_optional_fields }, "show optional fields" }, |
| | 4105 | { "show_private_data", OPT_BOOL, { &show_private_data }, "show private data" }, |
| | 4106 | { "private", OPT_BOOL, { &show_private_data }, "same as show_private_data" }, |
| | 4107 | { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" }, |
| | 4108 | { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" }, |
| | 4109 | { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"}, |
| | 4110 | { "o", HAS_ARG, {.func_arg = opt_output_file_o}, "write to specified output", "output_file"}, |
| | 4111 | { "print_filename", HAS_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, |
| | 4112 | { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info }, |
| | 4113 | "read and decode the streams to fill missing information with heuristics" }, |
| | 4114 | { NULL, }, |
| | 4115 | }; |
| | 4116 | |
| | 4117 | static inline int check_section_show_entries(int section_id) |
| | 4118 | { |
| | 4119 | struct section *section = §ions[section_id]; |
| | 4120 | if (sections[section_id].show_all_entries || sections[section_id].entries_to_show) |
| | 4121 | return 1; |
| | 4122 | for (const SectionID *id = section->children_ids; *id != -1; id++) |
| | 4123 | if (check_section_show_entries(*id)) |
| | 4124 | return 1; |
| | 4125 | return 0; |
| | 4126 | } |
| | 4127 | |
| | 4128 | #define SET_DO_SHOW(id, varname) do { \ |
| | 4129 | if (check_section_show_entries(SECTION_ID_##id)) \ |
| | 4130 | do_show_##varname = 1; \ |
| | 4131 | } while (0) |
| | 4132 | |
| | 4133 | int main_ffprobe(int argc, char **argv) |
| | 4134 | { |
| | 4135 | const Writer *w; |
| | 4136 | WriterContext *wctx; |
| | 4137 | char *buf; |
| | 4138 | char *w_name = NULL, *w_args = NULL; |
| | 4139 | int ret, input_ret, i; |
| | 4140 | |
| | 4141 | init_dynload(); |
| | 4142 | |
| | 4143 | #if HAVE_THREADS |
| | 4144 | ret = pthread_mutex_init(&log_mutex, NULL); |
| | 4145 | if (ret != 0) { |
| | 4146 | goto end; |
| | 4147 | } |
| | 4148 | #endif |
| | 4149 | av_log_set_flags(AV_LOG_SKIP_REPEATED); |
| | 4150 | |
| | 4151 | options = real_options; |
| | 4152 | parse_loglevel(argc, argv, options); |
| | 4153 | avformat_network_init(); |
| | 4154 | #if CONFIG_AVDEVICE |
| | 4155 | avdevice_register_all(); |
| | 4156 | #endif |
| | 4157 | |
| | 4158 | show_banner(argc, argv, options); |
| | 4159 | ret = parse_options(NULL, argc, argv, options, opt_input_file); |
| | 4160 | if (ret < 0) { |
| | 4161 | ret = (ret == AVERROR_EXIT) ? 0 : ret; |
| | 4162 | goto end; |
| | 4163 | } |
| | 4164 | |
| | 4165 | if (do_show_log) |
| | 4166 | av_log_set_callback(log_callback); |
| | 4167 | |
| | 4168 | /* mark things to show, based on -show_entries */ |
| | 4169 | SET_DO_SHOW(CHAPTERS, chapters); |
| | 4170 | SET_DO_SHOW(ERROR, error); |
| | 4171 | SET_DO_SHOW(FORMAT, format); |
| | 4172 | SET_DO_SHOW(FRAMES, frames); |
| | 4173 | SET_DO_SHOW(LIBRARY_VERSIONS, library_versions); |
| | 4174 | SET_DO_SHOW(PACKETS, packets); |
| | 4175 | SET_DO_SHOW(PIXEL_FORMATS, pixel_formats); |
| | 4176 | SET_DO_SHOW(PIXEL_FORMAT_FLAGS, pixel_format_flags); |
| | 4177 | SET_DO_SHOW(PIXEL_FORMAT_COMPONENTS, pixel_format_components); |
| | 4178 | SET_DO_SHOW(PROGRAM_VERSION, program_version); |
| | 4179 | SET_DO_SHOW(PROGRAMS, programs); |
| | 4180 | SET_DO_SHOW(STREAMS, streams); |
| | 4181 | SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition); |
| | 4182 | SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition); |
| | 4183 | |
| | 4184 | SET_DO_SHOW(CHAPTER_TAGS, chapter_tags); |
| | 4185 | SET_DO_SHOW(FORMAT_TAGS, format_tags); |
| | 4186 | SET_DO_SHOW(FRAME_TAGS, frame_tags); |
| | 4187 | SET_DO_SHOW(PROGRAM_TAGS, program_tags); |
| | 4188 | SET_DO_SHOW(STREAM_TAGS, stream_tags); |
| | 4189 | SET_DO_SHOW(PROGRAM_STREAM_TAGS, stream_tags); |
| | 4190 | SET_DO_SHOW(PACKET_TAGS, packet_tags); |
| | 4191 | |
| | 4192 | if (do_bitexact && (do_show_program_version || do_show_library_versions)) { |
| | 4193 | av_log(NULL, AV_LOG_ERROR, |
| | 4194 | "-bitexact and -show_program_version or -show_library_versions " |
| | 4195 | "options are incompatible\n"); |
| | 4196 | ret = AVERROR(EINVAL); |
| | 4197 | goto end; |
| | 4198 | } |
| | 4199 | |
| | 4200 | writer_register_all(); |
| | 4201 | |
| | 4202 | if (!output_format) |
| | 4203 | output_format = av_strdup("default"); |
| | 4204 | if (!output_format) { |
| | 4205 | ret = AVERROR(ENOMEM); |
| | 4206 | goto end; |
| | 4207 | } |
| | 4208 | w_name = av_strtok(output_format, "=", &buf); |
| | 4209 | if (!w_name) { |
| | 4210 | av_log(NULL, AV_LOG_ERROR, |
| | 4211 | "No name specified for the output format\n"); |
| | 4212 | ret = AVERROR(EINVAL); |
| | 4213 | goto end; |
| | 4214 | } |
| | 4215 | w_args = buf; |
| | 4216 | |
| | 4217 | if (show_data_hash) { |
| | 4218 | if ((ret = av_hash_alloc(&hash, show_data_hash)) < 0) { |
| | 4219 | if (ret == AVERROR(EINVAL)) { |
| | 4220 | const char *n; |
| | 4221 | av_log(NULL, AV_LOG_ERROR, |
| | 4222 | "Unknown hash algorithm '%s'\nKnown algorithms:", |
| | 4223 | show_data_hash); |
| | 4224 | for (i = 0; (n = av_hash_names(i)); i++) |
| | 4225 | av_log(NULL, AV_LOG_ERROR, " %s", n); |
| | 4226 | av_log(NULL, AV_LOG_ERROR, "\n"); |
| | 4227 | } |
| | 4228 | goto end; |
| | 4229 | } |
| | 4230 | } |
| | 4231 | |
| | 4232 | w = writer_get_by_name(w_name); |
| | 4233 | if (!w) { |
| | 4234 | av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name); |
| | 4235 | ret = AVERROR(EINVAL); |
| | 4236 | goto end; |
| | 4237 | } |
| | 4238 | |
| | 4239 | if ((ret = writer_open(&wctx, w, w_args, |
| | 4240 | sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) { |
| | 4241 | if (w == &xml_writer) |
| | 4242 | wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES; |
| | 4243 | |
| | 4244 | writer_print_section_header(wctx, NULL, SECTION_ID_ROOT); |
| | 4245 | |
| | 4246 | if (do_show_program_version) |
| | 4247 | ffprobe_show_program_version(wctx); |
| | 4248 | if (do_show_library_versions) |
| | 4249 | ffprobe_show_library_versions(wctx); |
| | 4250 | if (do_show_pixel_formats) |
| | 4251 | ffprobe_show_pixel_formats(wctx); |
| | 4252 | |
| | 4253 | if (!input_filename && |
| | 4254 | ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) || |
| | 4255 | (!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) { |
| | 4256 | show_usage(); |
| | 4257 | av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n"); |
| | 4258 | av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name); |
| | 4259 | ret = AVERROR(EINVAL); |
| | 4260 | } else if (input_filename) { |
| | 4261 | ret = probe_file(wctx, input_filename, print_input_filename); |
| | 4262 | if (ret < 0 && do_show_error) |
| | 4263 | show_error(wctx, ret); |
| | 4264 | } |
| | 4265 | |
| | 4266 | input_ret = ret; |
| | 4267 | |
| | 4268 | writer_print_section_footer(wctx); |
| | 4269 | ret = writer_close(&wctx); |
| | 4270 | if (ret < 0) |
| | 4271 | av_log(NULL, AV_LOG_ERROR, "Writing output failed: %s\n", av_err2str(ret)); |
| | 4272 | |
| | 4273 | ret = FFMIN(ret, input_ret); |
| | 4274 | } |
| | 4275 | |
| | 4276 | end: |
| | 4277 | av_freep(&output_format); |
| | 4278 | av_freep(&read_intervals); |
| | 4279 | av_hash_freep(&hash); |
| | 4280 | |
| | 4281 | uninit_opts(); |
| | 4282 | for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) |
| | 4283 | av_dict_free(&(sections[i].entries_to_show)); |
| | 4284 | |
| | 4285 | avformat_network_deinit(); |
| | 4286 | |
| | 4287 | #if HAVE_THREADS |
| | 4288 | pthread_mutex_destroy(&log_mutex); |
| | 4289 | #endif |
| | 4290 | |
| | 4291 | return ret < 0; |
| | 4292 | } |