From: zhanquan cen <cenzhanqu...@gmail.com>

This commit introduces three key components for Bluetooth A2DP LATM streams:

1.A2DP-specific LATM decoder (aac_latm_a2dp)
  New codec ID AV_CODEC_ID_AAC_LATM_A2DP
  Inherits LATMContext with A2DP extensions
  Attaches "a2dp_rechunk" bitstream filter.

2.Parser enhancement
  Extend latm_parser to handle A2DP variant
  Support rechunking fragmented A2DP packets.

3.Bitstream filter implementation (a2dp_rechunk)
  Reassembles A2DP packets into valid LATM frames
  Handles dynamic MTU fragmentation (672 bytes typical)
  Supports codecs: SBC, AAC, AAC_LATM & AAC_LATM_A2DP
  This enables end-to-end processing of Bluetooth A2DP audio streams,
  including: packet rechunking -> LATM syntax parsing -> AAC decoding.

Signed-off-by: zhanquan cen <cenzhanqu...@gmail.com>
---
 libavcodec/Makefile            |   1 +
 libavcodec/a2dp_rechunk_bsf.c  | 153 +++++++++++++++++++++++++++++++++
 libavcodec/aac/aacdec_latm.h   |  26 +++++-
 libavcodec/allcodecs.c         |   1 +
 libavcodec/bitstream_filters.c |   1 +
 libavcodec/codec_desc.c        |   8 ++
 libavcodec/codec_id.h          |   1 +
 libavcodec/latm_parser.c       |   2 +-
 8 files changed, 191 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/a2dp_rechunk_bsf.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 7bd1dbec9a..e74d722669 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1266,6 +1266,7 @@ OBJS-$(CONFIG_HAPQA_EXTRACT_BSF)          += hap.o
 OBJS-$(CONFIG_HEVC_METADATA_BSF)          += h265_profile_level.o h2645data.o
 OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF)       += av1_parse.o
 OBJS-$(CONFIG_TRUEHD_CORE_BSF)            += mlp_parse.o mlp.o
+OBJS-$(CONFIG_A2DP_RECHUNK_BSF)           += a2dp_rechunk_bsf.o
 
 # thread libraries
 OBJS-$(HAVE_LIBC_MSVCRT)               += file_open.o
diff --git a/libavcodec/a2dp_rechunk_bsf.c b/libavcodec/a2dp_rechunk_bsf.c
new file mode 100644
index 0000000000..59c16f152a
--- /dev/null
+++ b/libavcodec/a2dp_rechunk_bsf.c
@@ -0,0 +1,153 @@
+/*
+* A2DP rechunk bitstream filter
+*
+* 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 "avcodec.h"
+#include "bsf_internal.h"
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include <libavcodec/avcodec.h>
+typedef struct A2DPContext {
+     AVCodecContext       *avctx;
+     AVCodecParserContext *parser;
+     AVPacket             *in_pkt;
+} A2DPContext;
+
+static int a2dp_rechunk_init(AVBSFContext *ctx)
+{
+     A2DPContext *s = ctx->priv_data;
+     const AVCodec *codec;
+     int ret;
+
+     s->in_pkt = av_packet_alloc();
+     if (!s->in_pkt)
+         return AVERROR(ENOMEM);
+
+     s->parser = av_parser_init(ctx->par_in->codec_id);
+     if (!s->parser) {
+         ret = AVERROR(EINVAL);
+         goto error;
+     }
+
+     /* find the audio decoder */
+     codec = avcodec_find_decoder(ctx->par_in->codec_id);
+     if (!codec) {
+         ret = AVERROR_DECODER_NOT_FOUND;
+         goto error;
+     }
+
+     s->avctx = avcodec_alloc_context3(codec);
+     if (!s->avctx) {
+         ret = AVERROR(ENOMEM);
+         goto error;
+     }
+
+     return 0;
+
+ error:
+     av_packet_free(&s->in_pkt);
+     av_parser_close(s->parser);
+     return ret;
+}
+
+static void a2dp_rechunk_uninit(AVBSFContext *ctx)
+{
+     A2DPContext *s = ctx->priv_data;
+
+     av_packet_free(&s->in_pkt);
+     av_parser_close(s->parser);
+     avcodec_free_context(&s->avctx);
+}
+
+static void a2dp_rechunk_flush(AVBSFContext *ctx)
+{
+     A2DPContext *s = ctx->priv_data;
+
+     av_packet_unref(s->in_pkt);
+}
+
+static int a2dp_rechunk_filter(AVBSFContext *ctx, AVPacket *pkt)
+{
+     A2DPContext *s = ctx->priv_data;
+     uint8_t *outbuf;
+     int outbuf_size;
+     int ret = 0;
+
+     while (1) {
+         if (!s->in_pkt->size) {
+             ret = ff_bsf_get_packet_ref(ctx, s->in_pkt);
+             if (ret < 0)
+                 break;
+         }
+
+         ret = av_parser_parse2(s->parser, s->avctx, &outbuf, &outbuf_size,
+                             s->in_pkt->data, s->in_pkt->size,
+                             AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
+         /* if parser header error, return error */
+         if (ret < 0) {
+             av_log(ctx, AV_LOG_ERROR, "[A2DP] Parser error: %d\n", ret);
+             break;
+         }
+
+         s->in_pkt->data += ret;
+         s->in_pkt->size -= ret;
+
+         /* if not found end, parser had cached remaining data, unref current 
pkt */
+         if (!outbuf_size || !outbuf) {
+             av_assert0(!s->in_pkt->size);
+             av_packet_unref(s->in_pkt);
+             continue;
+         }
+
+         /* note: outbuf_size >= ret */
+         /* get one packet from in_pkt or */
+         /* cache combine with part in_pkt data */
+         if (!s->in_pkt->size) {
+             if (outbuf_size == ret) {
+                av_packet_move_ref(pkt, s->in_pkt);
+             } else {
+                av_packet_unref(s->in_pkt);
+             }
+         }
+
+         pkt->data = outbuf;
+         pkt->size = outbuf_size;
+         break;
+     };
+
+     return ret;
+ }
+
+static const enum AVCodecID codec_ids[] = {
+     AV_CODEC_ID_SBC,
+     AV_CODEC_ID_AAC,
+     AV_CODEC_ID_AAC_LATM,
+     AV_CODEC_ID_AAC_LATM_A2DP,
+     AV_CODEC_ID_NONE,
+};
+
+const FFBitStreamFilter ff_a2dp_rechunk_bsf = {
+     .p.name         = "a2dp_rechunk",
+     .p.codec_ids    = codec_ids,
+     .priv_data_size = sizeof(A2DPContext),
+     .filter         = a2dp_rechunk_filter,
+     .init           = a2dp_rechunk_init,
+     .flush          = a2dp_rechunk_flush,
+     .close          = a2dp_rechunk_uninit,
+};
\ No newline at end of file
diff --git a/libavcodec/aac/aacdec_latm.h b/libavcodec/aac/aacdec_latm.h
index 398d40741b..b0aca3c331 100644
--- a/libavcodec/aac/aacdec_latm.h
+++ b/libavcodec/aac/aacdec_latm.h
@@ -347,4 +347,28 @@ const FFCodec ff_aac_latm_decoder = {
     .p.profiles      = NULL_IF_CONFIG_SMALL(ff_aac_profiles),
 };
 
-#endif /* AVCODEC_AAC_AACDEC_LATM_H */
+/*
+* Note: This decoder filter is intended to decode A2DP LATM streams 
transferred,
+* frame received from socket, need do rechunk process
+*/
+const FFCodec ff_aac_latm_a2dp_decoder = {
+    .p.name         = "aac_latm_a2dp",
+    CODEC_LONG_NAME("AAC LATM (Advanced Audio Coding LATM syntax) for A2DP"),
+    .p.type         = AVMEDIA_TYPE_AUDIO,
+    .p.id           = AV_CODEC_ID_AAC_LATM_A2DP,
+    .priv_data_size = sizeof(struct LATMContext),
+    .init           = latm_decode_init,
+    .close          = decode_close,
+    FF_CODEC_DECODE_CB(latm_decode_frame),
+    .p.sample_fmts  = (const enum AVSampleFormat[]) {
+        AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE
+    },
+    .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1,
+    .caps_internal  =  FF_CODEC_CAP_INIT_CLEANUP,
+    CODEC_CH_LAYOUTS_ARRAY(ff_aac_ch_layout),
+    .flush = flush,
+    .p.profiles     = NULL_IF_CONFIG_SMALL(ff_aac_profiles),
+    .bsfs           = "a2dp_rechunk",
+};
+
+#endif /* AVCODEC_AAC_AACDEC_LATM_H */
\ No newline at end of file
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index f10519617e..2cc2c87dc6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -427,6 +427,7 @@ extern const FFCodec ff_aac_encoder;
 extern const FFCodec ff_aac_decoder;
 extern const FFCodec ff_aac_fixed_decoder;
 extern const FFCodec ff_aac_latm_decoder;
+extern const FFCodec ff_aac_latm_a2dp_decoder;
 extern const FFCodec ff_ac3_encoder;
 extern const FFCodec ff_ac3_decoder;
 extern const FFCodec ff_ac3_fixed_encoder;
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index f923411bee..5d9d7a1ec3 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -24,6 +24,7 @@
 #include "bsf.h"
 #include "bsf_internal.h"
 
+extern const FFBitStreamFilter ff_a2dp_rechunk_bsf;
 extern const FFBitStreamFilter ff_aac_adtstoasc_bsf;
 extern const FFBitStreamFilter ff_av1_frame_merge_bsf;
 extern const FFBitStreamFilter ff_av1_frame_split_bsf;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 9fb190e35a..ca12f5cf64 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3466,6 +3466,14 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication 
Codec)"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_AAC_LATM_A2DP,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "aac_latm_a2dp",
+        .long_name = NULL_IF_CONFIG_SMALL("AAC LATM (Advanced Audio Coding 
LATM syntax) for A2DP"),
+        .props     = AV_CODEC_PROP_LOSSY,
+        .profiles  = NULL_IF_CONFIG_SMALL(ff_aac_profiles),
+    },
 
     /* subtitle codecs */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 2f6efe8261..dc566acc3a 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -552,6 +552,7 @@ enum AVCodecID {
     AV_CODEC_ID_OSQ,
     AV_CODEC_ID_QOA,
     AV_CODEC_ID_LC3,
+    AV_CODEC_ID_AAC_LATM_A2DP,
 
     /* subtitle codecs */
     AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at 
the start of subtitle codecs.
diff --git a/libavcodec/latm_parser.c b/libavcodec/latm_parser.c
index 8cc2024c4f..0af587115c 100644
--- a/libavcodec/latm_parser.c
+++ b/libavcodec/latm_parser.c
@@ -105,7 +105,7 @@ static int latm_parse(AVCodecParserContext *s1, 
AVCodecContext *avctx,
 }
 
 const AVCodecParser ff_aac_latm_parser = {
-    .codec_ids      = { AV_CODEC_ID_AAC_LATM },
+    .codec_ids      = { AV_CODEC_ID_AAC_LATM , AV_CODEC_ID_AAC_LATM_A2DP},
     .priv_data_size = sizeof(LATMParseContext),
     .parser_parse   = latm_parse,
     .parser_close   = ff_parse_close
-- 
2.34.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".

Reply via email to