From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 7A31B4B505 for ; Fri, 25 Apr 2025 10:42:25 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 394CF68813C; Fri, 25 Apr 2025 13:42:20 +0300 (EEST) Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DFF10687DC1 for ; Fri, 25 Apr 2025 13:42:13 +0300 (EEST) Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-22c3407a87aso32464745ad.3 for ; Fri, 25 Apr 2025 03:42:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1745577731; x=1746182531; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=gg02LnJNSAny6TF2AILPVDGmvZw3OBC/30AX9bWbQC8=; b=MS62YScTom/aS3aJT2gE+dhf6EBDLfsLBFs7AWLxo7dX/P8Pk9v8YL+W89hYpmKo55 rblTGEHvUBCRHmqzBeOJkB0bgUET9Y7Lw49uhp4QiFZnhLoe8Yo1Jfv5aAAtgIg0dfbP q3/V50zMBTxoaUeuyzr86knF8LkNLamY+StXwrpZPK2CKmPwzMNjLXLyMl1GSICjZ7rJ afF3v2WdH+AeOdpplmrTtijaQqhP7JpdWh1TK+DX/GyKPOk2Ic0yDxVISZkcO8q+rVkh uv43hKZjXhnzZxdg0ojfL2pzj/vSvTvIf6PJxciorkrHajMBVN7hWTbybM9z+vfrmnF8 njsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745577731; x=1746182531; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=gg02LnJNSAny6TF2AILPVDGmvZw3OBC/30AX9bWbQC8=; b=avIOHlkfX8/WpGI2Vx7DRHP4aWqkfUKi9cMdl5szJchVpxJMYyolcXg9GTcAbIyjRl e4Vqt8jLh65c9kMo7zFnotmB6P/aoBhcKo5aZuChy1awn6b3hIKFTR/cmNh+7mHh7G4u 5m+D4ElpY23/cPTxYRfZPrHEGXD6owOUEGMBkeW8tjc/C4NfZ9JeplHZmed8m+MZCJQM LIf52YcNWSn//4faI2tzBm3+FOAGj++EHxXcRQx7x8QLepT4cQ/svXsVa0is5zW8TYIo ywdfDuieK9ecO27nBCBhOL5tE6tBcR0MvCIAyJ0aOFLzsfIPLMLCBnr1v455wmzPPdHI ye+Q== X-Gm-Message-State: AOJu0YyKOeN81roSxuQBHCooehyOFqCt4Z0tnVeuZ4Iszlq+L24AlwLY zh6xSUvZLNmVuuKvsVLz8lyXz/PUDJp5aevD4LtqoS5MRurIJe6B1XNaJA== X-Gm-Gg: ASbGncuJ4rp8pXypvrFrEpoNw/Nzq9f2EIu/O5LIaYWUoQ4IzNx2XsuNgGvGRyHhSMk QGD5gyyRTlK54C4fYWWrerQ6zoLR4sDPP3Y3fH23e6qYHQff5UPC1xej9TX+UeexXgw+JjJHvd1 G3ndlIKs9yQhv3kbOBodLjJjSa1lPNwM3d1/Hyk6KGqiUdWnKDtfkr4kh9OP+8+j7nZECy7a7kn wW5Lr1Lup6QPCdzZYe63Mw7FUukLEm+uJLHGmz11b81Az/lNAF7Xg7wp9eAOIpvIAdN12p81Vi+ UFbsH/XHcY0X0R5qSMn1IVDH1pGEe0IKgejTDvCB7bZFIHVhV5b50/mTgeBl X-Google-Smtp-Source: AGHT+IEhwjSuisQmQZ9i3iUkh5SsYGXimnOeqLfZM7b4f9Tih1o29dKrVZtzn7JYAUvNj8VkoJk58Q== X-Received: by 2002:a17:902:fc46:b0:224:179a:3b8f with SMTP id d9443c01a7336-22dbf5fa5c1mr30411345ad.23.1745577731505; Fri, 25 Apr 2025 03:42:11 -0700 (PDT) Received: from cen.mioffice.cn ([43.224.245.246]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22db4dc7b7asm29139175ad.102.2025.04.25.03.42.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Apr 2025 03:42:11 -0700 (PDT) From: cenzhanquan2@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Fri, 25 Apr 2025 18:41:59 +0800 Message-Id: <20250425104159.1838684-1-cenzhanquan2@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/1] avcodec: Add A2DP LATM audio support. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: zhanquan cen Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: From: zhanquan cen 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 --- 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 +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-request@ffmpeg.org with subject "unsubscribe".