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
62 rtpdec_mpeg12.o \62 rtpdec_mpeg12.o \
63 rtpdec_mpeg4.o \63 rtpdec_mpeg4.o \
64 rtpdec_mpegts.o \64 rtpdec_mpegts.o \
65 rtpdec_opus.o \
65 rtpdec_qcelp.o \66 rtpdec_qcelp.o \
66 rtpdec_qdm2.o \67 rtpdec_qdm2.o \
67 rtpdec_qt.o \68 rtpdec_qt.o \
libavformat/rtpdec.c+1−55
@@ -61,12 +61,6 @@ static const RTPDynamicProtocolHandler speex_dynamic_handler = { · 60 unmodified lines
61 .codec_id = AV_CODEC_ID_SPEEX,61 .codec_id = AV_CODEC_ID_SPEEX,
62};62};
63 63
64static const RTPDynamicProtocolHandler opus_dynamic_handler = {
65 .enc_name = "opus",
66 .codec_type = AVMEDIA_TYPE_AUDIO,
67 .codec_id = AV_CODEC_ID_OPUS,
68};
69
70static const RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */64static const RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */
71 .enc_name = "t140",65 .enc_name = "t140",
72 .codec_type = AVMEDIA_TYPE_SUBTITLE,66 .codec_type = AVMEDIA_TYPE_SUBTITLE,
@@ -125,7 +119,7 @@ static const RTPDynamicProtocolHandler *const rtp_dynamic_protocol_handler_list[ · 52 unmodified lines
125 &ff_vp9_dynamic_handler,119 &ff_vp9_dynamic_handler,
126 &gsm_dynamic_handler,120 &gsm_dynamic_handler,
127 &l24_dynamic_handler,121 &l24_dynamic_handler,
128 &opus_dynamic_handler,122 &ff_opus_dynamic_handler,
129 &realmedia_mp3_dynamic_handler,123 &realmedia_mp3_dynamic_handler,
130 &speex_dynamic_handler,124 &speex_dynamic_handler,
131 &t140_dynamic_handler,125 &t140_dynamic_handler,
@@ -531,43 +525,6 @@ int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, · 399 unmodified lines
531 return 0;525 return 0;
532}526}
533 527
534static 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
571/**528/**
572 * open a new RTP parse context for stream 'st'. 'st' can be NULL for529 * open a new RTP parse context for stream 'st'. 'st' can be NULL for
573 * MPEG-2 TS streams.530 * MPEG-2 TS streams.
@@ -576,7 +533,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, · 2 unmodified lines
576 int payload_type, int queue_size)533 int payload_type, int queue_size)
577{534{
578 RTPDemuxContext *s;535 RTPDemuxContext *s;
579 int ret;
580 536
581 s = av_mallocz(sizeof(RTPDemuxContext));537 s = av_mallocz(sizeof(RTPDemuxContext));
582 if (!s)538 if (!s)
@@ -600,16 +556,6 @@ RTPDemuxContext *ff_rtp_parse_open(AVFormatContext *s1, AVStream *st, · 17 unmodified lines
600 if (st->codecpar->sample_rate == 8000)556 if (st->codecpar->sample_rate == 8000)
601 st->codecpar->sample_rate = 16000;557 st->codecpar->sample_rate = 16000;
602 break;558 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;
613 default:559 default:
614 break;560 break;
615 }561 }
libavformat/rtpdec_formats.h+1−0
@@ -77,6 +77,7 @@ extern const RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler; · 76 unmodified lines
77extern const RTPDynamicProtocolHandler ff_mpegts_dynamic_handler;77extern const RTPDynamicProtocolHandler ff_mpegts_dynamic_handler;
78extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfa_handler;78extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfa_handler;
79extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler;79extern const RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler;
80extern const RTPDynamicProtocolHandler ff_opus_dynamic_handler;
80extern const RTPDynamicProtocolHandler ff_qcelp_dynamic_handler;81extern const RTPDynamicProtocolHandler ff_qcelp_dynamic_handler;
81extern const RTPDynamicProtocolHandler ff_qdm2_dynamic_handler;82extern const RTPDynamicProtocolHandler ff_qdm2_dynamic_handler;
82extern const RTPDynamicProtocolHandler ff_qt_rtp_aud_handler;83extern 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
27static 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
47static 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
84static int opus_init(AVFormatContext *s, int st_index, PayloadContext *priv_data)
85{
86 return opus_write_extradata(s->streams[st_index]->codecpar);
87}
88
89static 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
111const 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