[FFmpeg-devel] [PATCH 0/1] [FFmpeg-devel, WIP] libopusdec: Enable FEC/PLC

2021-02-16 Thread Philip-Dylan Gleonec
Hello,

As part of GNU Jami development, I've picked up this patch and worked
to complete and validate it.
The few changes I've made are mostly to add checks to manage errors
and different cases, notably video vs audio containers. I have tested
the feature with opus stream encapsulated in mkv, opus, and RTP stream.
In the last case, packets can be dropped with the tc command.
Thanks a lot Steiner for your original work, it helped a lot.

Thanks for your time.

Philip-Dylan Gleonec (1):
  avcodec/libopusdec: Enable FEC/PLC

 libavcodec/libopusdec.c | 105 +++-
 1 file changed, 94 insertions(+), 11 deletions(-)

-- 
2.25.1

___
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".

[FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-16 Thread Philip-Dylan Gleonec
Adds FEC/PLC support to libopus. The lost packets are detected as a
discontinuity in the audio stream. When a discontinuity is used, this
patch tries to decode the FEC data. If FEC data is present in the
packet, it is decoded, otherwise audio is re-created through PLC.

This patch is based on Steinar H. Gunderson contribution, and corrects
the pts computation: all pts are expressed in samples instead of time.
This patch also adds an option "decode_fec" which enables or disables
FEC decoding. This option is disabled by default to keep consistent
behaviour with former versions.

A number of checks are made to ensure compatibility with different
containers. Indeed, video containers seem to have a pts expressed in ms
while it is expressed in samples for audio containers. It also manages
the cases where pkt->duration is 0, in some RTP streams. This patch
ignores data it can not reconstruct, i.e. packets received twice and
packets with a length that is not a multiple of 2.5ms.

Signed-off-by: Philip-Dylan Gleonec 
Co-developed-by: Steinar H. Gunderson 
---
 libavcodec/libopusdec.c | 105 +++-
 1 file changed, 94 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index 082a431c6c..504043353f 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -43,10 +43,15 @@ struct libopus_context {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
 int apply_phase_inv;
 #endif
+int decode_fec;
+int64_t expected_next_pts;
 };
 
 #define OPUS_HEAD_SIZE 19
 
+// Sample rate is constant as libopus always output at 48kHz
+#define OPUS_SAMPLERATE 48000
+
 static av_cold int libopus_decode_init(AVCodecContext *avc)
 {
 struct libopus_context *opus = avc->priv_data;
@@ -134,6 +139,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
 /* Decoder delay (in samples) at 48kHz */
 avc->delay = avc->internal->skip_samples = opus->pre_skip;
 
+opus->expected_next_pts = AV_NOPTS_VALUE;
+
 return 0;
 }
 
@@ -155,25 +162,100 @@ static int libopus_decode(AVCodecContext *avc, void 
*data,
 {
 struct libopus_context *opus = avc->priv_data;
 AVFrame *frame   = data;
-int ret, nb_samples;
+uint8_t *outptr;
+int ret, nb_samples = 0, nb_lost_samples = 0, nb_samples_left;
+
+// If FEC is enabled, calculate number of lost samples
+if (opus->decode_fec &&
+opus->expected_next_pts != AV_NOPTS_VALUE &&
+pkt->pts != AV_NOPTS_VALUE &&
+pkt->pts != opus->expected_next_pts) {
+// Cap at recovering 120 ms of lost audio.
+nb_lost_samples = pkt->pts - opus->expected_next_pts;
+nb_lost_samples = FFMIN(nb_lost_samples, MAX_FRAME_SIZE);
+// pts is expressed in ms for some containers (e.g. mkv)
+// FEC only works for SILK frames (> 10ms)
+// Detect if nb_lost_samples is in ms, and convert in samples if it is
+if (nb_lost_samples > 0) {
+if (pkt->duration > 0 && pkt->duration < OPUS_SAMPLERATE * 10 / 
1000) {
+nb_lost_samples = nb_lost_samples * OPUS_SAMPLERATE / 1000;
+}
+// For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms
+if (nb_lost_samples % (int)(2.5 / 1000 * OPUS_SAMPLERATE)) {
+nb_lost_samples -= nb_lost_samples % (int)(2.5 / 1000 * 
OPUS_SAMPLERATE);
+}
+}
+}
 
-frame->nb_samples = MAX_FRAME_SIZE;
+frame->nb_samples = MAX_FRAME_SIZE + nb_lost_samples;
 if ((ret = ff_get_buffer(avc, frame, 0)) < 0)
 return ret;
 
+outptr = frame->data[0];
+nb_samples_left = frame->nb_samples;
+
+if (opus->decode_fec && nb_lost_samples > 0) {
+// Try to recover the lost samples with FEC data from this one.
+// If there's no FEC data, the decoder will do loss concealment 
instead.
+if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
+ret = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
+  (opus_int16 *)outptr,
+  nb_lost_samples, 1);
+else
+ret = opus_multistream_decode_float(opus->dec, pkt->data, 
pkt->size,
+   (float *)outptr,
+   nb_lost_samples, 1);
+
+if (ret < 0) {
+if (opus->decode_fec) opus->expected_next_pts = pkt->pts + 
pkt->duration;
+av_log(avc, AV_LOG_ERROR, "Decoding error: %s\n",
+   opus_strerror(ret));
+return ff_opus_error_to_averror(ret);
+}
+
+av_log(avc, AV_LOG_WARNING, "Recovered %d samples with FEC/PLC\n",
+

[FFmpeg-devel] [PATCH] avcodec/libopusenc: reload packet loss at encode

2021-02-16 Thread Philip-Dylan Gleonec
An estimation of packet loss is required by libopus to compute its FEC
data. Currently, this estimation is constant, and can not be changed
after configuration. This means an application using libopus through
ffmpeg can not adapt the packet loss estimation when the network
quality degrades.

This patch makes the encoder reload the packet_loss AVOption before
encoding samples, if fec is enabled. This way an application can modify
the packet loss estimation by changing the AVOption. Typical use-case
is a RTP stream, where packet loss can be estimated from RTCP packets.

Signed-off-by: Philip-Dylan Gleonec 
---
 libavcodec/libopusenc.c | 9 +
 1 file changed, 9 insertions(+)

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index 70d17f802b..c18e8ae7fa 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -460,6 +460,15 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
 int ret;
 int discard_padding;
 
+if (opus->opts.fec) {
+ret = opus_multistream_encoder_ctl(opus->enc,
+   
OPUS_SET_PACKET_LOSS_PERC(opus->opts.packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+   "Unable to set expected packet loss percentage: %s\n",
+   opus_strerror(ret));
+}
+
 if (frame) {
 ret = ff_af_queue_add(&opus->afq, frame);
 if (ret < 0)
-- 
2.25.1

___
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".

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-17 Thread Philip-Dylan Gleonec
> Could you elaborate?
> I would have expected that the normal use case is not have a
> lossy input and that the new feature is always useful if data
> was lost.

The use-case for FEC is typically RTP stream where audio is compressed
with opus. In that case, depending on the network conditions, packets
can be lost. From the decoder side, this can be observed as a packet
with a pts that is incremented more than we would expect.

With this patch, if no discontinuity is detected, the packet is decoded
as usual. If a discontinuity is detected, its duration is checked to
deduct how many samples should be reconstructed. One limitation of opus
FEC is that packets can be reconstructed with a granularity of 2.5ms
(120 samples), so libopus can not jsut reconstruct an arbitrary number
of samples. This patch manages this by "rounding" the number of lost
samples to the closest number of samples that libopsu can reconstruct.

> Or is there no way to distinguish between a stream with
> unusual timestamps and a stream with packet loss?

From my understanding, the only information at the decoder side are the
metadata associated with the packet to decode, notably the pts, dts and
packet duration. It does not have information about how the packet has
been received (RTP, SDP, other...). Packet loss can typically be
detected at higher layers (RTCP packets for RTP) but not at the decoder
layer.

From observation it seemed that a pts different from the last pts +
packet duration would indicate that some samples were lost. The decoder
than tries to restore as much sample as it can.

Note that packet duration seem to change units depending on how the file
is encapsulated: the same file had a packet->duration of 20 (ms) in a
.mkv container but 960 (samples) in a .opus container. Since the decoder
is not aware of which container is used, it has to "guess" which unit is
used. As FEC is ony used with SILK frames, which have a 10ms (480
samples) minimal duration, and an opus packet has a max duration of
120ms, a threshold can find how the pts is expressed.

An alternative could be to simply provide a new function to decode FEC
data and let the higher layers manage if FEC should be decoded, but this
implies more modification to the stack to make FEC work, while here only
an AVOption has to be set.
___
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".

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-17 Thread Philip-Dylan Gleonec
>> Am Di., 16. Feb. 2021 um 15:02 Uhr schrieb Philip-Dylan Gleonec
>> :
>>
>>>
>>> Adds FEC/PLC support to libopus. The lost packets are detected as a
>>> discontinuity in the audio stream. When a discontinuity is used, this
>>> patch tries to decode the FEC data. If FEC data is present in the
>>> packet, it is decoded, otherwise audio is re-created through PLC.
>>>
>>> This patch is based on Steinar H. Gunderson contribution, and corrects
>>> the pts computation: all pts are expressed in samples instead of time.
>>> This patch also adds an option "decode_fec" which enables or disables
>>> FEC decoding. This option is disabled by default to keep consistent
>>> behaviour with former versions.
>>>
>>
>> Could you elaborate?
>> I would have expected that the normal use case is not have a
>> lossy input and that the new feature is always useful if data
>> was lost.
>>
>> Or is there no way to distinguish between a stream with
>> unusual timestamps and a stream with packet loss?
>>
>
>Streams can switch between being FEC encoded and not on
>a per-frame basis. Opus streams can have odd timestamps
>since the frame size is somewhat variable. So no way to
>check purely on timestamps.

Is there a possibility of the packet duration varying ? I'm not sure
this patch can manage this case, though it could by comparing the pts
to the last pts + the new packet duration.

>I'd like to have the original patch author review this, could
>someone CC him?

I've added him to the CC list.
___
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".

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-17 Thread Philip-Dylan Gleonec
> I've added him to the CC list

Now done.
___
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".

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-18 Thread Philip-Dylan Gleonec

Andreas Rheinhardt:

>>> Could you elaborate?
>>> I would have expected that the normal use case is not have a
>>> lossy input and that the new feature is always useful if data
>>> was lost.
>>
>> The use-case for FEC is typically RTP stream where audio is compressed
>> with opus. In that case, depending on the network conditions, packets
>> can be lost. From the decoder side, this can be observed as a packet
>> with a pts that is incremented more than we would expect.
>>
>> With this patch, if no discontinuity is detected, the packet is decoded
>> as usual. If a discontinuity is detected, its duration is checked to
>> deduct how many samples should be reconstructed. One limitation of opus
>> FEC is that packets can be reconstructed with a granularity of 2.5ms
>> (120 samples), so libopus can not jsut reconstruct an arbitrary number
>> of samples. This patch manages this by "rounding" the number of lost
>> samples to the closest number of samples that libopsu can reconstruct.
>>
>>> Or is there no way to distinguish between a stream with
>>> unusual timestamps and a stream with packet loss?
>>
>> From my understanding, the only information at the decoder side are the
>> metadata associated with the packet to decode, notably the pts, dts and
>> packet duration. It does not have information about how the packet has
>> been received (RTP, SDP, other...). Packet loss can typically be
>> detected at higher layers (RTCP packets for RTP) but not at the decoder
>> layer.
>>
>> From observation it seemed that a pts different from the last pts +
>> packet duration would indicate that some samples were lost. The decoder
>> than tries to restore as much sample as it can.
>>
>> Note that packet duration seem to change units depending on how the file
>> is encapsulated: the same file had a packet->duration of 20 (ms) in a
>> .mkv container but 960 (samples) in a .opus container. Since the decoder
>> is not aware of which container is used, it has to "guess" which unit is
>> used. As FEC is ony used with SILK frames, which have a 10ms (480
>> samples) minimal duration, and an opus packet has a max duration of
>> 120ms, a threshold can find how the pts is expressed.
>
>There is AVCodecContext.pkt_timebase.

Thanks for the feedback, I had missed that property.
I've attached a reworked version of the patch which makes use of it. It
is indeed more clean and precise. I've left a fallback when computing
the expected_next_pts in case it has not been set after creating the
AVCodecContext and left at the {0, 1} value.
>From 098076ac2acdf531fc44b594dee10ceb9b42270e Mon Sep 17 00:00:00 2001
From: Philip-Dylan Gleonec 
Date: Thu, 11 Feb 2021 12:22:03 +0100
Subject: [PATCH] avcodec/libopusdec: Enable FEC/PLC

Adds FEC/PLC support to libopus. The lost packets are detected as a
discontinuity in the audio stream. When a discontinuity is used, this
patch tries to decode the FEC data. If FEC data is present in the
packet, it is decoded, otherwise audio is re-created through PLC.

This patch is based on Steinar H. Gunderson contribution, and corrects
the pts computation: all pts are expressed in samples instead of time.
This patch also adds an option "decode_fec" which enables or disables
FEC decoding. This option is disabled by default to keep consistent
behaviour with former versions.

A number of checks are made to ensure compatibility with different
containers. Indeed, video containers seem to have a pts expressed in ms
while it is expressed in samples for audio containers. It also manages
the cases where pkt->duration is 0, in some RTP streams. This patch
ignores data it can not reconstruct, i.e. packets received twice and
packets with a length that is not a multiple of 2.5ms.

Signed-off-by: Philip-Dylan Gleonec 
Co-developed-by: Steinar H. Gunderson 
---
 libavcodec/libopusdec.c | 107 +++-
 1 file changed, 96 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index 082a431c6c..3de784dfbd 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -43,10 +43,15 @@ struct libopus_context {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
 int apply_phase_inv;
 #endif
+int decode_fec;
+int64_t expected_next_pts;
 };
 
 #define OPUS_HEAD_SIZE 19
 
+// Sample rate is constant as libopus always output at 48kHz
+const AVRational opus_timebase = { 1, 48000 };
+
 static av_cold int libopus_decode_init(AVCodecContext *avc)
 {
 struct libopus_context *opus = avc->priv_data;
@@ -134,6 +139,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
 /* Decoder delay (in samples) at 48kHz */
 avc->delay = avc->

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-02-18 Thread Philip-Dylan Gleonec
Here is the reworked patch properly attached.
Sorry about the duplicate mail, I just noticed I had a mishap with my
mail client and the previous patch was scrubbed.

Signed-off-by: Philip-Dylan Gleonec 
Co-developed-by: Steinar H. Gunderson 
---
 libavcodec/libopusdec.c | 107 +++-
 1 file changed, 96 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index 082a431c6c..3de784dfbd 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -43,10 +43,15 @@ struct libopus_context {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
 int apply_phase_inv;
 #endif
+int decode_fec;
+int64_t expected_next_pts;
 };
 
 #define OPUS_HEAD_SIZE 19
 
+// Sample rate is constant as libopus always output at 48kHz
+const AVRational opus_timebase = { 1, 48000 };
+
 static av_cold int libopus_decode_init(AVCodecContext *avc)
 {
 struct libopus_context *opus = avc->priv_data;
@@ -134,6 +139,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
 /* Decoder delay (in samples) at 48kHz */
 avc->delay = avc->internal->skip_samples = opus->pre_skip;
 
+opus->expected_next_pts = AV_NOPTS_VALUE;
+
 return 0;
 }
 
@@ -155,25 +162,102 @@ static int libopus_decode(AVCodecContext *avc, void 
*data,
 {
 struct libopus_context *opus = avc->priv_data;
 AVFrame *frame   = data;
-int ret, nb_samples;
+uint8_t *outptr;
+int ret, nb_samples = 0, nb_lost_samples = 0, nb_samples_left;
+
+// If FEC is enabled, calculate number of lost samples
+if (opus->decode_fec &&
+opus->expected_next_pts != AV_NOPTS_VALUE &&
+pkt->pts != AV_NOPTS_VALUE &&
+pkt->pts != opus->expected_next_pts) {
+// Cap at recovering 120 ms of lost audio.
+nb_lost_samples = pkt->pts - opus->expected_next_pts;
+nb_lost_samples = FFMIN(nb_lost_samples, MAX_FRAME_SIZE);
+// pts is expressed in ms for some containers (e.g. mkv)
+// FEC only works for SILK frames (> 10ms)
+// Detect if nb_lost_samples is in ms, and convert in samples if it is
+if (nb_lost_samples > 0) {
+if (avc->pkt_timebase.den != 48000) {
+nb_lost_samples = av_rescale_q(nb_lost_samples, 
avc->pkt_timebase, opus_timebase);
+}
+// For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms
+if (nb_lost_samples % (int)(2.5 / 1000 * opus_timebase.den)) {
+nb_lost_samples -= nb_lost_samples % (int)(2.5 / 1000 * 
opus_timebase.den);
+}
+}
+}
 
-frame->nb_samples = MAX_FRAME_SIZE;
+frame->nb_samples = MAX_FRAME_SIZE + nb_lost_samples;
 if ((ret = ff_get_buffer(avc, frame, 0)) < 0)
 return ret;
 
+outptr = frame->data[0];
+nb_samples_left = frame->nb_samples;
+
+if (opus->decode_fec && nb_lost_samples > 0) {
+// Try to recover the lost samples with FEC data from this one.
+// If there's no FEC data, the decoder will do loss concealment 
instead.
+if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
+ret = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
+  (opus_int16 *)outptr,
+  nb_lost_samples, 1);
+else
+ret = opus_multistream_decode_float(opus->dec, pkt->data, 
pkt->size,
+   (float *)outptr,
+   nb_lost_samples, 1);
+
+if (ret < 0) {
+if (opus->decode_fec) opus->expected_next_pts = pkt->pts + 
pkt->duration;
+av_log(avc, AV_LOG_ERROR, "Decoding error: %s\n",
+   opus_strerror(ret));
+return ff_opus_error_to_averror(ret);
+}
+
+av_log(avc, AV_LOG_WARNING, "Recovered %d samples with FEC/PLC\n",
+   ret);
+
+outptr += ret * avc->channels * 
av_get_bytes_per_sample(avc->sample_fmt);
+nb_samples_left -= ret;
+nb_samples += ret;
+if (pkt->pts != AV_NOPTS_VALUE) {
+frame->pts = pkt->pts - ret;
+}
+}
+
+// Decode the actual, non-lost data.
 if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
-nb_samples = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
- (opus_int16 *)frame->data[0],
- frame->nb_samples, 0);
+ret = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
+  (opus_int16 *)outptr,
+  nb_samples_left, 0);
 else
-nb_samples = opu

Re: [FFmpeg-devel] [PATCH] avcodec/libopusenc: reload packet loss at encode

2021-03-03 Thread Philip-Dylan Gleonec

Le 16/02/2021 à 15:04, Philip-Dylan Gleonec a écrit :

An estimation of packet loss is required by libopus to compute its FEC
data. Currently, this estimation is constant, and can not be changed
after configuration. This means an application using libopus through
ffmpeg can not adapt the packet loss estimation when the network
quality degrades.

This patch makes the encoder reload the packet_loss AVOption before
encoding samples, if fec is enabled. This way an application can modify
the packet loss estimation by changing the AVOption. Typical use-case
is a RTP stream, where packet loss can be estimated from RTCP packets.

Signed-off-by: Philip-Dylan Gleonec 
---
  libavcodec/libopusenc.c | 9 +
  1 file changed, 9 insertions(+)

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index 70d17f802b..c18e8ae7fa 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -460,6 +460,15 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
  int ret;
  int discard_padding;
  
+if (opus->opts.fec) {

+ret = opus_multistream_encoder_ctl(opus->enc,
+   
OPUS_SET_PACKET_LOSS_PERC(opus->opts.packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+   "Unable to set expected packet loss percentage: %s\n",
+   opus_strerror(ret));
+}
+
  if (frame) {
  ret = ff_af_queue_add(&opus->afq, frame);
  if (ret < 0)



Hello,

Is someone interested in picking this up, or is there some correction I 
should make to this patch ?


Regards,
Philip-Dylan Gleonec
___
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".

Re: [FFmpeg-devel] [PATCH 1/1] avcodec/libopusdec: Enable FEC/PLC

2021-03-03 Thread Philip-Dylan Gleonec

Le 18/02/2021 à 17:38, Philip-Dylan Gleonec a écrit :

Here is the reworked patch properly attached.
Sorry about the duplicate mail, I just noticed I had a mishap with my
mail client and the previous patch was scrubbed.



Hello,

Is someone interested in picking this up, or is there some further 
corrections/improvements I should make to this patch ?


Regards,
Philip-Dylan Gleonec
___
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".

[FFmpeg-devel] libopusdec: Enable FEC/PLC

2022-03-16 Thread Philip-Dylan Gleonec
Hello,

Please find attached a rebased patchset for the FEC implementation of
libopus. Following the received feedbacks, some improvements have been
done compared to the first version:
- remove a log when a packet is decoded without FEC 
- add a check to only set libopus encoder packet loss estimate if it
  has not changed since previous encoding

Both patches have passed FATE testing successfully. Moreover, the patch
on libopus decoder is used in production in GNU Jami.

Thanks for your time.


___
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".


[FFmpeg-devel] [PATCH 1/2] avcodec/libopusenc: reload packet loss at encode

2022-03-16 Thread Philip-Dylan Gleonec
An estimation of packet loss is required by libopus to compute its FEC
data. Currently, this estimation is constant, and can not be changed
after configuration. This means an application using libopus through
ffmpeg can not adapt the packet loss estimation when the network
quality degrades.

This patch makes the encoder reload the packet_loss AVOption before
encoding samples, if fec is enabled and the packet loss estimation set
is different than the current one. This way an application can modify
the packet loss estimation by changing the AVOption. Typical use-case
is a RTP stream, where packet loss can be estimated from RTCP packets.

Signed-off-by: Philip-Dylan Gleonec 
---
 libavcodec/libopusenc.c | 17 +
 1 file changed, 17 insertions(+)

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index 45b23fcbb5..b9e2fc45e3 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -460,6 +460,23 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
 uint8_t *audio;
 int ret;
 int discard_padding;
+int32_t opus_packet_loss = 0;
+
+ret = opus_multistream_encoder_ctl(opus->enc,
+OPUS_GET_PACKET_LOSS_PERC(&opus_packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+"Unable to get expected packet loss percentage: %s\n",
+opus_strerror(ret));
+
+if (opus->opts.fec && (opus_packet_loss != opus->opts.packet_loss)) {
+ret = opus_multistream_encoder_ctl(opus->enc,
+
OPUS_SET_PACKET_LOSS_PERC(opus->opts.packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+"Unable to set expected packet loss percentage: %s\n",
+opus_strerror(ret));
+}
 
 if (frame) {
 ret = ff_af_queue_add(&opus->afq, frame);
-- 
2.25.1

___
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".


[FFmpeg-devel] [PATCH 2/2] avcodec/libopusdec: Enable FEC/PLC

2022-03-16 Thread Philip-Dylan Gleonec
Adds FEC/PLC support to libopus. The lost packets are detected as a
discontinuity in the audio stream. When a discontinuity is used, this
patch tries to decode the FEC data. If FEC data is present in the
packet, it is decoded, otherwise audio is re-created through PLC.

This patch is based on Steinar H. Gunderson contribution, and corrects
the pts computation: all pts are expressed in samples instead of time.
This patch also adds an option "decode_fec" which enables or disables
FEC decoding. This option is disabled by default to keep consistent
behaviour with former versions.

A number of checks are made to ensure compatibility with different
containers. Indeed, video containers seem to have a pts expressed in ms
while it is expressed in samples for audio containers. It also manages
the cases where pkt->duration is 0, in some RTP streams. This patch
ignores data it can not reconstruct, i.e. packets received twice and
packets with a length that is not a multiple of 2.5ms.

Signed-off-by: Philip-Dylan Gleonec 
Co-developed-by: Steinar H. Gunderson 
---
 libavcodec/libopusdec.c | 105 +++-
 1 file changed, 94 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index 86ef715205..66134300d2 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -43,10 +43,15 @@ struct libopus_context {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
 int apply_phase_inv;
 #endif
+int decode_fec;
+int64_t expected_next_pts;
 };
 
 #define OPUS_HEAD_SIZE 19
 
+// Sample rate is constant as libopus always output at 48kHz
+const AVRational opus_timebase = { 1, 48000 };
+
 static av_cold int libopus_decode_init(AVCodecContext *avc)
 {
 struct libopus_context *opus = avc->priv_data;
@@ -134,6 +139,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
 /* Decoder delay (in samples) at 48kHz */
 avc->delay = avc->internal->skip_samples = opus->pre_skip;
 
+opus->expected_next_pts = AV_NOPTS_VALUE;
+
 return 0;
 }
 
@@ -155,25 +162,100 @@ static int libopus_decode(AVCodecContext *avc, void 
*data,
 {
 struct libopus_context *opus = avc->priv_data;
 AVFrame *frame   = data;
-int ret, nb_samples;
+uint8_t *outptr;
+int ret, nb_samples = 0, nb_lost_samples = 0, nb_samples_left;
+
+// If FEC is enabled, calculate number of lost samples
+if (opus->decode_fec &&
+opus->expected_next_pts != AV_NOPTS_VALUE &&
+pkt->pts != AV_NOPTS_VALUE &&
+pkt->pts != opus->expected_next_pts) {
+// Cap at recovering 120 ms of lost audio.
+nb_lost_samples = pkt->pts - opus->expected_next_pts;
+nb_lost_samples = FFMIN(nb_lost_samples, MAX_FRAME_SIZE);
+// pts is expressed in ms for some containers (e.g. mkv)
+// FEC only works for SILK frames (> 10ms)
+// Detect if nb_lost_samples is in ms, and convert in samples if it is
+if (nb_lost_samples > 0) {
+if (avc->pkt_timebase.den != 48000) {
+nb_lost_samples = av_rescale_q(nb_lost_samples, 
avc->pkt_timebase, opus_timebase);
+}
+// For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms
+if (nb_lost_samples % (int)(2.5 / 1000 * opus_timebase.den)) {
+nb_lost_samples -= nb_lost_samples % (int)(2.5 / 1000 * 
opus_timebase.den);
+}
+}
+}
 
-frame->nb_samples = MAX_FRAME_SIZE;
+frame->nb_samples = MAX_FRAME_SIZE + nb_lost_samples;
 if ((ret = ff_get_buffer(avc, frame, 0)) < 0)
 return ret;
 
+outptr = frame->data[0];
+nb_samples_left = frame->nb_samples;
+
+if (opus->decode_fec && nb_lost_samples > 0) {
+// Try to recover the lost samples with FEC data from this one.
+// If there's no FEC data, the decoder will do loss concealment 
instead.
+if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
+ret = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
+  (opus_int16 *)outptr,
+  nb_lost_samples, 1);
+else
+ret = opus_multistream_decode_float(opus->dec, pkt->data, 
pkt->size,
+   (float *)outptr,
+   nb_lost_samples, 1);
+
+if (ret < 0) {
+if (opus->decode_fec) opus->expected_next_pts = pkt->pts + 
pkt->duration;
+av_log(avc, AV_LOG_ERROR, "Decoding error: %s\n",
+   opus_strerror(ret));
+return ff_opus_error_to_averror(ret);
+}
+
+av_log(avc, AV_LOG_WARNING, "Recovered %d samples with FEC/PLC\n",
+   ret);
+
+  

Re: [FFmpeg-devel] [PATCH 2/2] avcodec/libopusdec: Enable FEC/PLC

2022-06-16 Thread Philip-Dylan Gleonec
Hello,

Is there some interest in this patch ? If so, is there something I can
modify to improve it ?

Regards,
Philip-Dylan Gleonec
___
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".


Re: [FFmpeg-devel] [PATCH 2/2] avcodec/libopusdec: Enable FEC/PLC

2022-07-04 Thread Philip-Dylan Gleonec
Hello,

Please find attached a new version of the patchset, with the required
corrections. I also added the following changes:
- remove use of avc->channels (deprecated) in favor of avc->ch_layout
- rebase on master

The patches have been tested against FATE, and validated in use on a
rtp stream with packet loss inserted by `tc`, with up to 50% packet
loss.

I'll keep pinging, I wasn't sure what the etiquette was :)

Thanks for yout time.


___
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".


[FFmpeg-devel] [PATCH v2 1/2] avcodec/libopusenc: reload packet loss at encode

2022-07-04 Thread Philip-Dylan Gleonec
An estimation of packet loss is required by libopus to compute its FEC
data. Currently, this estimation is constant, and can not be changed
after configuration. This means an application using libopus through
ffmpeg can not adapt the packet loss estimation when the network
quality degrades.

This patch makes the encoder reload the packet_loss AVOption before
encoding samples, if fec is enabled and the packet loss estimation set
is different than the current one. This way an application can modify
the packet loss estimation by changing the AVOption. Typical use-case
is a RTP stream, where packet loss can be estimated from RTCP packets.

Signed-off-by: Philip-Dylan Gleonec 
---
 libavcodec/libopusenc.c | 17 +
 1 file changed, 17 insertions(+)

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index c884075ffe..26d2082ffa 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -462,6 +462,23 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
 uint8_t *audio;
 int ret;
 int discard_padding;
+int32_t opus_packet_loss = 0;
+
+ret = opus_multistream_encoder_ctl(opus->enc,
+OPUS_GET_PACKET_LOSS_PERC(&opus_packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+"Unable to get expected packet loss percentage: %s\n",
+opus_strerror(ret));
+
+if (opus->opts.fec && (opus_packet_loss != opus->opts.packet_loss)) {
+ret = opus_multistream_encoder_ctl(opus->enc,
+
OPUS_SET_PACKET_LOSS_PERC(opus->opts.packet_loss));
+if (ret != OPUS_OK)
+av_log(avctx, AV_LOG_WARNING,
+"Unable to set expected packet loss percentage: %s\n",
+opus_strerror(ret));
+}
 
 if (frame) {
 ret = ff_af_queue_add(&opus->afq, frame);
-- 
2.25.1

___
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".


[FFmpeg-devel] [PATCH v2 2/2] avcodec/libopusdec: Enable FEC/PLC

2022-07-04 Thread Philip-Dylan Gleonec
Adds FEC/PLC support to libopus. The lost packets are detected as a
discontinuity in the audio stream. When a discontinuity is used, this
patch tries to decode the FEC data. If FEC data is present in the
packet, it is decoded, otherwise audio is re-created through PLC.

This patch is based on Steinar H. Gunderson contribution, and corrects
the pts computation: all pts are expressed in samples instead of time.
This patch also adds an option "decode_fec" which enables or disables
FEC decoding. This option is disabled by default to keep consistent
behaviour with former versions.

A number of checks are made to ensure compatibility with different
containers. Indeed, video containers seem to have a pts expressed in ms
while it is expressed in samples for audio containers. It also manages
the cases where pkt->duration is 0, in some RTP streams. This patch
ignores data it can not reconstruct, i.e. packets received twice and
packets with a length that is not a multiple of 2.5ms.

Signed-off-by: Philip-Dylan Gleonec 
Co-developed-by: Steinar H. Gunderson 
---
 libavcodec/libopusdec.c | 105 +++-
 1 file changed, 94 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index 316ab0f2a7..f5d0e95fc8 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -44,10 +44,15 @@ struct libopus_context {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
 int apply_phase_inv;
 #endif
+int decode_fec;
+int64_t expected_next_pts;
 };
 
 #define OPUS_HEAD_SIZE 19
 
+// Sample rate is constant as libopus always output at 48kHz
+static const AVRational opus_timebase = { 1, 48000 };
+
 static av_cold int libopus_decode_init(AVCodecContext *avc)
 {
 struct libopus_context *opus = avc->priv_data;
@@ -140,6 +145,8 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
 /* Decoder delay (in samples) at 48kHz */
 avc->delay = avc->internal->skip_samples = opus->pre_skip;
 
+opus->expected_next_pts = AV_NOPTS_VALUE;
+
 return 0;
 }
 
@@ -160,25 +167,100 @@ static int libopus_decode(AVCodecContext *avc, AVFrame 
*frame,
   int *got_frame_ptr, AVPacket *pkt)
 {
 struct libopus_context *opus = avc->priv_data;
-int ret, nb_samples;
+uint8_t *outptr;
+int ret, nb_samples = 0, nb_lost_samples = 0, nb_samples_left;
+
+// If FEC is enabled, calculate number of lost samples
+if (opus->decode_fec &&
+opus->expected_next_pts != AV_NOPTS_VALUE &&
+pkt->pts != AV_NOPTS_VALUE &&
+pkt->pts != opus->expected_next_pts) {
+// Cap at recovering 120 ms of lost audio.
+nb_lost_samples = pkt->pts - opus->expected_next_pts;
+nb_lost_samples = FFMIN(nb_lost_samples, MAX_FRAME_SIZE);
+// pts is expressed in ms for some containers (e.g. mkv)
+// FEC only works for SILK frames (> 10ms)
+// Detect if nb_lost_samples is in ms, and convert in samples if it is
+if (nb_lost_samples > 0) {
+if (avc->pkt_timebase.den != 48000) {
+nb_lost_samples = av_rescale_q(nb_lost_samples, 
avc->pkt_timebase, opus_timebase);
+}
+// For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms
+if (nb_lost_samples % (5LL * opus_timebase.den / 2000)) {
+nb_lost_samples -= nb_lost_samples % (5LL * opus_timebase.den 
/ 2000);
+}
+}
+}
 
-frame->nb_samples = MAX_FRAME_SIZE;
+frame->nb_samples = MAX_FRAME_SIZE + nb_lost_samples;
 if ((ret = ff_get_buffer(avc, frame, 0)) < 0)
 return ret;
 
+outptr = frame->data[0];
+nb_samples_left = frame->nb_samples;
+
+if (opus->decode_fec && nb_lost_samples > 0) {
+// Try to recover the lost samples with FEC data from this one.
+// If there's no FEC data, the decoder will do loss concealment 
instead.
+if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
+ret = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
+  (opus_int16 *)outptr,
+  nb_lost_samples, 1);
+else
+ret = opus_multistream_decode_float(opus->dec, pkt->data, 
pkt->size,
+   (float *)outptr,
+   nb_lost_samples, 1);
+
+if (ret < 0) {
+if (opus->decode_fec) opus->expected_next_pts = pkt->pts + 
pkt->duration;
+av_log(avc, AV_LOG_ERROR, "Decoding error: %s\n",
+   opus_strerror(ret));
+return ff_opus_error_to_averror(ret);
+}
+
+av_log(avc, AV_LOG_WARNING, "Recovered %d samples with FEC/PLC\n",
+