This commit will properly set the duration field of Opus AVPackets. Currently, duration is set to 0 on Opus packets from the RTP demuxer.
The Ogg muxer depends on the duration field to properly compute the page granule value. Without a proper duration, the granule will be wrong, and result in negative pts values in ogg files. See oggenc.c:657 (ogg_write_packet_internal) This commit calculates using the opus_duration function, which was copied from oggparseopus.c I moved this functionality and the existing opus extradata functionality (added by me in 6c24f2b) into a new rtpdec_opus.c file. --- libavformat/Makefile | 1 + libavformat/rtpdec.c | 56 +---------------- libavformat/rtpdec_formats.h | 1 + libavformat/rtpdec_opus.c | 117 +++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 55 deletions(-) create mode 100644 libavformat/rtpdec_opus.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 6c9992adab..ee68345858 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -62,6 +62,7 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ rtpdec_mpeg12.o \ rtpdec_mpeg4.o \ rtpdec_mpegts.o \ + rtpdec_opus.o \ rtpdec_qcelp.o \ rtpdec_qdm2.o \ rtpdec_qt.o \ diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c index a7d5a79a83..5eff1552f0 100644 --- a/libavformat/rtpdec.c +++ b/libavformat/rtpdec.c @@ -61,12 +61,6 @@ static const RTPDynamicProtocolHandler speex_dynamic_handler = { .codec_id = AV_CODEC_ID_SPEEX, }; -static const RTPDynamicProtocolHandler opus_dynamic_handler = { - .enc_name = "opus", - .codec_type = AVMEDIA_TYPE_AUDIO, - .codec_id = AV_CODEC_ID_OPUS, -}; - static const RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */ .enc_name = "t140", .codec_type = AVMEDIA_TYPE_SUBTITLE, @@ -125,7 +119,7 @@ static const RTPDynamicProtocolHandler *const rtp_dynamic_protocol_handler_list[ &ff_vp9_dynamic_handler, &gsm_dynamic_handler, &l24_dynamic_handler, - &opus_dynamic_handler, + &ff_opus_dynamic_handler, &realmedia_mp3_dynamic_handler, &speex_dynamic_handler, &t140_dynamic_handler, @@ -531,43 +525,6 @@ int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, return 0; } -static int opus_write_extradata(AVCodecParameters *codecpar) -{ - uint8_t *bs; - int ret; - - /* This function writes an extradata with a channel mapping family of 0. - * This mapping family only supports mono and stereo layouts. And RFC7587 - * specifies that the number of channels in the SDP must be 2. - */ - if (codecpar->ch_layout.nb_channels > 2) { - return AVERROR_INVALIDDATA; - } - - ret = ff_alloc_extradata(codecpar, 19); - if (ret < 0) - return ret; - - bs = (uint8_t *)codecpar->extradata; - - /* Opus magic */ - bytestream_put_buffer(&bs, "OpusHead", 8); - /* Version */ - bytestream_put_byte (&bs, 0x1); - /* Channel count */ - bytestream_put_byte (&bs, codecpar->ch_layout.nb_channels); - /* Pre skip */ - bytestream_put_le16 (&bs, 0); - /* Input sample rate */ - bytestream_put_le32 (&bs, 48000); - /* Output gain */ - bytestream_put_le16 (&bs, 0x0); - /* Mapping family */ - bytestream_put_byte (&bs, 0x0); - - return 0; -} - /** * open a new RTP parse context for stream 'st'. 'st' can be NULL for * MPEG-2 TS streams. @@ -576,7 +533,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, int queue_size) { RTPDemuxContext *s; - int ret; s = av_mallocz(sizeof(RTPDemuxContext)); if (!s) @@ -600,16 +556,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, if (st->codecpar->sample_rate == 8000) st->codecpar->sample_rate = 16000; break; - case AV_CODEC_ID_OPUS: - ret = opus_write_extradata(st->codecpar); - if (ret < 0) { - av_log(s1, AV_LOG_ERROR, - "Error creating opus extradata: %s\n", - av_err2str(ret)); - av_free(s); - return NULL; - } - break; default: break; } diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h index 72a8f16a90..1ff2a72d2a 100644 --- a/libavformat/rtpdec_formats.h +++ b/libavformat/rtpdec_formats.h @@ -77,6 +77,7 @@ extern const RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler; extern const RTPDynamicProtocolHandler ff_mpegts_dynamic_handler; extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfa_handler; extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler; +extern const RTPDynamicProtocolHandler ff_opus_dynamic_handler; extern const RTPDynamicProtocolHandler ff_qcelp_dynamic_handler; extern const RTPDynamicProtocolHandler ff_qdm2_dynamic_handler; extern const RTPDynamicProtocolHandler ff_qt_rtp_aud_handler; diff --git a/libavformat/rtpdec_opus.c b/libavformat/rtpdec_opus.c new file mode 100644 index 0000000000..1588f6d715 --- /dev/null +++ b/libavformat/rtpdec_opus.c @@ -0,0 +1,117 @@ +/* + * RTP Depacketization of Opus, RFC 7587 + * Copyright (c) 2025 Jonathan Baudanza <j...@jonb.org> + * + * 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 "libavcodec/bytestream.h" +#include "libavutil/mem.h" +#include "rtpdec_formats.h" +#include "internal.h" + +static int opus_duration(const uint8_t *src, int size) +{ + unsigned nb_frames = 1; + unsigned toc = src[0]; + unsigned toc_config = toc >> 3; + unsigned toc_count = toc & 3; + unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : + toc_config < 16 ? 480 << (toc_config & 1) : + 120 << (toc_config & 3); + if (toc_count == 3) { + if (size<2) + return AVERROR_INVALIDDATA; + nb_frames = src[1] & 0x3F; + } else if (toc_count) { + nb_frames = 2; + } + + return frame_size * nb_frames; +} + +static int opus_write_extradata(AVCodecParameters *codecpar) +{ + uint8_t *bs; + int ret; + + /* This function writes an extradata with a channel mapping family of 0. + * This mapping family only supports mono and stereo layouts. And RFC7587 + * specifies that the number of channels in the SDP must be 2. + */ + if (codecpar->ch_layout.nb_channels > 2) { + return AVERROR_INVALIDDATA; + } + + ret = ff_alloc_extradata(codecpar, 19); + if (ret < 0) + return ret; + + bs = (uint8_t *)codecpar->extradata; + + /* Opus magic */ + bytestream_put_buffer(&bs, "OpusHead", 8); + /* Version */ + bytestream_put_byte (&bs, 0x1); + /* Channel count */ + bytestream_put_byte (&bs, codecpar->ch_layout.nb_channels); + /* Pre skip */ + bytestream_put_le16 (&bs, 0); + /* Input sample rate */ + bytestream_put_le32 (&bs, 48000); + /* Output gain */ + bytestream_put_le16 (&bs, 0x0); + /* Mapping family */ + bytestream_put_byte (&bs, 0x0); + + return 0; +} + +static int opus_init(AVFormatContext *s, int st_index, PayloadContext *priv_data) +{ + return opus_write_extradata(s->streams[st_index]->codecpar); +} + +static int opus_parse_packet(AVFormatContext *ctx, PayloadContext *data, + AVStream *st, AVPacket *pkt, uint32_t *timestamp, + const uint8_t *buf, int len, uint16_t seq, + int flags) +{ + int rv; + int duration; + + if ((rv = av_new_packet(pkt, len)) < 0) + return rv; + + memcpy(pkt->data, buf, len); + pkt->stream_index = st->index; + + duration = opus_duration(buf, len); + if (duration != AVERROR_INVALIDDATA) { + pkt->duration = duration; + } + + return 0; +} + +const RTPDynamicProtocolHandler ff_opus_dynamic_handler = { + .enc_name = "opus", + .codec_type = AVMEDIA_TYPE_AUDIO, + .codec_id = AV_CODEC_ID_OPUS, + .parse_packet = opus_parse_packet, + .init = opus_init, +}; -- 2.41.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".