diff --git a/libavformat/Makefile b/libavformat/Makefile index ed93458f03..86177af2f4 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -179,7 +179,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER) += rawenc.o -OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o +OBJS-$(CONFIG_DASH_MUXER) += dash.o dashenc.o hlsplaylist.o codecstring.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o @@ -267,7 +267,7 @@ OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o OBJS-$(CONFIG_EVC_MUXER) += rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o -OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o +OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o codecstring.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o OBJS-$(CONFIG_HXVS_DEMUXER) += hxvs.o OBJS-$(CONFIG_IAMF_DEMUXER) += iamfdec.o diff --git a/libavformat/codecstring.c b/libavformat/codecstring.c new file mode 100644 index 0000000000..773c37f6b5 --- /dev/null +++ b/libavformat/codecstring.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014 Martin Storsjo + * Copyright (c) 2018 Akamai Technologies, Inc. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" +#include "libavutil/rational.h" + +#include "av1.h" +#include "avc.h" +#include "avformat.h" +#include "internal.h" +#include "nal.h" +#include "vpcc.h" + +static const struct codec_string { + enum AVCodecID id; + char str[8]; +} codecs[] = { + { AV_CODEC_ID_VP8, "vp8" }, + { AV_CODEC_ID_VP9, "vp9" }, + { AV_CODEC_ID_VORBIS, "vorbis" }, + { AV_CODEC_ID_OPUS, "opus" }, + { AV_CODEC_ID_FLAC, "flac" }, + { AV_CODEC_ID_NONE } +}; + +static void set_vp9_codec_str(void *logctx, const AVCodecParameters *par, + const AVRational *frame_rate, char *str, int size) { + VPCC vpcc; + int ret = ff_isom_get_vpcc_features(logctx, par, NULL, 0, frame_rate, &vpcc); + if (ret == 0) { + snprintf(str, size, "vp09.%02d.%02d.%02d", + vpcc.profile, vpcc.level, vpcc.bitdepth); + } else { + // Default to just vp9 in case of error while finding out profile or level + av_log(logctx, AV_LOG_WARNING, "Could not find VP9 profile and/or level\n"); + av_strlcpy(str, "vp9", size); + } +} + +int ff_make_codec_str(void *logctx, const AVCodecParameters *par, + const AVRational *frame_rate, char *str, size_t size) +{ + int i; + + if (size < 5) + return AVERROR(EINVAL); + + str[0] = '\0'; + + // common Webm codecs are not part of RFC 6381 + for (i = 0; codecs[i].id != AV_CODEC_ID_NONE; i++) + if (codecs[i].id == par->codec_id) { + if (codecs[i].id == AV_CODEC_ID_VP9) { + set_vp9_codec_str(logctx, par, frame_rate, str, size); + } else { + av_strlcpy(str, codecs[i].str, size); + } + return 0; + } + + if (par->codec_id == AV_CODEC_ID_H264) { + // RFC 6381 + uint8_t *data = par->extradata; + if (data) { + const uint8_t *p; + + if (AV_RB32(data) == 0x01 && (data[4] & 0x1F) == 7) + p = &data[5]; + else if (AV_RB24(data) == 0x01 && (data[3] & 0x1F) == 7) + p = &data[4]; + else if (data[0] == 0x01) /* avcC */ + p = &data[1]; + else + return AVERROR(EINVAL); + snprintf(str, size, + "avc1.%02x%02x%02x", p[0], p[1], p[2]); + } else { + return AVERROR(EINVAL); + } + } else if (par->codec_id == AV_CODEC_ID_HEVC) { + // 3GPP TS 26.244 + uint8_t *data = par->extradata; + int profile = AV_PROFILE_UNKNOWN; + uint32_t profile_compatibility = AV_PROFILE_UNKNOWN; + char tier = 0; + int level = AV_LEVEL_UNKNOWN; + char constraints[8] = ""; + + if (par->profile != AV_PROFILE_UNKNOWN) + profile = par->profile; + if (par->level != AV_LEVEL_UNKNOWN) + level = par->level; + + /* check the boundary of data which from current position is small than extradata_size */ + while (data && (data - par->extradata + 19) < par->extradata_size) { + /* get HEVC SPS NAL and seek to profile_tier_level */ + if (!(data[0] | data[1] | data[2]) && data[3] == 1 && ((data[4] & 0x7E) == 0x42)) { + uint8_t *rbsp_buf; + int remain_size = 0; + int rbsp_size = 0; + uint32_t profile_compatibility_flags = 0; + uint8_t high_nibble = 0; + /* skip start code + nalu header */ + data += 6; + /* process by reference General NAL unit syntax */ + remain_size = par->extradata_size - (data - par->extradata); + rbsp_buf = ff_nal_unit_extract_rbsp(data, remain_size, &rbsp_size, 0); + if (!rbsp_buf) + return AVERROR(EINVAL); + if (rbsp_size < 13) { + av_freep(&rbsp_buf); + break; + } + /* skip sps_video_parameter_set_id u(4), + * sps_max_sub_layers_minus1 u(3), + * and sps_temporal_id_nesting_flag u(1) + * + * TIER represents the general_tier_flag, with 'L' indicating the flag is 0, + * and 'H' indicating the flag is 1 + */ + tier = (rbsp_buf[1] & 0x20) == 0 ? 'L' : 'H'; + profile = rbsp_buf[1] & 0x1f; + /* PROFILE_COMPATIBILITY is general_profile_compatibility_flags, but in reverse bit order, + * in a hexadecimal representation (leading zeroes may be omitted). + */ + profile_compatibility_flags = AV_RB32(rbsp_buf + 2); + /* revise these bits to get the profile compatibility value */ + profile_compatibility_flags = ((profile_compatibility_flags & 0x55555555U) << 1) | ((profile_compatibility_flags >> 1) & 0x55555555U); + profile_compatibility_flags = ((profile_compatibility_flags & 0x33333333U) << 2) | ((profile_compatibility_flags >> 2) & 0x33333333U); + profile_compatibility_flags = ((profile_compatibility_flags & 0x0F0F0F0FU) << 4) | ((profile_compatibility_flags >> 4) & 0x0F0F0F0FU); + profile_compatibility_flags = ((profile_compatibility_flags & 0x00FF00FFU) << 8) | ((profile_compatibility_flags >> 8) & 0x00FF00FFU); + profile_compatibility = (profile_compatibility_flags << 16) | (profile_compatibility_flags >> 16); + /* skip 8 + 8 + 32 + * CONSTRAINTS is a hexadecimal representation of the general_constraint_indicator_flags. + * each byte is separated by a '.', and trailing zero bytes may be omitted. + * drop the trailing zero bytes refer to ISO/IEC14496-15. + */ + high_nibble = rbsp_buf[7] >> 4; + snprintf(constraints, sizeof(constraints), + high_nibble ? "%02x.%x" : "%02x", + rbsp_buf[6], high_nibble); + /* skip 8 + 8 + 32 + 4 + 43 + 1 bit */ + level = rbsp_buf[12]; + av_freep(&rbsp_buf); + break; + } + data++; + } + if (par->codec_tag == MKTAG('h','v','c','1') && + profile != AV_PROFILE_UNKNOWN && + profile_compatibility != AV_PROFILE_UNKNOWN && + tier != 0 && + level != AV_LEVEL_UNKNOWN && + constraints[0] != '\0') { + snprintf(str, size, "%s.%d.%x.%c%d.%s", av_fourcc2str(par->codec_tag), profile, profile_compatibility, tier, level, constraints); + } else + return AVERROR(EINVAL); + } else if (par->codec_id == AV_CODEC_ID_AV1) { + // https://aomediacodec.github.io/av1-isobmff/#codecsparam + AV1SequenceParameters seq; + int err; + if (!par->extradata_size) + return AVERROR(EINVAL); + if ((err = ff_av1_parse_seq_header(&seq, par->extradata, par->extradata_size)) < 0) + return err; + + snprintf(str, size, "av01.%01u.%02u%s.%02u", + seq.profile, seq.level, seq.tier ? "H" : "M", seq.bitdepth); + if (seq.color_description_present_flag) + av_strlcatf(str, size, ".%01u.%01u%01u%01u.%02u.%02u.%02u.%01u", + seq.monochrome, + seq.chroma_subsampling_x, seq.chroma_subsampling_y, seq.chroma_sample_position, + seq.color_primaries, seq.transfer_characteristics, seq.matrix_coefficients, + seq.color_range); + } else if (par->codec_id == AV_CODEC_ID_MPEG4) { + // RFC 6381 + snprintf(str, size, "mp4v.20"); + // Unimplemented, should output ProfileLevelIndication as a decimal number + av_log(logctx, AV_LOG_WARNING, "Incomplete RFC 6381 codec string for mp4v\n"); + } else if (par->codec_id == AV_CODEC_ID_MP2) { + snprintf(str, size, "mp4a.40.33"); + } else if (par->codec_id == AV_CODEC_ID_MP3) { + snprintf(str, size, "mp4a.40.34"); + } else if (par->codec_id == AV_CODEC_ID_AAC) { + // RFC 6381 + int aot = 2; + if (par->extradata_size >= 2) { + aot = par->extradata[0] >> 3; + if (aot == 31) + aot = ((AV_RB16(par->extradata) >> 5) & 0x3f) + 32; + } else if (par->profile != AV_PROFILE_UNKNOWN) + aot = par->profile + 1; + snprintf(str, size, "mp4a.40.%d", aot); + } else if (par->codec_id == AV_CODEC_ID_AC3) { + snprintf(str, size, "ac-3"); + } else if (par->codec_id == AV_CODEC_ID_EAC3) { + snprintf(str, size, "ec-3"); + } else { + return AVERROR(EINVAL); + } + return 0; +} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index af92e38bbd..636e770450 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -42,8 +42,6 @@ #include "libavcodec/avcodec.h" -#include "av1.h" -#include "avc.h" #include "avformat.h" #include "avio_internal.h" #include "hlsplaylist.h" @@ -51,11 +49,9 @@ #include "http.h" #endif #include "internal.h" -#include "isom.h" #include "mux.h" #include "os_support.h" #include "url.h" -#include "vpcc.h" #include "dash.h" typedef enum { @@ -206,18 +202,6 @@ typedef struct DASHContext { int64_t update_period; } DASHContext; -static const struct codec_string { - enum AVCodecID id; - const char str[8]; -} codecs[] = { - { AV_CODEC_ID_VP8, "vp8" }, - { AV_CODEC_ID_VP9, "vp9" }, - { AV_CODEC_ID_VORBIS, "vorbis" }, - { AV_CODEC_ID_OPUS, "opus" }, - { AV_CODEC_ID_FLAC, "flac" }, - { AV_CODEC_ID_NONE } -}; - static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { DASHContext *c = s->priv_data; @@ -327,117 +311,6 @@ static int init_segment_types(AVFormatContext *s) return 0; } -static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, - AVRational *frame_rate, char *str, int size) { - VPCC vpcc; - int ret = ff_isom_get_vpcc_features(s, par, NULL, 0, frame_rate, &vpcc); - if (ret == 0) { - av_strlcatf(str, size, "vp09.%02d.%02d.%02d", - vpcc.profile, vpcc.level, vpcc.bitdepth); - } else { - // Default to just vp9 in case of error while finding out profile or level - av_log(s, AV_LOG_WARNING, "Could not find VP9 profile and/or level\n"); - av_strlcpy(str, "vp9", size); - } - return; -} - -static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, - AVRational *frame_rate, char *str, int size) -{ - const AVCodecTag *tags[2] = { NULL, NULL }; - uint32_t tag; - int i; - - // common Webm codecs are not part of RFC 6381 - for (i = 0; codecs[i].id != AV_CODEC_ID_NONE; i++) - if (codecs[i].id == par->codec_id) { - if (codecs[i].id == AV_CODEC_ID_VP9) { - set_vp9_codec_str(s, par, frame_rate, str, size); - } else { - av_strlcpy(str, codecs[i].str, size); - } - return; - } - - // for codecs part of RFC 6381 - if (par->codec_type == AVMEDIA_TYPE_VIDEO) - tags[0] = ff_codec_movvideo_tags; - else if (par->codec_type == AVMEDIA_TYPE_AUDIO) - tags[0] = ff_codec_movaudio_tags; - else - return; - - tag = par->codec_tag; - if (!tag) - tag = av_codec_get_tag(tags, par->codec_id); - if (!tag) - return; - if (size < 5) - return; - - AV_WL32(str, tag); - str[4] = '\0'; - if (!strcmp(str, "mp4a") || !strcmp(str, "mp4v")) { - uint32_t oti; - tags[0] = ff_mp4_obj_type; - oti = av_codec_get_tag(tags, par->codec_id); - if (oti) - av_strlcatf(str, size, ".%02"PRIx32, oti); - else - return; - - if (tag == MKTAG('m', 'p', '4', 'a')) { - if (par->extradata_size >= 2) { - int aot = par->extradata[0] >> 3; - if (aot == 31) - aot = ((AV_RB16(par->extradata) >> 5) & 0x3f) + 32; - av_strlcatf(str, size, ".%d", aot); - } - } else if (tag == MKTAG('m', 'p', '4', 'v')) { - // Unimplemented, should output ProfileLevelIndication as a decimal number - av_log(s, AV_LOG_WARNING, "Incomplete RFC 6381 codec string for mp4v\n"); - } - } else if (!strcmp(str, "avc1")) { - uint8_t *tmpbuf = NULL; - uint8_t *extradata = par->extradata; - int extradata_size = par->extradata_size; - if (!extradata_size) - return; - if (extradata[0] != 1) { - AVIOContext *pb; - if (avio_open_dyn_buf(&pb) < 0) - return; - if (ff_isom_write_avcc(pb, extradata, extradata_size) < 0) { - ffio_free_dyn_buf(&pb); - return; - } - extradata_size = avio_close_dyn_buf(pb, &extradata); - tmpbuf = extradata; - } - - if (extradata_size >= 4) - av_strlcatf(str, size, ".%02x%02x%02x", - extradata[1], extradata[2], extradata[3]); - av_free(tmpbuf); - } else if (!strcmp(str, "av01")) { - AV1SequenceParameters seq; - if (!par->extradata_size) - return; - if (ff_av1_parse_seq_header(&seq, par->extradata, par->extradata_size) < 0) - return; - - av_strlcatf(str, size, ".%01u.%02u%s.%02u", - seq.profile, seq.level, seq.tier ? "H" : "M", seq.bitdepth); - if (seq.color_description_present_flag) - av_strlcatf(str, size, ".%01u.%01u%01u%01u.%02u.%02u.%02u.%01u", - seq.monochrome, - seq.chroma_subsampling_x, seq.chroma_subsampling_y, seq.chroma_sample_position, - seq.color_primaries, seq.transfer_characteristics, seq.matrix_coefficients, - seq.color_range); - } -} - static int flush_dynbuf(DASHContext *c, OutputStream *os, int *range_length) { uint8_t *buffer; @@ -1701,8 +1574,8 @@ static int dash_init(AVFormatContext *s) c->has_video = 1; } - set_codec_str(s, st->codecpar, &st->avg_frame_rate, os->codec_str, - sizeof(os->codec_str)); + ff_make_codec_str(s, st->codecpar, &st->avg_frame_rate, os->codec_str, + sizeof(os->codec_str)); os->first_pts = AV_NOPTS_VALUE; os->max_pts = AV_NOPTS_VALUE; os->last_dts = AV_NOPTS_VALUE; @@ -1838,7 +1711,7 @@ static int update_stream_extradata(AVFormatContext *s, OutputStream *os, memcpy(par->extradata, extradata, extradata_size); - set_codec_str(s, par, frame_rate, os->codec_str, sizeof(os->codec_str)); + ff_make_codec_str(s, par, frame_rate, os->codec_str, sizeof(os->codec_str)); return 0; } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index d3db83093c..8e36887e06 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -44,13 +44,11 @@ #include "avformat.h" #include "avio_internal.h" -#include "avc.h" #if CONFIG_HTTP_PROTOCOL #include "http.h" #endif #include "hlsplaylist.h" #include "internal.h" -#include "nal.h" #include "mux.h" #include "os_support.h" #include "url.h" @@ -358,118 +356,10 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN) return; - if (st->codecpar->codec_id == AV_CODEC_ID_H264) { - uint8_t *data = st->codecpar->extradata; - if (data) { - const uint8_t *p; - - if (AV_RB32(data) == 0x01 && (data[4] & 0x1F) == 7) - p = &data[5]; - else if (AV_RB24(data) == 0x01 && (data[3] & 0x1F) == 7) - p = &data[4]; - else if (data[0] == 0x01) /* avcC */ - p = &data[1]; - else - goto fail; - snprintf(attr, sizeof(attr), - "avc1.%02x%02x%02x", p[0], p[1], p[2]); - } else { - goto fail; - } - } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) { - uint8_t *data = st->codecpar->extradata; - int profile = AV_PROFILE_UNKNOWN; - uint32_t profile_compatibility = AV_PROFILE_UNKNOWN; - char tier = 0; - int level = AV_LEVEL_UNKNOWN; - char constraints[8] = ""; - - if (st->codecpar->profile != AV_PROFILE_UNKNOWN) - profile = st->codecpar->profile; - if (st->codecpar->level != AV_LEVEL_UNKNOWN) - level = st->codecpar->level; - - /* check the boundary of data which from current position is small than extradata_size */ - while (data && (data - st->codecpar->extradata + 19) < st->codecpar->extradata_size) { - /* get HEVC SPS NAL and seek to profile_tier_level */ - if (!(data[0] | data[1] | data[2]) && data[3] == 1 && ((data[4] & 0x7E) == 0x42)) { - uint8_t *rbsp_buf; - int remain_size = 0; - int rbsp_size = 0; - uint32_t profile_compatibility_flags = 0; - uint8_t high_nibble = 0; - /* skip start code + nalu header */ - data += 6; - /* process by reference General NAL unit syntax */ - remain_size = st->codecpar->extradata_size - (data - st->codecpar->extradata); - rbsp_buf = ff_nal_unit_extract_rbsp(data, remain_size, &rbsp_size, 0); - if (!rbsp_buf) - return; - if (rbsp_size < 13) { - av_freep(&rbsp_buf); - break; - } - /* skip sps_video_parameter_set_id u(4), - * sps_max_sub_layers_minus1 u(3), - * and sps_temporal_id_nesting_flag u(1) - * - * TIER represents the general_tier_flag, with 'L' indicating the flag is 0, - * and 'H' indicating the flag is 1 - */ - tier = (rbsp_buf[1] & 0x20) == 0 ? 'L' : 'H'; - profile = rbsp_buf[1] & 0x1f; - /* PROFILE_COMPATIBILITY is general_profile_compatibility_flags, but in reverse bit order, - * in a hexadecimal representation (leading zeroes may be omitted). - */ - profile_compatibility_flags = AV_RB32(rbsp_buf + 2); - /* revise these bits to get the profile compatibility value */ - profile_compatibility_flags = ((profile_compatibility_flags & 0x55555555U) << 1) | ((profile_compatibility_flags >> 1) & 0x55555555U); - profile_compatibility_flags = ((profile_compatibility_flags & 0x33333333U) << 2) | ((profile_compatibility_flags >> 2) & 0x33333333U); - profile_compatibility_flags = ((profile_compatibility_flags & 0x0F0F0F0FU) << 4) | ((profile_compatibility_flags >> 4) & 0x0F0F0F0FU); - profile_compatibility_flags = ((profile_compatibility_flags & 0x00FF00FFU) << 8) | ((profile_compatibility_flags >> 8) & 0x00FF00FFU); - profile_compatibility = (profile_compatibility_flags << 16) | (profile_compatibility_flags >> 16); - /* skip 8 + 8 + 32 - * CONSTRAINTS is a hexadecimal representation of the general_constraint_indicator_flags. - * each byte is separated by a '.', and trailing zero bytes may be omitted. - * drop the trailing zero bytes refer to ISO/IEC14496-15. - */ - high_nibble = rbsp_buf[7] >> 4; - snprintf(constraints, sizeof(constraints), - high_nibble ? "%02x.%x" : "%02x", - rbsp_buf[6], high_nibble); - /* skip 8 + 8 + 32 + 4 + 43 + 1 bit */ - level = rbsp_buf[12]; - av_freep(&rbsp_buf); - break; - } - data++; - } - if (st->codecpar->codec_tag == MKTAG('h','v','c','1') && - profile != AV_PROFILE_UNKNOWN && - profile_compatibility != AV_PROFILE_UNKNOWN && - tier != 0 && - level != AV_LEVEL_UNKNOWN && - constraints[0] != '\0') { - snprintf(attr, sizeof(attr), "%s.%d.%x.%c%d.%s", av_fourcc2str(st->codecpar->codec_tag), profile, profile_compatibility, tier, level, constraints); - } else - goto fail; - } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { - snprintf(attr, sizeof(attr), "mp4a.40.33"); - } else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) { - snprintf(attr, sizeof(attr), "mp4a.40.34"); - } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { - if (st->codecpar->profile != AV_PROFILE_UNKNOWN) - snprintf(attr, sizeof(attr), "mp4a.40.%d", st->codecpar->profile+1); - else - // This is for backward compatibility with the previous implementation. - snprintf(attr, sizeof(attr), "mp4a.40.2"); - } else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { - snprintf(attr, sizeof(attr), "ac-3"); - } else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) { - snprintf(attr, sizeof(attr), "ec-3"); - } else { + if (ff_make_codec_str(vs->avf, st->codecpar, &st->avg_frame_rate, + attr, sizeof(attr)) < 0) goto fail; - } + // Don't write the same attribute multiple times if (!av_stristr(vs->codec_attr, attr)) { snprintf(vs->codec_attr + codec_strlen, diff --git a/libavformat/internal.h b/libavformat/internal.h index 1a50ba07d3..54b2ad6c79 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -661,4 +661,18 @@ int ff_dict_set_timestamp(AVDictionary **dict, const char *key, int64_t timestam */ int ff_parse_opts_from_query_string(void *obj, const char *str, int allow_unkown); +/** + * Make a RFC 4281/6381 like string describing a codec. + * + * @param logctx a context for potential log messages + * @param par pointer to an AVCodecParameters struct describing the codec + * @param frame_rate an optional pointer to AVRational for the frame rate, + * for deciding the right profile for video codecs + * @param str the output string buffer + * @param size the size of the string pointed to by str + * @return <0 on error + */ +int ff_make_codec_str(void *logctx, const AVCodecParameters *par, + const AVRational *frame_rate, char *str, size_t size); + #endif /* AVFORMAT_INTERNAL_H */