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

Lines 1–500 of 3400.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t