History

libavformat/rtpdec_opus: Set duration field on Opus AVPacket

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.

Jonathan Baudanza committed 1 year ago

Commit 2a180c71 parent 36ec9214 files · +120 −55Browse files

Unified · Split

4 files
  1. libavformat/Makefile +1 −0
  2. libavformat/rtpdec.c +1 −55
  3. libavformat/rtpdec_formats.h +1 −0
  4. libavformat/rtpdec_opus.c +117 −0
libavformat/Makefile+1−0
@@ -62,6 +62,7 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ · 61 unmodified lines
6262 rtpdec_mpeg12.o \
6363 rtpdec_mpeg4.o \
6464 rtpdec_mpegts.o \
65+ rtpdec_opus.o \
6566 rtpdec_qcelp.o \
6667 rtpdec_qdm2.o \
6768 rtpdec_qt.o \
libavformat/rtpdec.c+1−55
@@ -61,12 +61,6 @@ static const RTPDynamicProtocolHandler speex_dynamic_handler = { · 60 unmodified lines
6161 .codec_id = AV_CODEC_ID_SPEEX,
6262 };
6363
64−static const RTPDynamicProtocolHandler opus_dynamic_handler = {
65− .enc_name = "opus",
66− .codec_type = AVMEDIA_TYPE_AUDIO,
67− .codec_id = AV_CODEC_ID_OPUS,
68−};
69−
7064 static const RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */
7165 .enc_name = "t140",
7266 .codec_type = AVMEDIA_TYPE_SUBTITLE,
@@ -125,7 +119,7 @@ static const RTPDynamicProtocolHandler *const rtp_dynamic_protocol_handler_list[ · 52 unmodified lines
125119 &ff_vp9_dynamic_handler,
126120 &gsm_dynamic_handler,
127121 &l24_dynamic_handler,
128− &opus_dynamic_handler,
122+ &ff_opus_dynamic_handler,
129123 &realmedia_mp3_dynamic_handler,
130124 &speex_dynamic_handler,
131125 &t140_dynamic_handler,
@@ -531,43 +525,6 @@ int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, · 399 unmodified lines
531525 return 0;
532526 }
533527
534−static int opus_write_extradata(AVCodecParameters *codecpar)
535−{
536− uint8_t *bs;
537− int ret;
538−
539− /* This function writes an extradata with a channel mapping family of 0.
540− * This mapping family only supports mono and stereo layouts. And RFC7587
541− * specifies that the number of channels in the SDP must be 2.
542− */
543− if (codecpar->ch_layout.nb_channels > 2) {
544− return AVERROR_INVALIDDATA;
545− }
546−
547− ret = ff_alloc_extradata(codecpar, 19);
548− if (ret < 0)
549− return ret;
550−
551− bs = (uint8_t *)codecpar->extradata;
552−
553− /* Opus magic */
554− bytestream_put_buffer(&bs, "OpusHead", 8);
555− /* Version */
556− bytestream_put_byte (&bs, 0x1);
557− /* Channel count */
558− bytestream_put_byte (&bs, codecpar->ch_layout.nb_channels);
559− /* Pre skip */
560− bytestream_put_le16 (&bs, 0);
561− /* Input sample rate */
562− bytestream_put_le32 (&bs, 48000);
563− /* Output gain */
564− bytestream_put_le16 (&bs, 0x0);
565− /* Mapping family */
566− bytestream_put_byte (&bs, 0x0);
567−
568− return 0;
569−}
570−
571528 /**
572529 * open a new RTP parse context for stream 'st'. 'st' can be NULL for
573530 * MPEG-2 TS streams.
@@ -576,7 +533,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, · 2 unmodified lines
576533 int payload_type, int queue_size)
577534 {
578535 RTPDemuxContext *s;
579− int ret;
580536
581537 s = av_mallocz(sizeof(RTPDemuxContext));
582538 if (!s)
@@ -600,16 +556,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, · 17 unmodified lines
600556 if (st->codecpar->sample_rate == 8000)
601557 st->codecpar->sample_rate = 16000;
602558 break;
603− case AV_CODEC_ID_OPUS:
604− ret = opus_write_extradata(st->codecpar);
605− if (ret < 0) {
606− av_log(s1, AV_LOG_ERROR,
607− "Error creating opus extradata: %s\n",
608− av_err2str(ret));
609− av_free(s);
610− return NULL;
611− }
612− break;
613559 default:
614560 break;
615561 }
libavformat/rtpdec_formats.h+1−0
@@ -77,6 +77,7 @@ extern const RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler; · 76 unmodified lines
7777 extern const RTPDynamicProtocolHandler ff_mpegts_dynamic_handler;
7878 extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfa_handler;
7979 extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler;
80+extern const RTPDynamicProtocolHandler ff_opus_dynamic_handler;
8081 extern const RTPDynamicProtocolHandler ff_qcelp_dynamic_handler;
8182 extern const RTPDynamicProtocolHandler ff_qdm2_dynamic_handler;
8283 extern const RTPDynamicProtocolHandler ff_qt_rtp_aud_handler;
libavformat/rtpdec_opus.c+117−0
@@ -0,0 +1,117 @@
1+/*
2+ * RTP Depacketization of Opus, RFC 7587
3+ * Copyright (c) 2025 Jonathan Baudanza <jon@jonb.org>
4+ *
5+ * This file is part of FFmpeg.
6+ *
7+ * FFmpeg is free software; you can redistribute it and/or
8+ * modify it under the terms of the GNU Lesser General Public
9+ * License as published by the Free Software Foundation; either
10+ * version 2.1 of the License, or (at your option) any later version.
11+ *
12+ * FFmpeg is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public
18+ * License along with FFmpeg; if not, write to the Free Software
19+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+ */
21+
22+#include "libavcodec/bytestream.h"
23+#include "libavutil/mem.h"
24+#include "rtpdec_formats.h"
25+#include "internal.h"
26+
27+static int opus_duration(const uint8_t *src, int size)
28+{
29+ unsigned nb_frames = 1;
30+ unsigned toc = src[0];
31+ unsigned toc_config = toc >> 3;
32+ unsigned toc_count = toc & 3;
33+ unsigned frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) :
34+ toc_config < 16 ? 480 << (toc_config & 1) :
35+ 120 << (toc_config & 3);
36+ if (toc_count == 3) {
37+ if (size<2)
38+ return AVERROR_INVALIDDATA;
39+ nb_frames = src[1] & 0x3F;
40+ } else if (toc_count) {
41+ nb_frames = 2;
42+ }
43+
44+ return frame_size * nb_frames;
45+}
46+
47+static int opus_write_extradata(AVCodecParameters *codecpar)
48+{
49+ uint8_t *bs;
50+ int ret;
51+
52+ /* This function writes an extradata with a channel mapping family of 0.
53+ * This mapping family only supports mono and stereo layouts. And RFC7587
54+ * specifies that the number of channels in the SDP must be 2.
55+ */
56+ if (codecpar->ch_layout.nb_channels > 2) {
57+ return AVERROR_INVALIDDATA;
58+ }
59+
60+ ret = ff_alloc_extradata(codecpar, 19);
61+ if (ret < 0)
62+ return ret;
63+
64+ bs = (uint8_t *)codecpar->extradata;
65+
66+ /* Opus magic */
67+ bytestream_put_buffer(&bs, "OpusHead", 8);
68+ /* Version */
69+ bytestream_put_byte (&bs, 0x1);
70+ /* Channel count */
71+ bytestream_put_byte (&bs, codecpar->ch_layout.nb_channels);
72+ /* Pre skip */
73+ bytestream_put_le16 (&bs, 0);
74+ /* Input sample rate */
75+ bytestream_put_le32 (&bs, 48000);
76+ /* Output gain */
77+ bytestream_put_le16 (&bs, 0x0);
78+ /* Mapping family */
79+ bytestream_put_byte (&bs, 0x0);
80+
81+ return 0;
82+}
83+
84+static int opus_init(AVFormatContext *s, int st_index, PayloadContext *priv_data)
85+{
86+ return opus_write_extradata(s->streams[st_index]->codecpar);
87+}
88+
89+static int opus_parse_packet(AVFormatContext *ctx, PayloadContext *data,
90+ AVStream *st, AVPacket *pkt, uint32_t *timestamp,
91+ const uint8_t *buf, int len, uint16_t seq,
92+ int flags)
93+{
94+ int rv;
95+ int duration;
96+
97+ if ((rv = av_new_packet(pkt, len)) < 0)
98+ return rv;
99+
100+ memcpy(pkt->data, buf, len);
101+ pkt->stream_index = st->index;
102+
103+ duration = opus_duration(buf, len);
104+ if (duration != AVERROR_INVALIDDATA) {
105+ pkt->duration = duration;
106+ }
107+
108+ return 0;
109+}
110+
111+const RTPDynamicProtocolHandler ff_opus_dynamic_handler = {
112+ .enc_name = "opus",
113+ .codec_type = AVMEDIA_TYPE_AUDIO,
114+ .codec_id = AV_CODEC_ID_OPUS,
115+ .parse_packet = opus_parse_packet,
116+ .init = opus_init,
117+};

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