[FFmpeg-devel] [PATCH v2] Extract av_hls_codec_attr
Andreas Rheinhardt
andreas.rheinhardt at outlook.com
Fri Nov 3 10:01:09 EET 2023
Romain Beauxis:
> The logic for extracting HLS codec attribute strings is very useful and
> can be re-used in many different situations when working with HLS
> streams using libavcodec/libavformat.
>
> This patch extracts the function's code and places it into a publicly
> available function.
>
> Differences since v1:
> - ff_nal_unit_extract_rbsp renamed into avpriv_nal_unit_extract_rbsp to
> follow the appropriate library cross-linking convention.
>
> ---
> libavcodec/Makefile | 2 +
> libavcodec/hls.c | 105 +++++++++++++++++++++++++++++++++++++++++++
> libavcodec/hls.h | 42 +++++++++++++++++
> libavformat/avc.c | 4 +-
> libavformat/avc.h | 2 +-
> libavformat/hevc.c | 2 +-
> libavformat/hlsenc.c | 83 +++-------------------------------
> 7 files changed, 159 insertions(+), 81 deletions(-)
> create mode 100644 libavcodec/hls.c
> create mode 100644 libavcodec/hls.h
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 580a8d6b54..b3b2b18980 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -16,6 +16,7 @@ HEADERS = ac3_parser.h \
> dirac.h \
> dv_profile.h \
> dxva2.h \
> + hls.h \
> jni.h \
> mediacodec.h \
> packet.h \
> @@ -47,6 +48,7 @@ OBJS = ac3_parser.o \
> get_buffer.o \
> imgconvert.o \
> jni.o \
> + hls.o \
> mathtables.o \
> mediacodec.o \
> mpeg12framerate.o \
> diff --git a/libavcodec/hls.c b/libavcodec/hls.c
> new file mode 100644
> index 0000000000..05a6277dbc
> --- /dev/null
> +++ b/libavcodec/hls.c
> @@ -0,0 +1,105 @@
> +/*
> + * HLS public API
> + *
> + * 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 "hls.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavformat/avc.h"
> +
> +int av_hls_codec_attr(AVStream *st, char *attr, size_t attr_len) {
> + 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
> + return AVERROR_INVALIDDATA;
> + snprintf(attr, attr_len,
> + "avc1.%02x%02x%02x", p[0], p[1], p[2]);
> + } else {
> + return AVERROR_INVALIDDATA;
> + }
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
> + uint8_t *data = st->codecpar->extradata;
> + int profile = AV_PROFILE_UNKNOWN;
> + int level = AV_LEVEL_UNKNOWN;
> +
> + 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;
> + /* 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 = avpriv_nal_unit_extract_rbsp(data, remain_size, &rbsp_size, 0);
> + if (!rbsp_buf)
> + return AVERROR_INVALIDDATA;
> + 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) */
> + profile = rbsp_buf[1] & 0x1f;
> + /* 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 &&
> + level != AV_LEVEL_UNKNOWN) {
> + snprintf(attr, attr_len, "%s.%d.4.L%d.B01", av_fourcc2str(st->codecpar->codec_tag), profile, level);
> + } else
> + return AVERROR_INVALIDDATA;
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) {
> + snprintf(attr, attr_len, "mp4a.40.33");
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) {
> + snprintf(attr, attr_len, "mp4a.40.34");
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
> + /* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */
> + snprintf(attr, attr_len, "mp4a.40.2");
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
> + snprintf(attr, attr_len, "ac-3");
> + } else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) {
> + snprintf(attr, attr_len, "ec-3");
> + } else {
> + return AVERROR_INVALIDDATA;
> + }
> +
> + return 0;
> +}
> diff --git a/libavcodec/hls.h b/libavcodec/hls.h
> new file mode 100644
> index 0000000000..160e31de13
> --- /dev/null
> +++ b/libavcodec/hls.h
> @@ -0,0 +1,42 @@
> +/*
> + * HLS public API
> + *
> + * 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
> + * A public API for HLS codec information
> + */
> +
> +#ifndef AVCODEC_HLS_H
> +#define AVCODEC_HLS_H
> +
> +#include "libavformat/avformat.h"
> +
> +/**
> + * Write a NULL-terminated string representing the codec attributes
> + * associated with the given AVStream.
> + *
> + * @param st stream to extract codec attributes from
> + * @param attr buffer to write the codec attributes to
> + * @param attr_len size of the buffer
> + * @return 0 on success, a negative AVERROR code on failure
> + */
> +int av_hls_codec_attr(AVStream *st, char *attr, size_t attr_len);
> +
> +#endif
> diff --git a/libavformat/avc.c b/libavformat/avc.c
> index b0ceb1d2d8..f85483271b 100644
> --- a/libavformat/avc.c
> +++ b/libavformat/avc.c
> @@ -300,7 +300,7 @@ const uint8_t *ff_avc_mp4_find_startcode(const uint8_t *start,
> return start + res;
> }
>
> -uint8_t *ff_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
> +uint8_t *avpriv_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
> uint32_t *dst_len, int header_len)
> {
> uint8_t *dst;
> @@ -374,7 +374,7 @@ int ff_avc_decode_sps(H264SPS *sps, const uint8_t *buf, int buf_size)
> GetBitContext gb;
> uint8_t *rbsp_buf;
>
> - rbsp_buf = ff_nal_unit_extract_rbsp(buf, buf_size, &rbsp_size, 0);
> + rbsp_buf = avpriv_nal_unit_extract_rbsp(buf, buf_size, &rbsp_size, 0);
> if (!rbsp_buf)
> return AVERROR(ENOMEM);
>
> diff --git a/libavformat/avc.h b/libavformat/avc.h
> index 0ce95c194e..0bdaba162b 100644
> --- a/libavformat/avc.h
> +++ b/libavformat/avc.h
> @@ -63,7 +63,7 @@ int ff_avc_write_annexb_extradata(const uint8_t *in, uint8_t **buf, int *size);
> const uint8_t *ff_avc_mp4_find_startcode(const uint8_t *start,
> const uint8_t *end,
> int nal_length_size);
> -uint8_t *ff_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
> +uint8_t *avpriv_nal_unit_extract_rbsp(const uint8_t *src, uint32_t src_len,
> uint32_t *dst_len, int header_len);
>
> typedef struct {
> diff --git a/libavformat/hevc.c b/libavformat/hevc.c
> index ca5187a92e..5ea4280ebe 100644
> --- a/libavformat/hevc.c
> +++ b/libavformat/hevc.c
> @@ -708,7 +708,7 @@ static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
> uint8_t *rbsp_buf;
> uint32_t rbsp_size;
>
> - rbsp_buf = ff_nal_unit_extract_rbsp(nal_buf, nal_size, &rbsp_size, 2);
> + rbsp_buf = avpriv_nal_unit_extract_rbsp(nal_buf, nal_size, &rbsp_size, 2);
> if (!rbsp_buf) {
> ret = AVERROR(ENOMEM);
> goto end;
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index 4ef84c05c1..1c0a35c36d 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -40,6 +40,7 @@
> #include "libavutil/time_internal.h"
>
> #include "libavcodec/defs.h"
> +#include "libavcodec/hls.h"
>
> #include "avformat.h"
> #include "avio_internal.h"
> @@ -344,89 +345,17 @@ static void write_codec_attr(AVStream *st, VariantStream *vs)
> {
> int codec_strlen = strlen(vs->codec_attr);
> char attr[32];
> + int ret;
>
> if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
> return;
> 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;
> - int level = AV_LEVEL_UNKNOWN;
> -
> - 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;
> - /* 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) */
> - profile = rbsp_buf[1] & 0x1f;
> - /* 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 &&
> - level != AV_LEVEL_UNKNOWN) {
> - snprintf(attr, sizeof(attr), "%s.%d.4.L%d.B01", av_fourcc2str(st->codecpar->codec_tag), profile, level);
> - } 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) {
> - /* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */
> - 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 {
> - goto fail;
> - }
> + ret = av_hls_codec_attr(st, attr, sizeof(attr));
> + if (ret < 0)
> + goto fail;
> +
> // Don't write the same attribute multiple times
> if (!av_stristr(vs->codec_attr, attr)) {
> snprintf(vs->codec_attr + codec_strlen,
1. I agree with Zhao that this should not be public.
2. Did you test this with shared builds? It should still fail, because
lavc does not link to lavf. It is the other way around.
3. Why did you put this in lavc anyway?
- Andreas
More information about the ffmpeg-devel
mailing list