diff mbox series

[FFmpeg-devel] libavformat/rtpdec_mpeg: handle bare ADTS packets with explicit decoder config

Message ID 20211001225323.32700-1-jeffm@jeffm.io
State New
Headers show
Series [FFmpeg-devel] libavformat/rtpdec_mpeg: handle bare ADTS packets with explicit decoder config | expand

Checks

Context Check Description
andriy/make_x86 fail Make failed

Commit Message

Jeff Mahoney Oct. 1, 2021, 10:53 p.m. UTC
When SDP specifies a decoder config, there may not be any AU headers
provided by the sender.  This can result in rtp_parse_mp4_au failing
and aac_parse_packet reporting "Error parsing AU headers." and no audio
is recovered from the stream.

This commit modifies aac_parse_header to check for an explicit decoder config
set by the sdp parser (e.g. a:fmtp # config=hexvalue). If it has and there
is an ADTS header present, it skips the header and copies the RTP
payload directly as an AAC packet.

This resolves an issue observed with some inexpensive IP cameras.

Signed-off-by: Jeff Mahoney <jeffm@jeffm.io>
---
 libavformat/rtpdec_mpeg4.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

Comments

Jeff Mahoney Oct. 18, 2021, 12:07 a.m. UTC | #1
Hi folks -

Just wanted to follow up on this patch. Is it obviously wrong, it
doesn't scratch a common itch, or... ?  I've tested the camera
connection and ran the patched code through FATE.   The frigate
container I'm using has audio disabled entirely since it's unreliable on
many devices and this fixes it for at least some.  I'd prefer to get the
patch (in this iteration or after review/resubmit cycles) landed so the
container stack can pull it in.

I've been in the Linux world long enough to know I can't expect free
review, but if I've missed something in the process I'd appreciate some
feedback.

Thanks,

-Jeff

On 10/2/21 6:53 PM, Jeff Mahoney wrote:
> When SDP specifies a decoder config, there may not be any AU headers
> provided by the sender.  This can result in rtp_parse_mp4_au failing
> and aac_parse_packet reporting "Error parsing AU headers." and no audio
> is recovered from the stream.
> 
> This commit modifies aac_parse_header to check for an explicit decoder config
> set by the sdp parser (e.g. a:fmtp # config=hexvalue). If it has and there
> is an ADTS header present, it skips the header and copies the RTP
> payload directly as an AAC packet.
> 
> This resolves an issue observed with some inexpensive IP cameras.
> 
> Signed-off-by: Jeff Mahoney <jeffm@jeffm.io>
> ---
>  libavformat/rtpdec_mpeg4.c | 34 +++++++++++++++++++++++++++++++++-
>  1 file changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
> index 34c7950bcc..dd0ced790e 100644
> --- a/libavformat/rtpdec_mpeg4.c
> +++ b/libavformat/rtpdec_mpeg4.c
> @@ -176,7 +176,7 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>                              int flags)
>  {
>      int ret;
> -
> +    AVCodecParameters *par = st->codecpar;
>  
>      if (!buf) {
>          if (data->cur_au_index > data->nb_au_headers) {
> @@ -204,6 +204,38 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>          return 1;
>      }
>  
> +    /* Check for an explicit decoder config (e.g. SDP a:fmtp... config=) */
> +    if (par->extradata && len > 7) {
> +        /*
> +         * Start of ADTS header - syncword
> +         * If present skip the header and copy the entire payload as AAC data
> +         */
> +        if (buf[0] == 0xff && (buf[1] & 0xf0) == 0xf0) {
> +            /*
> +             * The ADTS header is 7 or 9 bytes depending on whether
> +             * the protection absent bit is set.  If it is unset, a 16-bit CRC
> +             * is appended to the header.
> +             */
> +            size_t header_size = 7 + ((buf[1] & 0x01) ? 0 : 2);
> +            if (len < header_size) {
> +                ac_log(ctx, AV_LOG_ERROR, "Error parsing ADTS header\n");
> +                return -1;
> +            }
> +
> +            buf += header_size;
> +            len -= header_size;
> +
> +            if ((ret = av_new_packet(pkt, len)) < 0) {
> +                av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
> +                return ret;
> +            }
> +            memcpy(pkt->data, buf, len);
> +            pkt->stream_index = st->index;
> +
> +            return 0;
> +        }
> +    }
> +
>      if (rtp_parse_mp4_au(data, buf, len)) {
>          av_log(ctx, AV_LOG_ERROR, "Error parsing AU headers\n");
>          return -1;
>
Lance Wang Oct. 18, 2021, 1:28 a.m. UTC | #2
On Fri, Oct 01, 2021 at 06:53:23PM -0400, Jeff Mahoney wrote:
> When SDP specifies a decoder config, there may not be any AU headers
> provided by the sender.  This can result in rtp_parse_mp4_au failing
> and aac_parse_packet reporting "Error parsing AU headers." and no audio
> is recovered from the stream.
> 
> This commit modifies aac_parse_header to check for an explicit decoder config
> set by the sdp parser (e.g. a:fmtp # config=hexvalue). If it has and there
> is an ADTS header present, it skips the header and copies the RTP
> payload directly as an AAC packet.
> 
> This resolves an issue observed with some inexpensive IP cameras.
> 
> Signed-off-by: Jeff Mahoney <jeffm@jeffm.io>
> ---
>  libavformat/rtpdec_mpeg4.c | 34 +++++++++++++++++++++++++++++++++-
>  1 file changed, 33 insertions(+), 1 deletion(-)
> 
> diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
> index 34c7950bcc..dd0ced790e 100644
> --- a/libavformat/rtpdec_mpeg4.c
> +++ b/libavformat/rtpdec_mpeg4.c
> @@ -176,7 +176,7 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>                              int flags)
>  {
>      int ret;
> -
> +    AVCodecParameters *par = st->codecpar;
>  
>      if (!buf) {
>          if (data->cur_au_index > data->nb_au_headers) {
> @@ -204,6 +204,38 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>          return 1;
>      }
>  
> +    /* Check for an explicit decoder config (e.g. SDP a:fmtp... config=) */
> +    if (par->extradata && len > 7) {
> +        /*
> +         * Start of ADTS header - syncword
> +         * If present skip the header and copy the entire payload as AAC data
> +         */
> +        if (buf[0] == 0xff && (buf[1] & 0xf0) == 0xf0) {
> +            /*
> +             * The ADTS header is 7 or 9 bytes depending on whether
> +             * the protection absent bit is set.  If it is unset, a 16-bit CRC
> +             * is appended to the header.
> +             */
> +            size_t header_size = 7 + ((buf[1] & 0x01) ? 0 : 2);
> +            if (len < header_size) {
> +                ac_log(ctx, AV_LOG_ERROR, "Error parsing ADTS header\n");
> +                return -1;
> +            }

I prefer to use avpriv_adts_header_parse() for ADTS header parse.

> +
> +            buf += header_size;
> +            len -= header_size;
> +
> +            if ((ret = av_new_packet(pkt, len)) < 0) {
> +                av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
> +                return ret;
> +            }
> +            memcpy(pkt->data, buf, len);
> +            pkt->stream_index = st->index;
> +
> +            return 0;
> +        }
> +    }
> +
>      if (rtp_parse_mp4_au(data, buf, len)) {
>          av_log(ctx, AV_LOG_ERROR, "Error parsing AU headers\n");
>          return -1;
> -- 
> 2.33.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-request@ffmpeg.org with subject "unsubscribe".
Jeff Mahoney Oct. 19, 2021, 2:10 p.m. UTC | #3
On 10/17/21 21:28, lance.lmwang@gmail.com wrote:
> On Fri, Oct 01, 2021 at 06:53:23PM -0400, Jeff Mahoney wrote:
>> When SDP specifies a decoder config, there may not be any AU headers
>> provided by the sender.  This can result in rtp_parse_mp4_au failing
>> and aac_parse_packet reporting "Error parsing AU headers." and no audio
>> is recovered from the stream.
>>
>> This commit modifies aac_parse_header to check for an explicit decoder config
>> set by the sdp parser (e.g. a:fmtp # config=hexvalue). If it has and there
>> is an ADTS header present, it skips the header and copies the RTP
>> payload directly as an AAC packet.
>>
>> This resolves an issue observed with some inexpensive IP cameras.
>>
>> Signed-off-by: Jeff Mahoney <jeffm@jeffm.io>
>> ---
>>  libavformat/rtpdec_mpeg4.c | 34 +++++++++++++++++++++++++++++++++-
>>  1 file changed, 33 insertions(+), 1 deletion(-)
>>
>> diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
>> index 34c7950bcc..dd0ced790e 100644
>> --- a/libavformat/rtpdec_mpeg4.c
>> +++ b/libavformat/rtpdec_mpeg4.c
>> @@ -176,7 +176,7 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>>                              int flags)
>>  {
>>      int ret;
>> -
>> +    AVCodecParameters *par = st->codecpar;
>>  
>>      if (!buf) {
>>          if (data->cur_au_index > data->nb_au_headers) {
>> @@ -204,6 +204,38 @@ static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
>>          return 1;
>>      }
>>  
>> +    /* Check for an explicit decoder config (e.g. SDP a:fmtp... config=) */
>> +    if (par->extradata && len > 7) {
>> +        /*
>> +         * Start of ADTS header - syncword
>> +         * If present skip the header and copy the entire payload as AAC data
>> +         */
>> +        if (buf[0] == 0xff && (buf[1] & 0xf0) == 0xf0) {
>> +            /*
>> +             * The ADTS header is 7 or 9 bytes depending on whether
>> +             * the protection absent bit is set.  If it is unset, a 16-bit CRC
>> +             * is appended to the header.
>> +             */
>> +            size_t header_size = 7 + ((buf[1] & 0x01) ? 0 : 2);
>> +            if (len < header_size) {
>> +                ac_log(ctx, AV_LOG_ERROR, "Error parsing ADTS header\n");
>> +                return -1;
>> +            }
> 
> I prefer to use avpriv_adts_header_parse() for ADTS header parse.


Thanks, Lance.  This wasn't in the master branch when I posted this.
It's much cleaner and I'm happy to adapt to it.

-Jeff

>> +
>> +            buf += header_size;
>> +            len -= header_size;
>> +
>> +            if ((ret = av_new_packet(pkt, len)) < 0) {
>> +                av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
>> +                return ret;
>> +            }
>> +            memcpy(pkt->data, buf, len);
>> +            pkt->stream_index = st->index;
>> +
>> +            return 0;
>> +        }
>> +    }
>> +
>>      if (rtp_parse_mp4_au(data, buf, len)) {
>>          av_log(ctx, AV_LOG_ERROR, "Error parsing AU headers\n");
>>          return -1;
>> -- 
>> 2.33.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-request@ffmpeg.org with subject "unsubscribe".
>
diff mbox series

Patch

diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c
index 34c7950bcc..dd0ced790e 100644
--- a/libavformat/rtpdec_mpeg4.c
+++ b/libavformat/rtpdec_mpeg4.c
@@ -176,7 +176,7 @@  static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
                             int flags)
 {
     int ret;
-
+    AVCodecParameters *par = st->codecpar;
 
     if (!buf) {
         if (data->cur_au_index > data->nb_au_headers) {
@@ -204,6 +204,38 @@  static int aac_parse_packet(AVFormatContext *ctx, PayloadContext *data,
         return 1;
     }
 
+    /* Check for an explicit decoder config (e.g. SDP a:fmtp... config=) */
+    if (par->extradata && len > 7) {
+        /*
+         * Start of ADTS header - syncword
+         * If present skip the header and copy the entire payload as AAC data
+         */
+        if (buf[0] == 0xff && (buf[1] & 0xf0) == 0xf0) {
+            /*
+             * The ADTS header is 7 or 9 bytes depending on whether
+             * the protection absent bit is set.  If it is unset, a 16-bit CRC
+             * is appended to the header.
+             */
+            size_t header_size = 7 + ((buf[1] & 0x01) ? 0 : 2);
+            if (len < header_size) {
+                ac_log(ctx, AV_LOG_ERROR, "Error parsing ADTS header\n");
+                return -1;
+            }
+
+            buf += header_size;
+            len -= header_size;
+
+            if ((ret = av_new_packet(pkt, len)) < 0) {
+                av_log(ctx, AV_LOG_ERROR, "Out of memory\n");
+                return ret;
+            }
+            memcpy(pkt->data, buf, len);
+            pkt->stream_index = st->index;
+
+            return 0;
+        }
+    }
+
     if (rtp_parse_mp4_au(data, buf, len)) {
         av_log(ctx, AV_LOG_ERROR, "Error parsing AU headers\n");
         return -1;