1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-10 06:10:52 +02:00

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.

Reviewed-by: Tristan Matthews <tmatth@videolan.org>
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
This commit is contained in:
Jonathan Baudanza
2025-05-12 12:11:14 +09:00
committed by Marvin Scholz
parent 36ec9217e6
commit 2a180c7150
4 changed files with 120 additions and 55 deletions

View File

@@ -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 \

View File

@@ -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;
}

View File

@@ -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;

117
libavformat/rtpdec_opus.c Normal file
View File

@@ -0,0 +1,117 @@
/*
* RTP Depacketization of Opus, RFC 7587
* Copyright (c) 2025 Jonathan Baudanza <jon@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,
};