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 51A154DFF2 for ; Sun, 27 Apr 2025 07:08:42 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9CB2A68B206; Sun, 27 Apr 2025 10:08:37 +0300 (EEST) Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BAB37687BF9 for ; Sun, 27 Apr 2025 10:08:30 +0300 (EEST) Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-22438c356c8so41332415ad.1 for ; Sun, 27 Apr 2025 00:08:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1745737708; x=1746342508; 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=4zUc+yjJCvWfgZWq25tr9RsNKN2j3YuLXdxkHzDLhMw=; b=XemLHhaJ9A0r4jZDqyI8pfny3ropOQF4nds4Q4dR0gribeGQFMLt+QKzqo27KCWxgT wbv0su6Oo63l/3+g8icyERiUzYI+d/R/IKDrPfqrMEZaIBpg4mJY16Gq0soA/OaTb50m N9bnkaa52LQv49uczpR5zrXESUjimsiGm7S3Nzjl7NY1br7VxZ3Gf1cR2ekdk3qRa6MM zjWWfu0XoCE5GnCpiKSTgQGfdvU3mjkMjDipLFuOYCBSdoubKU9XjISfWiZdF86B8kjm Xwche8k7MNHXBXO5l3XLn0rCxlv6K/yzEP1RPqLrQs/PmivOab5XqfDdavrcEoDAgxIE 3law== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745737708; x=1746342508; 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=4zUc+yjJCvWfgZWq25tr9RsNKN2j3YuLXdxkHzDLhMw=; b=gOjV1+shsuwq8v2RzM06lgVPkyQ4Y39e5nMgwt8oO6kORVqu689HDJr1Z2ts4dWj6s vlFFXrJwBPsozTnu0KoqTX39Of0O+segXpabpBJHpbEHDdXWCLoDe7AxulbAgsiqDF4D kBH2WlUTBh9ATamMQFWwc2XzPDBF5hSWy2Phuee7kqt0fR9y11FuiO4eAB46e2MQT9EZ DDFhZIPyQ4wcoUzUKI/1uE2V8dgXtJMuL0cYsElK8oUaKo3VH566p3S+TYNAuFVsfRcB kRwZNVB+4HE8gYpeHdBM1Y4BtAgNLowLdakft2YLzXSBq4xH9q3Pfsn/QUKaSZldDf+4 oEVA== X-Gm-Message-State: AOJu0YwEwwU+RfL9ac5JXhvTq0Nuca9F4vu0V1744/Ki8FflJtnQyK+Y P6VbMUfaN0qN4wnhlj/CUaYcAfwd0X3ikv9Q3axLx9FExBpGg8N8kkpGnA== X-Gm-Gg: ASbGncvn5YdlAX+1kmWijCXBlFrHYaHaDfKXHRJMrzhrJYFqwR7XxyApnpQZAG0LG7I yx9gcnInLAt15Dcv9LZFPqZJ7HTaKG4OVt7FjhwLp0nARih2YplOniOYDco1paDf+j9yGbCGlxk 3C1cVD9UVWrD3Ddckd8tOYefs++tD8uol6fmeiRU4NGsPZgr1c2K9MG62QnNTgOe5uql0apMV/O WLJ1zyoP4CAE5BCX5MqewmMPP5F/9zjDtuDuJdJjqj53/S8enmh3f/ud6ICsCmxWjZWcQJWYmG1 8Pnaq2SV59T675AaGcYDD+DVPC6k7dIyR5xSwjIE2chlx8mMi5F31lQ5MYT3 X-Google-Smtp-Source: AGHT+IFmQxIKyvmQdvhRkSO3cTdIdmcj0sIk3CC3MOH5+8vxpfROmg1h82wbyEzI726zeJNT/UmGUg== X-Received: by 2002:a17:902:d4c3:b0:220:d79f:60f1 with SMTP id d9443c01a7336-22dbf62c270mr111705965ad.42.1745737707703; Sun, 27 Apr 2025 00:08:27 -0700 (PDT) Received: from cen.mioffice.cn ([43.224.245.246]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22db4dbd432sm59784395ad.90.2025.04.27.00.08.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Apr 2025 00:08:27 -0700 (PDT) From: cenzhanquan2@gmail.com To: ffmpeg-devel@ffmpeg.org Date: Sun, 27 Apr 2025 15:08:19 +0800 Message-Id: <20250427070819.182516-1-cenzhanquan2@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2] 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..f2a0e3a957 --- /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".