* [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
@ 2024-03-26 16:47 Antoine Soulier via ffmpeg-devel
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
` (5 more replies)
0 siblings, 6 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
Signed-off-by: Antoine Soulier <asoulier@google.com>
Signed-off-by: Antoine SOULIER <asoulier@google.com>
---
configure | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/configure b/configure
index 343edb38ab..eb8ff81a11 100755
--- a/configure
+++ b/configure
@@ -244,6 +244,7 @@ External library support:
--enable-libjxl enable JPEG XL de/encoding via libjxl [no]
--enable-libklvanc enable Kernel Labs VANC processing [no]
--enable-libkvazaar enable HEVC encoding via libkvazaar [no]
+ --enable-liblc3 enable LC3 de/encoding via liblc3 [no]
--enable-liblensfun enable lensfun lens correction [no]
--enable-libmodplug enable ModPlug via libmodplug [no]
--enable-libmp3lame enable MP3 encoding via libmp3lame [no]
@@ -1926,6 +1927,7 @@ EXTERNAL_LIBRARY_LIST="
libjxl
libklvanc
libkvazaar
+ liblc3
libmodplug
libmp3lame
libmysofa
@@ -3501,6 +3503,10 @@ libilbc_encoder_deps="libilbc"
libjxl_decoder_deps="libjxl libjxl_threads"
libjxl_encoder_deps="libjxl libjxl_threads"
libkvazaar_encoder_deps="libkvazaar"
+liblc3_lc3_decoder_deps="liblc3"
+liblc3_lc3plus_decoder_deps="liblc3"
+liblc3_encoder_deps="liblc3"
+liblc3_encoder_select="audio_frame_queue"
libmodplug_demuxer_deps="libmodplug"
libmp3lame_encoder_deps="libmp3lame"
libmp3lame_encoder_select="audio_frame_queue mpegaudioheader"
@@ -6858,6 +6864,7 @@ enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/dec
require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner
enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc
enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 2.0.0" kvazaar.h kvz_api_get
+enabled liblc3 && require_pkg_config liblc3 "lc3 >= 1.1.0" lc3.h lc3_hr_setup_encoder
enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_create
if enabled libmfx && enabled libvpl; then
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
@ 2024-03-26 16:47 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:49 ` Stefano Sabatini
2024-03-26 18:27 ` Andreas Rheinhardt
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
` (4 subsequent siblings)
5 siblings, 2 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
The LC3 audio codec is the default codec of Bluetooth LE audio.
This is a wrapper over the liblc3 library (https://github.com/google/liblc3).
Signed-off-by: Antoine Soulier <asoulier@google.com>
Signed-off-by: Antoine SOULIER <asoulier@google.com>
---
libavcodec/Makefile | 3 +
libavcodec/allcodecs.c | 3 +
libavcodec/codec_desc.c | 14 +++
libavcodec/codec_id.h | 2 +
libavcodec/liblc3dec.c | 146 ++++++++++++++++++++++++++++++
libavcodec/liblc3enc.c | 191 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 359 insertions(+)
create mode 100644 libavcodec/liblc3dec.c
create mode 100644 libavcodec/liblc3enc.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 708434ac76..7d2cf3076d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1123,6 +1123,9 @@ OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o
OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o
OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o
OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o
+OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
+OBJS-$(CONFIG_LIBLC3_LC3_DECODER) += liblc3dec.o
+OBJS-$(CONFIG_LIBLC3_LC3PLUS_DECODER) += liblc3dec.o
OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2386b450a6..29aedaeac6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -776,6 +776,9 @@ extern const FFCodec ff_libilbc_encoder;
extern const FFCodec ff_libilbc_decoder;
extern const FFCodec ff_libjxl_decoder;
extern const FFCodec ff_libjxl_encoder;
+extern const FFCodec ff_liblc3_encoder;
+extern const FFCodec ff_liblc3_lc3_decoder;
+extern const FFCodec ff_liblc3_lc3plus_decoder;
extern const FFCodec ff_libmp3lame_encoder;
extern const FFCodec ff_libopencore_amrnb_encoder;
extern const FFCodec ff_libopencore_amrnb_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 3bab86db62..230bba2a09 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3425,6 +3425,20 @@ static const AVCodecDescriptor codec_descriptors[] = {
.long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"),
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
},
+ {
+ .id = AV_CODEC_ID_LC3,
+ .type = AVMEDIA_TYPE_AUDIO,
+ .name = "lc3",
+ .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_LC3_PLUS,
+ .type = AVMEDIA_TYPE_AUDIO,
+ .name = "lc3_plus",
+ .long_name = NULL_IF_CONFIG_SMALL("LC3plus (Low Complexity Communication Codec plus)"),
+ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+ },
/* subtitle codecs */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index c8dc21da74..7e4cb39049 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -543,6 +543,8 @@ enum AVCodecID {
AV_CODEC_ID_AC4,
AV_CODEC_ID_OSQ,
AV_CODEC_ID_QOA,
+ AV_CODEC_ID_LC3,
+ AV_CODEC_ID_LC3_PLUS,
/* subtitle codecs */
AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs.
diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
new file mode 100644
index 0000000000..e97cecc68f
--- /dev/null
+++ b/libavcodec/liblc3dec.c
@@ -0,0 +1,146 @@
+/*
+ * LC3 decoder wrapper
+ * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <lc3.h>
+
+#include "libavutil/intreadwrite.h"
+
+#include "avcodec.h"
+#include "codec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "internal.h"
+
+#define DECODER_MAX_CHANNELS 2
+
+typedef struct LibLC3DecContext {
+ int frame_us, srate_hz;
+ bool hr_mode;
+ lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
+} LibLC3DecContext;
+
+static av_cold int liblc3_decode_init(AVCodecContext *avctx)
+{
+ LibLC3DecContext *liblc3 = avctx->priv_data;
+ int channels = avctx->ch_layout.nb_channels;
+ unsigned decoder_size;
+
+ if (avctx->extradata_size < 2)
+ return AVERROR_INVALIDDATA;
+
+ liblc3->frame_us = AV_RL16(avctx->extradata + 0);
+ liblc3->srate_hz = avctx->sample_rate;
+ liblc3->hr_mode = avctx->extradata_size >= 6 &&
+ AV_RL16(avctx->extradata + 4);
+
+ av_log(avctx, AV_LOG_INFO,
+ "Decoding %.1f ms frames\n", liblc3->frame_us / 1e3f);
+ if (liblc3->hr_mode)
+ av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
+
+ decoder_size = lc3_hr_decoder_size(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
+ if (!decoder_size)
+ return AVERROR_INVALIDDATA;
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->decoder[ch] = lc3_hr_setup_decoder(liblc3->hr_mode,
+ liblc3->frame_us, liblc3->srate_hz, 0, av_malloc(decoder_size));
+ if (!liblc3->decoder[ch])
+ return AVERROR(ENOMEM);
+ }
+
+ avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
+ avctx->delay = lc3_hr_delay_samples(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
+ avctx->internal->skip_samples = avctx->delay;
+
+ return 0;
+}
+
+static av_cold int liblc3_decode_close(AVCodecContext *avctx)
+{
+ LibLC3DecContext *liblc3 = avctx->priv_data;
+ int channels = avctx->ch_layout.nb_channels;
+
+ for (int ch = 0; ch < channels; ch++)
+ av_free(liblc3->decoder[ch]);
+
+ return 0;
+}
+
+static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
+ int *got_frame_ptr, AVPacket *avpkt)
+{
+ LibLC3DecContext *liblc3 = avctx->priv_data;
+ int channels = avctx->ch_layout.nb_channels;
+ uint8_t *in = avpkt->data;
+ int block_bytes, ret;
+
+ frame->nb_samples = lc3_hr_frame_samples(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
+ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+ return ret;
+
+ block_bytes = avpkt->size;
+ for (int ch = 0; ch < channels; ch++) {
+ int frame_bytes = block_bytes / channels
+ + (ch < block_bytes % channels);
+
+
+ ret = lc3_decode(liblc3->decoder[ch], in, frame_bytes,
+ LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+
+ in += frame_bytes;
+ }
+
+ frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
+
+ *got_frame_ptr = 1;
+
+ return avpkt->size;
+}
+
+const FFCodec ff_liblc3_lc3_decoder = {
+ .p.name = "liblc3",
+ CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
+ .p.type = AVMEDIA_TYPE_AUDIO,
+ .p.id = AV_CODEC_ID_LC3,
+ .p.capabilities = AV_CODEC_CAP_DR1,
+ .p.wrapper_name = "liblc3",
+ .priv_data_size = sizeof(LibLC3DecContext),
+ .init = liblc3_decode_init,
+ .close = liblc3_decode_close,
+ FF_CODEC_DECODE_CB(liblc3_decode),
+};
+
+const FFCodec ff_liblc3_lc3plus_decoder = {
+ .p.name = "liblc3",
+ CODEC_LONG_NAME("LC3plus (Low Complexity Communication Codec plus)"),
+ .p.type = AVMEDIA_TYPE_AUDIO,
+ .p.id = AV_CODEC_ID_LC3_PLUS,
+ .p.capabilities = AV_CODEC_CAP_DR1,
+ .p.wrapper_name = "liblc3",
+ .priv_data_size = sizeof(LibLC3DecContext),
+ .init = liblc3_decode_init,
+ .close = liblc3_decode_close,
+ FF_CODEC_DECODE_CB(liblc3_decode),
+};
diff --git a/libavcodec/liblc3enc.c b/libavcodec/liblc3enc.c
new file mode 100644
index 0000000000..6495742c04
--- /dev/null
+++ b/libavcodec/liblc3enc.c
@@ -0,0 +1,191 @@
+/*
+ * LC3 encoder wrapper
+ * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <lc3.h>
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+
+#include "avcodec.h"
+#include "codec.h"
+#include "codec_internal.h"
+#include "encode.h"
+
+#define ENCODER_MAX_CHANNELS 2
+
+typedef struct LibLC3EncOpts {
+ float frame_duration;
+ bool hr_mode;
+} LibLC3EncOpts;
+
+typedef struct LibLC3EncContext {
+ AVClass *av_class;
+ LibLC3EncOpts opts;
+ int block_bytes;
+ lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
+} LibLC3EncContext;
+
+static av_cold int liblc3_encode_init(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+ bool hr_mode = liblc3->opts.hr_mode;
+ int frame_us = liblc3->opts.frame_duration * 1000;
+ int srate_hz = avctx->sample_rate;
+ int channels = avctx->ch_layout.nb_channels;
+ int effective_bit_rate;
+ unsigned encoder_size;
+
+ if (frame_us != 2500 && frame_us != 5000 &&
+ frame_us != 7500 && frame_us != 10000 ) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Unsupported frame duration %.1f ms\n", frame_us / 1e3f);
+ return AVERROR(EINVAL);
+ }
+
+ hr_mode |= srate_hz > 48000;
+ hr_mode &= srate_hz >= 48000;
+
+ if (frame_us <= 5000 || hr_mode)
+ avctx->codec_id = AV_CODEC_ID_LC3_PLUS;
+
+ if (frame_us == 7500 && hr_mode) {
+ av_log(avctx, AV_LOG_ERROR,
+ "High-reolution mode not supported with 7.5 ms frames\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames\n", frame_us / 1e3f);
+ if (hr_mode)
+ av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
+
+ liblc3->block_bytes = lc3_hr_frame_block_bytes(
+ hr_mode, frame_us, srate_hz, channels, avctx->bit_rate);
+
+ effective_bit_rate = lc3_hr_resolve_bitrate(
+ hr_mode, frame_us, srate_hz, liblc3->block_bytes);
+
+ if (avctx->bit_rate != effective_bit_rate)
+ av_log(avctx, AV_LOG_WARNING,
+ "Bitrate changed to %d bps\n", effective_bit_rate);
+ avctx->bit_rate = effective_bit_rate;
+
+ encoder_size = lc3_hr_encoder_size(frame_us, frame_us, srate_hz);
+ if (!encoder_size)
+ return AVERROR(EINVAL);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->encoder[ch] = lc3_hr_setup_encoder(
+ hr_mode, frame_us, srate_hz, 0, av_malloc(encoder_size));
+ if (!liblc3->encoder[ch])
+ return AVERROR(ENOMEM);
+ }
+
+ avctx->extradata = av_mallocz(6);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+
+ AV_WL16(avctx->extradata + 0, frame_us);
+ AV_WL16(avctx->extradata + 2, 0);
+ AV_WL16(avctx->extradata + 4, hr_mode);
+ avctx->extradata_size = 6;
+
+ avctx->frame_size = lc3_hr_frame_samples(hr_mode, frame_us, srate_hz);
+
+ return 0;
+}
+
+static av_cold int liblc3_encode_close(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+ int channels = avctx->ch_layout.nb_channels;
+
+ for (int ch = 0; ch < channels; ch++)
+ av_free(liblc3->encoder[ch]);
+
+ return 0;
+}
+
+static int liblc3_encode(AVCodecContext *avctx, AVPacket *avpkt,
+ const AVFrame *av_frame, int *got_packet_ptr)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+ int block_bytes = liblc3->block_bytes;
+ int channels = avctx->ch_layout.nb_channels;
+ uint8_t *data_ptr;
+ int ret;
+
+ if ((ret = ff_alloc_packet(avctx, avpkt, block_bytes)) < 0)
+ return ret;
+
+ data_ptr = avpkt->data;
+ for (int ch = 0; ch < channels; ch++) {
+ int frame_bytes = block_bytes / channels
+ + (ch < block_bytes % channels);
+
+ lc3_encode(liblc3->encoder[ch],
+ LC3_PCM_FORMAT_FLOAT, av_frame->data[ch], 1,
+ frame_bytes, data_ptr);
+
+ data_ptr += frame_bytes;
+ }
+
+ *got_packet_ptr = 1;
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(LibLC3EncContext, opts.x)
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "frame_duration", "Duration of a frame in milliseconds",
+ OFFSET(frame_duration), AV_OPT_TYPE_FLOAT,
+ { .dbl = 10.0 }, 2.5, 10.0, FLAGS },
+ { "high_resolution", "Enable High-Resolution mode (48 KHz or 96 KHz)",
+ OFFSET(hr_mode), AV_OPT_TYPE_BOOL,
+ { .i64 = 0 }, 0, 1, FLAGS },
+ { NULL }
+};
+
+static const AVClass class = {
+ .class_name = "liblc3 encoder",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_liblc3_encoder = {
+ .p.name = "liblc3",
+ CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
+ .p.type = AVMEDIA_TYPE_AUDIO,
+ .p.id = AV_CODEC_ID_LC3,
+ .p.capabilities = AV_CODEC_CAP_DR1,
+ .p.ch_layouts = (const AVChannelLayout[])
+ { { AV_CHANNEL_ORDER_UNSPEC, 1 },
+ { AV_CHANNEL_ORDER_UNSPEC, 2 }, { 0 } },
+ .p.supported_samplerates = (const int [])
+ { 96000, 48000, 32000, 24000, 16000, 8000, 0 },
+ .p.sample_fmts = (const enum AVSampleFormat[])
+ { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE },
+ .p.priv_class = &class,
+ .p.wrapper_name = "liblc3",
+ .priv_data_size = sizeof(LibLC3EncContext),
+ .init = liblc3_encode_init,
+ .close = liblc3_encode_close,
+ FF_CODEC_ENCODE_CB(liblc3_encode),
+};
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-03-26 16:47 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:59 ` Stefano Sabatini
2024-03-26 18:33 ` Andreas Rheinhardt
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
` (3 subsequent siblings)
5 siblings, 2 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
A file format is described in Bluetooth SIG LC3 and ETSI TS 103 634, for
test purpose. This is the format implemented here.
Signed-off-by: Antoine Soulier <asoulier@google.com>
Signed-off-by: Antoine SOULIER <asoulier@google.com>
---
libavformat/Makefile | 3 +
libavformat/allformats.c | 3 +
libavformat/lc3dec.c | 140 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 118 +++++++++++++++++++++++++++++++++
4 files changed, 264 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 94a949f555..29a38c1d94 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,9 @@ OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o
OBJS-$(CONFIG_KVAG_MUXER) += kvag.o rawenc.o
OBJS-$(CONFIG_LAF_DEMUXER) += lafdec.o
OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o
+OBJS-$(CONFIG_LC3_DEMUXER) += lc3dec.o
+OBJS-$(CONFIG_LC3_MUXER) += lc3enc.o
+OBJS-$(CONFIG_LC3_PLUS_MUXER) += lc3enc.o
OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o
OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o
OBJS-$(CONFIG_LUODAT_DEMUXER) += luodatdec.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index e15d0fa6d7..551b0f0d7b 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,9 @@ extern const FFInputFormat ff_kvag_demuxer;
extern const FFOutputFormat ff_kvag_muxer;
extern const FFInputFormat ff_laf_demuxer;
extern const FFOutputFormat ff_latm_muxer;
+extern const FFInputFormat ff_lc3_demuxer;
+extern const FFOutputFormat ff_lc3_muxer;
+extern const FFOutputFormat ff_lc3_plus_muxer;
extern const FFInputFormat ff_lmlm4_demuxer;
extern const FFInputFormat ff_loas_demuxer;
extern const FFInputFormat ff_luodat_demuxer;
diff --git a/libavformat/lc3dec.c b/libavformat/lc3dec.c
new file mode 100644
index 0000000000..563384f786
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,140 @@
+/*
+ * LC3 demuxer
+ * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Based on the file format specified by :
+ *
+ * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
+ * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
+ * 3.2.8.2 Reference LC3 Codec Bitstream Format
+ *
+ * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
+ * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
+ * LC3plus conformance script package
+ */
+
+#include <lc3.h>
+
+#include "libavcodec/avcodec.h"
+#include "libavcodec/packet.h"
+#include "libavutil/intreadwrite.h"
+
+#include "avformat.h"
+#include "avio.h"
+#include "demux.h"
+#include "internal.h"
+
+typedef struct LC3DemuxContext {
+ int frame_samples;
+ int64_t position;
+ int64_t length;
+} LC3DemuxContext;
+
+static int lc3_read_header(AVFormatContext *s)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVStream *st = NULL;
+ uint16_t tag, hdr_size;
+ uint16_t frame_us;
+ uint32_t length;
+ bool ep_mode, hr_mode;
+ int srate_hz, channels, bit_rate;
+ int num_extra_params, ret;
+
+ tag = avio_rb16(s->pb);
+ hdr_size = avio_rl16(s->pb);
+
+ if (tag != 0x1ccc || hdr_size < 9 * sizeof(uint16_t))
+ return AVERROR_INVALIDDATA;
+
+ num_extra_params = hdr_size / sizeof(uint16_t) - 9;
+
+ srate_hz = avio_rl16(s->pb) * 100;
+ bit_rate = avio_rl16(s->pb) * 100;
+ channels = avio_rl16(s->pb);
+ frame_us = avio_rl16(s->pb) * 10;
+ ep_mode = avio_rl16(s->pb) != 0;
+ length = avio_rl32(s->pb);
+ hr_mode = num_extra_params >= 1 && avio_rl16(s->pb);
+
+ st = avformat_new_stream(s, NULL);
+ if (!st)
+ return AVERROR(ENOMEM);
+
+ avpriv_set_pts_info(st, 32, 1, srate_hz);
+ st->duration = length;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = frame_us <= 5000 || ep_mode || hr_mode ?
+ AV_CODEC_ID_LC3_PLUS : AV_CODEC_ID_LC3;
+ st->codecpar->sample_rate = srate_hz;
+ st->codecpar->bit_rate = bit_rate;
+ st->codecpar->ch_layout.nb_channels = channels;
+
+ if ((ret = ff_alloc_extradata(st->codecpar, 6)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
+ return ret;
+ }
+
+ AV_WL16(st->codecpar->extradata + 0, frame_us);
+ AV_WL16(st->codecpar->extradata + 2, ep_mode);
+ AV_WL16(st->codecpar->extradata + 4, hr_mode);
+
+ lc3->frame_samples = lc3_hr_frame_samples(hr_mode, frame_us, srate_hz);
+ lc3->position = 0;
+ lc3->length = st->duration +
+ lc3_hr_delay_samples(hr_mode, frame_us, srate_hz);
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+
+ if ((ret = av_get_packet(s->pb, pkt, avio_rl16(pb))) < 0)
+ return ret;
+
+ pkt->duration = lc3->frame_samples;
+ lc3->position += lc3->frame_samples;
+ if (lc3->position > lc3->length) {
+ pkt->duration -= lc3->position - lc3->length;
+ lc3->position = lc3->length;
+ }
+
+ return 0;
+}
+
+const FFInputFormat ff_lc3_demuxer = {
+ .p.name = "lc3",
+ .p.long_name = NULL_IF_CONFIG_SMALL(
+ "LC3 / LC3plus (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_GENERIC_INDEX |
+ AVFMT_NOTIMESTAMPS |
+ AVFMT_NO_BYTE_SEEK,
+ .priv_data_size = sizeof(LC3DemuxContext),
+ .read_header = lc3_read_header,
+ .read_packet = lc3_read_packet,
+};
diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
new file mode 100644
index 0000000000..e6a9ddb35d
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,118 @@
+/*
+ * LC3 muxer
+ * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * Based on the file format specified by :
+ *
+ * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
+ * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
+ * 3.2.8.2 Reference LC3 Codec Bitstream Format
+ *
+ * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
+ * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
+ * LC3plus conformance script package
+ */
+
+#include <lc3.h>
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+
+#include "avformat.h"
+#include "avio.h"
+#include "mux.h"
+#include "internal.h"
+
+static av_cold int lc3_init(AVFormatContext *s)
+{
+ if (s->nb_streams != 1) {
+ av_log(s, AV_LOG_ERROR, "This muxer only supports a single stream.\n");
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
+}
+
+static int lc3_write_header(AVFormatContext *s)
+{
+ AVStream *st = s->streams[0];
+ int channels = st->codecpar->ch_layout.nb_channels;
+ int srate_hz = st->codecpar->sample_rate;
+ int bit_rate = st->codecpar->bit_rate;
+ uint16_t frame_us = 10000;
+ bool ep_mode = false, hr_mode = false;
+ uint32_t nb_samples = av_rescale_q(
+ st->duration, st->time_base, (AVRational){ 1, srate_hz });
+
+ if (st->codecpar->extradata_size >= 2)
+ frame_us = AV_RL16(st->codecpar->extradata + 0);
+ if (st->codecpar->extradata_size >= 4)
+ ep_mode = AV_RL16(st->codecpar->extradata + 2);
+ if (st->codecpar->extradata_size >= 6)
+ hr_mode = AV_RL16(st->codecpar->extradata + 4);
+
+ avio_wb16(s->pb, 0x1ccc);
+ avio_wl16(s->pb, (9 + hr_mode) * sizeof(uint16_t));
+ avio_wl16(s->pb, srate_hz / 100);
+ avio_wl16(s->pb, bit_rate / 100);
+ avio_wl16(s->pb, channels);
+ avio_wl16(s->pb, frame_us / 10);
+ avio_wl16(s->pb, ep_mode);
+ avio_wl32(s->pb, nb_samples);
+ if (hr_mode)
+ avio_wl16(s->pb, hr_mode);
+
+ return 0;
+}
+
+static int lc3_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ avio_wl16(s->pb, pkt->size);
+ avio_write(s->pb, pkt->data, pkt->size);
+ return 0;
+}
+
+const FFOutputFormat ff_lc3_muxer = {
+ .p.name = "lc3",
+ .p.long_name = NULL_IF_CONFIG_SMALL(
+ "LC3 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.audio_codec = AV_CODEC_ID_LC3,
+ .p.video_codec = AV_CODEC_ID_NONE,
+ .p.flags = AVFMT_NOTIMESTAMPS,
+ .init = lc3_init,
+ .write_header = lc3_write_header,
+ .write_packet = lc3_write_packet,
+};
+
+const FFOutputFormat ff_lc3_plus_muxer = {
+ .p.name = "lc3_plus",
+ .p.long_name = NULL_IF_CONFIG_SMALL(
+ "LC3Plus (Low Complexity Communication Codec plus)"),
+ .p.extensions = "lc3",
+ .p.audio_codec = AV_CODEC_ID_LC3_PLUS,
+ .p.video_codec = AV_CODEC_ID_NONE,
+ .p.flags = AVFMT_NOTIMESTAMPS,
+ .init = lc3_init,
+ .write_header = lc3_write_header,
+ .write_packet = lc3_write_packet,
+};
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-03-26 16:47 ` Antoine Soulier via ffmpeg-devel
2024-03-26 18:17 ` Stefano Sabatini
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support Antoine Soulier via ffmpeg-devel
` (2 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
Signed-off-by: Antoine Soulier <asoulier@google.com>
Signed-off-by: Antoine SOULIER <asoulier@google.com>
---
doc/encoders.texi | 57 +++++++++++++++++++++++++++++++++++++++
doc/general_contents.texi | 12 ++++++++-
doc/muxers.texi | 4 +++
3 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..0719ba13cc 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -814,6 +814,63 @@ ffmpeg -i input.wav -c:a libfdk_aac -profile:a aac_he -b:a 64k output.m4a
@end example
@end itemize
+@anchor{liblc3-enc}
+@section liblc3
+
+liblc3 LC3 (Low Complexity Communication Codec) encoder wrapper.
+
+Requires the presence of the liblc3 headers and library during configuration.
+You need to explicitly configure the build with @code{--enable-liblc3}.
+
+This encoder has support for the Bluetooth SIG LC3 codec for the LE Audio
+protocol, and the following features of LC3plus:
+@itemize @minus
+@item
+Frame duration of 2.5 and 5ms.
+@item
+High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
+@end itemize
+
+For more information see the liblc3 project at
+@url{https://github.com/google/liblc3}.
+
+@subsection Options
+
+The following options are mapped on the shared FFmpeg codec options.
+
+@table @option
+@item b
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar
+Set the audio sampling rate (in Hz).
+
+@item channels
+Set the number of audio channels.
+
+@item frame_duration
+Set the audio frame duration in milliseconds. Default value is 10ms.
+Allowed frame durations are 2.5ms, 5ms, 7.5ms and 10ms.
+LC3 (Bluetooth LE Audio), allows 7.5ms and 10ms; and LC3plus 2.5ms, 5ms
+and 10ms.
+
+The 10ms frame duration is available in LC3 and LC3 plus standard.
+In this mode, the produced bitstream can be referenced either as LC3 or LC3plus.
+
+@item high_resolution
+Enable the high-resolution mode if set to 1. The high-resolution mode is
+available with all LC3plus frame durations and for a sampling rate 48 KHz,
+and 96 KHz.
+
+The encoder automatically turns off this mode at lower sampling rates and
+activates it at 96 KHz.
+
+This mode should be preferred at high bitrates. In this mode, the audio
+bandwidth is always up to the Nyquist frequency, compared to LC3 at 48 KHz,
+which limits the bandwidth to 20 KHz.
+@end table
+
@anchor{libmp3lame}
@section libmp3lame
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index f269cbd1a9..9e340fce7b 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -237,6 +237,14 @@ Go to @url{http://sourceforge.net/projects/opencore-amr/} and follow the
instructions for installing the library.
Then pass @code{--enable-libfdk-aac} to configure to enable it.
+@subsection LC3 library
+
+FFmpeg can make use of the Google LC3 library for LC3 decoding & encoding.
+
+Go to @url{https://github.com/google/liblc3/} and follow the instructions for
+installing the library.
+Then pass @code{--enable-liblc3} to configure to enable it.
+
@section OpenH264
FFmpeg can make use of the OpenH264 library for H.264 decoding and encoding.
@@ -1300,7 +1308,9 @@ following image formats are supported:
@tab encoding and decoding supported through external library libilbc
@item IMC (Intel Music Coder) @tab @tab X
@item Interplay ACM @tab @tab X
-@item MACE (Macintosh Audio Compression/Expansion) 3:1 @tab @tab X
+@item LC3 @tab E @tab E
+ @tab supported through external library liblc3
+@item LC3plus @tab E @tab E
@item MACE (Macintosh Audio Compression/Expansion) 6:1 @tab @tab X
@item Marian's A-pac audio @tab @tab X
@item MI-SC4 (Micronas SC-4 Audio) @tab @tab X
diff --git a/doc/muxers.texi b/doc/muxers.texi
index d9dd72e613..43dabe4499 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -132,6 +132,10 @@ to Annex B syntax if it's in length-prefixed mode.
ITU-T H.265 / MPEG-H Part 2 HEVC video. Bitstream shall be converted
to Annex B syntax if it's in length-prefixed mode.
+@item lc3 @emph{audio} (lc3)
+Bluetooth SIG Low Complexity Communication Codec audio.
+ETSI TS 103 634 Low Complexity Communication Codec plus (LC3plus).
+
@item m4v @emph{video}
MPEG-4 Part 2 video
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
` (2 preceding siblings ...)
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
@ 2024-03-26 16:47 ` Antoine Soulier via ffmpeg-devel
2024-03-26 18:01 ` Stefano Sabatini
2024-03-26 16:58 ` [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Paul B Mahol
2024-03-26 17:31 ` Stefano Sabatini
5 siblings, 1 reply; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 16:47 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
Signed-off-by: Antoine Soulier <asoulier@google.com>
Signed-off-by: Antoine SOULIER <asoulier@google.com>
---
Changelog | 1 +
1 file changed, 1 insertion(+)
diff --git a/Changelog b/Changelog
index c6e8f6bcaf..fb08ee2f81 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
+- LC3/LC3plus decoding/encoding using external library liblc3
- DXV DXT1 encoder
- LEAD MCMP decoder
- EVC decoding using external library libxevd
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
` (3 preceding siblings ...)
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support Antoine Soulier via ffmpeg-devel
@ 2024-03-26 16:58 ` Paul B Mahol
2024-03-26 17:07 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:31 ` Stefano Sabatini
5 siblings, 1 reply; 22+ messages in thread
From: Paul B Mahol @ 2024-03-26 16:58 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
Isn't this using sub-optimal MDCT implementation?
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 16:58 ` [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Paul B Mahol
@ 2024-03-26 17:07 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:44 ` Paul B Mahol
0 siblings, 1 reply; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 17:07 UTC (permalink / raw)
To: Paul B Mahol; +Cc: Antoine Soulier, FFmpeg development discussions and patches
What do you mean by sub-optimal?
It's stacked by prime factors, and unrolled for FFT3 and FF5.
The butterfly implementations of FFT3 and FF5, gives me slightly slower
computation. FFT5 is done first, so it takes advantage of sin()/cos()
values of 0 or 1.
There are also no reordering steps (this stage is completely removed), but
cannot run in-place.
Benchmarks I made show that it runs slightly faster.
On Tue, Mar 26, 2024 at 9:59 AM Paul B Mahol <onemda@gmail.com> wrote:
>
> Isn't this using sub-optimal MDCT implementation?
>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
` (4 preceding siblings ...)
2024-03-26 16:58 ` [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Paul B Mahol
@ 2024-03-26 17:31 ` Stefano Sabatini
2024-03-26 17:35 ` Antoine Soulier via ffmpeg-devel
5 siblings, 1 reply; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-26 17:31 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 16:47:35 +0000, ffmpeg-devel Mailing List wrote:
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
why the double sign-off?
[...]
LGTM.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 17:31 ` Stefano Sabatini
@ 2024-03-26 17:35 ` Antoine Soulier via ffmpeg-devel
0 siblings, 0 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 17:35 UTC (permalink / raw)
To: FFmpeg development discussions and patches, Antoine Soulier
Cc: Antoine Soulier
Arf, sorry for that. I used `git send-email -s`, perhaps it's the source of
the double signed-off.
On Tue, Mar 26, 2024 at 10:32 AM Stefano Sabatini <stefasab@gmail.com>
wrote:
> On date Tuesday 2024-03-26 16:47:35 +0000, ffmpeg-devel Mailing List wrote:
>
> > Signed-off-by: Antoine Soulier <asoulier@google.com>
> > Signed-off-by: Antoine SOULIER <asoulier@google.com>
>
> why the double sign-off?
>
> [...]
>
> LGTM.
>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 17:07 ` Antoine Soulier via ffmpeg-devel
@ 2024-03-26 17:44 ` Paul B Mahol
2024-03-26 17:57 ` Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 22+ messages in thread
From: Paul B Mahol @ 2024-03-26 17:44 UTC (permalink / raw)
To: Antoine Soulier; +Cc: FFmpeg development discussions and patches
On Tue, Mar 26, 2024 at 6:07 PM Antoine Soulier <asoulier@google.com> wrote:
> What do you mean by sub-optimal?
> It's stacked by prime factors, and unrolled for FFT3 and FF5.
> The butterfly implementations of FFT3 and FF5, gives me slightly slower
> computation. FFT5 is done first, so it takes advantage of sin()/cos()
> values of 0 or 1.
> There are also no reordering steps (this stage is completely removed), but
> cannot run in-place.
> Benchmarks I made show that it runs slightly faster.
>
Compared with what?
Where is at least x86 SIMD for that MDCT?
>
> On Tue, Mar 26, 2024 at 9:59 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>>
>> Isn't this using sub-optimal MDCT implementation?
>>
>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-03-26 17:49 ` Stefano Sabatini
2024-03-26 18:50 ` Antoine Soulier via ffmpeg-devel
2024-03-26 18:27 ` Andreas Rheinhardt
1 sibling, 1 reply; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-26 17:49 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 16:47:36 +0000, ffmpeg-devel Mailing List wrote:
> The LC3 audio codec is the default codec of Bluetooth LE audio.
> This is a wrapper over the liblc3 library (https://github.com/google/liblc3).
>
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> libavcodec/Makefile | 3 +
> libavcodec/allcodecs.c | 3 +
> libavcodec/codec_desc.c | 14 +++
> libavcodec/codec_id.h | 2 +
> libavcodec/liblc3dec.c | 146 ++++++++++++++++++++++++++++++
> libavcodec/liblc3enc.c | 191 ++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 359 insertions(+)
> create mode 100644 libavcodec/liblc3dec.c
> create mode 100644 libavcodec/liblc3enc.c
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 708434ac76..7d2cf3076d 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -1123,6 +1123,9 @@ OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o
> OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o
> OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o
> OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o
> +OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
> +OBJS-$(CONFIG_LIBLC3_LC3_DECODER) += liblc3dec.o
> +OBJS-$(CONFIG_LIBLC3_LC3PLUS_DECODER) += liblc3dec.o
> OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
> OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
> OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2386b450a6..29aedaeac6 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -776,6 +776,9 @@ extern const FFCodec ff_libilbc_encoder;
> extern const FFCodec ff_libilbc_decoder;
> extern const FFCodec ff_libjxl_decoder;
> extern const FFCodec ff_libjxl_encoder;
> +extern const FFCodec ff_liblc3_encoder;
> +extern const FFCodec ff_liblc3_lc3_decoder;
> +extern const FFCodec ff_liblc3_lc3plus_decoder;
> extern const FFCodec ff_libmp3lame_encoder;
> extern const FFCodec ff_libopencore_amrnb_encoder;
> extern const FFCodec ff_libopencore_amrnb_decoder;
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 3bab86db62..230bba2a09 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3425,6 +3425,20 @@ static const AVCodecDescriptor codec_descriptors[] = {
> .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"),
> .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> },
> + {
> + .id = AV_CODEC_ID_LC3,
> + .type = AVMEDIA_TYPE_AUDIO,
> + .name = "lc3",
> + .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_LC3_PLUS,
> + .type = AVMEDIA_TYPE_AUDIO,
> + .name = "lc3_plus",
> + .long_name = NULL_IF_CONFIG_SMALL("LC3plus (Low Complexity Communication Codec plus)"),
> + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> + },
>
> /* subtitle codecs */
> {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index c8dc21da74..7e4cb39049 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -543,6 +543,8 @@ enum AVCodecID {
> AV_CODEC_ID_AC4,
> AV_CODEC_ID_OSQ,
> AV_CODEC_ID_QOA,
> + AV_CODEC_ID_LC3,
> + AV_CODEC_ID_LC3_PLUS,
>
> /* subtitle codecs */
> AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs.
> diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
> new file mode 100644
> index 0000000000..e97cecc68f
> --- /dev/null
> +++ b/libavcodec/liblc3dec.c
> @@ -0,0 +1,146 @@
> +/*
> + * LC3 decoder wrapper
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavutil/intreadwrite.h"
> +
> +#include "avcodec.h"
> +#include "codec.h"
> +#include "codec_internal.h"
> +#include "decode.h"
> +#include "internal.h"
> +
> +#define DECODER_MAX_CHANNELS 2
> +
> +typedef struct LibLC3DecContext {
> + int frame_us, srate_hz;
> + bool hr_mode;
> + lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
> +} LibLC3DecContext;
> +
> +static av_cold int liblc3_decode_init(AVCodecContext *avctx)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> + unsigned decoder_size;
> +
> + if (avctx->extradata_size < 2)
> + return AVERROR_INVALIDDATA;
> +
> + liblc3->frame_us = AV_RL16(avctx->extradata + 0);
> + liblc3->srate_hz = avctx->sample_rate;
> + liblc3->hr_mode = avctx->extradata_size >= 6 &&
> + AV_RL16(avctx->extradata + 4);
> +
> + av_log(avctx, AV_LOG_INFO,
> + "Decoding %.1f ms frames\n", liblc3->frame_us / 1e3f);
1e3f might be replaced by a symbolic constant/macro
> + if (liblc3->hr_mode)
> + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> +
> + decoder_size = lc3_hr_decoder_size(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + if (!decoder_size)
> + return AVERROR_INVALIDDATA;
> +
> + for (int ch = 0; ch < channels; ch++) {
> + liblc3->decoder[ch] = lc3_hr_setup_decoder(liblc3->hr_mode,
> + liblc3->frame_us, liblc3->srate_hz, 0, av_malloc(decoder_size));
> + if (!liblc3->decoder[ch])
> + return AVERROR(ENOMEM);
> + }
> +
> + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
> + avctx->delay = lc3_hr_delay_samples(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + avctx->internal->skip_samples = avctx->delay;
> +
> + return 0;
> +}
> +
> +static av_cold int liblc3_decode_close(AVCodecContext *avctx)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> +
> + for (int ch = 0; ch < channels; ch++)
> + av_free(liblc3->decoder[ch]);
> +
> + return 0;
> +}
> +
> +static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
> + int *got_frame_ptr, AVPacket *avpkt)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> + uint8_t *in = avpkt->data;
> + int block_bytes, ret;
> +
> + frame->nb_samples = lc3_hr_frame_samples(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> + return ret;
> +
> + block_bytes = avpkt->size;
> + for (int ch = 0; ch < channels; ch++) {
> + int frame_bytes = block_bytes / channels
> + + (ch < block_bytes % channels);
> +
> +
> + ret = lc3_decode(liblc3->decoder[ch], in, frame_bytes,
> + LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
> + if (ret < 0)
> + return AVERROR_INVALIDDATA;
> +
> + in += frame_bytes;
> + }
> +
> + frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
> +
> + *got_frame_ptr = 1;
> +
> + return avpkt->size;
> +}
> +
> +const FFCodec ff_liblc3_lc3_decoder = {
> + .p.name = "liblc3",
> + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> + .p.type = AVMEDIA_TYPE_AUDIO,
> + .p.id = AV_CODEC_ID_LC3,
> + .p.capabilities = AV_CODEC_CAP_DR1,
> + .p.wrapper_name = "liblc3",
> + .priv_data_size = sizeof(LibLC3DecContext),
> + .init = liblc3_decode_init,
> + .close = liblc3_decode_close,
> + FF_CODEC_DECODE_CB(liblc3_decode),
> +};
> +
> +const FFCodec ff_liblc3_lc3plus_decoder = {
> + .p.name = "liblc3",
> + CODEC_LONG_NAME("LC3plus (Low Complexity Communication Codec plus)"),
> + .p.type = AVMEDIA_TYPE_AUDIO,
> + .p.id = AV_CODEC_ID_LC3_PLUS,
> + .p.capabilities = AV_CODEC_CAP_DR1,
> + .p.wrapper_name = "liblc3",
> + .priv_data_size = sizeof(LibLC3DecContext),
> + .init = liblc3_decode_init,
> + .close = liblc3_decode_close,
> + FF_CODEC_DECODE_CB(liblc3_decode),
> +};
> diff --git a/libavcodec/liblc3enc.c b/libavcodec/liblc3enc.c
> new file mode 100644
> index 0000000000..6495742c04
> --- /dev/null
> +++ b/libavcodec/liblc3enc.c
> @@ -0,0 +1,191 @@
> +/*
> + * LC3 encoder wrapper
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +
> +#include "avcodec.h"
> +#include "codec.h"
> +#include "codec_internal.h"
> +#include "encode.h"
> +
> +#define ENCODER_MAX_CHANNELS 2
> +
> +typedef struct LibLC3EncOpts {
> + float frame_duration;
> + bool hr_mode;
> +} LibLC3EncOpts;
> +
> +typedef struct LibLC3EncContext {
> + AVClass *av_class;
> + LibLC3EncOpts opts;
> + int block_bytes;
> + lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
> +} LibLC3EncContext;
> +
> +static av_cold int liblc3_encode_init(AVCodecContext *avctx)
> +{
> + LibLC3EncContext *liblc3 = avctx->priv_data;
> + bool hr_mode = liblc3->opts.hr_mode;
> + int frame_us = liblc3->opts.frame_duration * 1000;
> + int srate_hz = avctx->sample_rate;
> + int channels = avctx->ch_layout.nb_channels;
> + int effective_bit_rate;
> + unsigned encoder_size;
> +
> + if (frame_us != 2500 && frame_us != 5000 &&
> + frame_us != 7500 && frame_us != 10000 ) {
> + av_log(avctx, AV_LOG_ERROR,
> + "Unsupported frame duration %.1f ms\n", frame_us / 1e3f);
> + return AVERROR(EINVAL);
> + }
> +
> + hr_mode |= srate_hz > 48000;
> + hr_mode &= srate_hz >= 48000;
> +
> + if (frame_us <= 5000 || hr_mode)
> + avctx->codec_id = AV_CODEC_ID_LC3_PLUS;
> +
> + if (frame_us == 7500 && hr_mode) {
> + av_log(avctx, AV_LOG_ERROR,
> + "High-reolution mode not supported with 7.5 ms frames\n");
typo
> + return AVERROR(EINVAL);
> + }
> +
> + av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames\n", frame_us / 1e3f);
> + if (hr_mode)
> + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> +
> + liblc3->block_bytes = lc3_hr_frame_block_bytes(
> + hr_mode, frame_us, srate_hz, channels, avctx->bit_rate);
> +
> + effective_bit_rate = lc3_hr_resolve_bitrate(
> + hr_mode, frame_us, srate_hz, liblc3->block_bytes);
> +
> + if (avctx->bit_rate != effective_bit_rate)
> + av_log(avctx, AV_LOG_WARNING,
> + "Bitrate changed to %d bps\n", effective_bit_rate);
> + avctx->bit_rate = effective_bit_rate;
> +
> + encoder_size = lc3_hr_encoder_size(frame_us, frame_us, srate_hz);
> + if (!encoder_size)
> + return AVERROR(EINVAL);
> +
> + for (int ch = 0; ch < channels; ch++) {
> + liblc3->encoder[ch] = lc3_hr_setup_encoder(
> + hr_mode, frame_us, srate_hz, 0, av_malloc(encoder_size));
not sure if this can fail for different reasons (e.g. if it performs
validation), if this is the case maybe you want to add an explicit
check for malloc/ENOMEM
[...]
No more comments from me, thanks.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper
2024-03-26 17:44 ` Paul B Mahol
@ 2024-03-26 17:57 ` Antoine Soulier via ffmpeg-devel
0 siblings, 0 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 17:57 UTC (permalink / raw)
To: Paul B Mahol; +Cc: Antoine Soulier, FFmpeg development discussions and patches
Compared with the C implementation of KissFFT (it's the only one I tested
on ARM M4).
Yes, there is no SIMD on x86. This was not the main target.
Was mainly made for ARM M4 (for BLE devices Nordic Semi / Zephyr), and ARM
Neon (Android).
By the way, this does not change a lot, the FFT/MDCT on powerful CPU's is
marginal compared to the read/write of the bitstream arithmetically coded.
We can perhaps connect the FFMpeg implementation, but it will probably miss
2 things:
- Some transformations are not a multiple of 15, but only 5 * 2^n. I guess
FFmpeg only has a base 15 implementation.
- It uses asymmetric windowing, to reduce algorithmic delay. Some
coefficients are zeroed. Not important, but will need a larger coefficients
table, and a bunch of multiplication by 0, without a specific
implementation.
So I think it will need some work.
On Tue, Mar 26, 2024 at 10:45 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>
> On Tue, Mar 26, 2024 at 6:07 PM Antoine Soulier <asoulier@google.com>
> wrote:
>
>> What do you mean by sub-optimal?
>> It's stacked by prime factors, and unrolled for FFT3 and FF5.
>> The butterfly implementations of FFT3 and FF5, gives me slightly slower
>> computation. FFT5 is done first, so it takes advantage of sin()/cos()
>> values of 0 or 1.
>> There are also no reordering steps (this stage is completely removed),
>> but cannot run in-place.
>> Benchmarks I made show that it runs slightly faster.
>>
>
> Compared with what?
> Where is at least x86 SIMD for that MDCT?
>
>
>>
>> On Tue, Mar 26, 2024 at 9:59 AM Paul B Mahol <onemda@gmail.com> wrote:
>>
>>>
>>> Isn't this using sub-optimal MDCT implementation?
>>>
>>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-03-26 17:59 ` Stefano Sabatini
2024-03-26 18:33 ` Andreas Rheinhardt
1 sibling, 0 replies; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-26 17:59 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 16:47:37 +0000, ffmpeg-devel Mailing List wrote:
> A file format is described in Bluetooth SIG LC3 and ETSI TS 103 634, for
> test purpose. This is the format implemented here.
>
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> libavformat/Makefile | 3 +
> libavformat/allformats.c | 3 +
> libavformat/lc3dec.c | 140 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 118 +++++++++++++++++++++++++++++++++
> 4 files changed, 264 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 94a949f555..29a38c1d94 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,9 @@ OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o
> OBJS-$(CONFIG_KVAG_MUXER) += kvag.o rawenc.o
> OBJS-$(CONFIG_LAF_DEMUXER) += lafdec.o
> OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o
> +OBJS-$(CONFIG_LC3_DEMUXER) += lc3dec.o
> +OBJS-$(CONFIG_LC3_MUXER) += lc3enc.o
> +OBJS-$(CONFIG_LC3_PLUS_MUXER) += lc3enc.o
> OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o
> OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o
> OBJS-$(CONFIG_LUODAT_DEMUXER) += luodatdec.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index e15d0fa6d7..551b0f0d7b 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,9 @@ extern const FFInputFormat ff_kvag_demuxer;
> extern const FFOutputFormat ff_kvag_muxer;
> extern const FFInputFormat ff_laf_demuxer;
> extern const FFOutputFormat ff_latm_muxer;
> +extern const FFInputFormat ff_lc3_demuxer;
> +extern const FFOutputFormat ff_lc3_muxer;
> +extern const FFOutputFormat ff_lc3_plus_muxer;
> extern const FFInputFormat ff_lmlm4_demuxer;
> extern const FFInputFormat ff_loas_demuxer;
> extern const FFInputFormat ff_luodat_demuxer;
> diff --git a/libavformat/lc3dec.c b/libavformat/lc3dec.c
> new file mode 100644
> index 0000000000..563384f786
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,140 @@
> +/*
> + * LC3 demuxer
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * Based on the file format specified by :
> + *
> + * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
> + * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
> + * 3.2.8.2 Reference LC3 Codec Bitstream Format
> + *
> + * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
> + * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
> + * LC3plus conformance script package
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/packet.h"
> +#include "libavutil/intreadwrite.h"
> +
> +#include "avformat.h"
> +#include "avio.h"
> +#include "demux.h"
> +#include "internal.h"
> +
> +typedef struct LC3DemuxContext {
> + int frame_samples;
> + int64_t position;
> + int64_t length;
> +} LC3DemuxContext;
> +
> +static int lc3_read_header(AVFormatContext *s)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVStream *st = NULL;
> + uint16_t tag, hdr_size;
> + uint16_t frame_us;
> + uint32_t length;
> + bool ep_mode, hr_mode;
I don't think we use bool anywhere
> + int srate_hz, channels, bit_rate;
> + int num_extra_params, ret;
> +
> + tag = avio_rb16(s->pb);
> + hdr_size = avio_rl16(s->pb);
> +
> + if (tag != 0x1ccc || hdr_size < 9 * sizeof(uint16_t))
> + return AVERROR_INVALIDDATA;
> +
> + num_extra_params = hdr_size / sizeof(uint16_t) - 9;
> +
> + srate_hz = avio_rl16(s->pb) * 100;
> + bit_rate = avio_rl16(s->pb) * 100;
> + channels = avio_rl16(s->pb);
> + frame_us = avio_rl16(s->pb) * 10;
> + ep_mode = avio_rl16(s->pb) != 0;
> + length = avio_rl32(s->pb);
> + hr_mode = num_extra_params >= 1 && avio_rl16(s->pb);
> +
> + st = avformat_new_stream(s, NULL);
> + if (!st)
> + return AVERROR(ENOMEM);
> +
> + avpriv_set_pts_info(st, 32, 1, srate_hz);
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = frame_us <= 5000 || ep_mode || hr_mode ?
> + AV_CODEC_ID_LC3_PLUS : AV_CODEC_ID_LC3;
> + st->codecpar->sample_rate = srate_hz;
> + st->codecpar->bit_rate = bit_rate;
> + st->codecpar->ch_layout.nb_channels = channels;
> +
> + if ((ret = ff_alloc_extradata(st->codecpar, 6)) < 0) {
> + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
> + return ret;
> + }
> +
> + AV_WL16(st->codecpar->extradata + 0, frame_us);
> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> +
> + lc3->frame_samples = lc3_hr_frame_samples(hr_mode, frame_us, srate_hz);
> + lc3->position = 0;
> + lc3->length = st->duration +
> + lc3_hr_delay_samples(hr_mode, frame_us, srate_hz);
do you know of some way to avoid the lib dependency for the demuxer?
Usually we try to avoid the muxer/demuxer depends on the binary (which
it cannot be obviously avoided in the case of the codec).
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int ret;
> +
> + if ((ret = av_get_packet(s->pb, pkt, avio_rl16(pb))) < 0)
> + return ret;
> +
> + pkt->duration = lc3->frame_samples;
> + lc3->position += lc3->frame_samples;
> + if (lc3->position > lc3->length) {
> + pkt->duration -= lc3->position - lc3->length;
> + lc3->position = lc3->length;
> + }
> +
> + return 0;
> +}
> +
> +const FFInputFormat ff_lc3_demuxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL(
> + "LC3 / LC3plus (Low Complexity Communication Codec)"),
nit++: weird indent, use a single line
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_GENERIC_INDEX |
> + AVFMT_NOTIMESTAMPS |
> + AVFMT_NO_BYTE_SEEK,
> + .priv_data_size = sizeof(LC3DemuxContext),
> + .read_header = lc3_read_header,
> + .read_packet = lc3_read_packet,
> +};
> diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
> new file mode 100644
> index 0000000000..e6a9ddb35d
> --- /dev/null
> +++ b/libavformat/lc3enc.c
> @@ -0,0 +1,118 @@
> +/*
> + * LC3 muxer
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * Based on the file format specified by :
> + *
> + * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
> + * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
> + * 3.2.8.2 Reference LC3 Codec Bitstream Format
> + *
> + * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
> + * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
> + * LC3plus conformance script package
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +
> +#include "avformat.h"
> +#include "avio.h"
> +#include "mux.h"
> +#include "internal.h"
> +
> +static av_cold int lc3_init(AVFormatContext *s)
> +{
> + if (s->nb_streams != 1) {
> + av_log(s, AV_LOG_ERROR, "This muxer only supports a single stream.\n");
> + return AVERROR(EINVAL);
> + }
probably you also need to check the codec ID
> +
> + return 0;
> +}
> +
> +static int lc3_write_header(AVFormatContext *s)
> +{
> + AVStream *st = s->streams[0];
> + int channels = st->codecpar->ch_layout.nb_channels;
> + int srate_hz = st->codecpar->sample_rate;
> + int bit_rate = st->codecpar->bit_rate;
> + uint16_t frame_us = 10000;
> + bool ep_mode = false, hr_mode = false;
ditto
[...]
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support Antoine Soulier via ffmpeg-devel
@ 2024-03-26 18:01 ` Stefano Sabatini
0 siblings, 0 replies; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-26 18:01 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 16:47:39 +0000, ffmpeg-devel Mailing List wrote:
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> Changelog | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Changelog b/Changelog
> index c6e8f6bcaf..fb08ee2f81 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
> releases are sorted from youngest to oldest.
>
> version <next>:
> +- LC3/LC3plus decoding/encoding using external library liblc3
> - DXV DXT1 encoder
> - LEAD MCMP decoder
> - EVC decoding using external library libxevd
new entries to the bottom, also this is not rebased on top of mainline
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
@ 2024-03-26 18:17 ` Stefano Sabatini
0 siblings, 0 replies; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-26 18:17 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 16:47:38 +0000, ffmpeg-devel Mailing List wrote:
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> doc/encoders.texi | 57 +++++++++++++++++++++++++++++++++++++++
> doc/general_contents.texi | 12 ++++++++-
> doc/muxers.texi | 4 +++
> 3 files changed, 72 insertions(+), 1 deletion(-)
>
> diff --git a/doc/encoders.texi b/doc/encoders.texi
> index 7c223ed74c..0719ba13cc 100644
> --- a/doc/encoders.texi
> +++ b/doc/encoders.texi
> @@ -814,6 +814,63 @@ ffmpeg -i input.wav -c:a libfdk_aac -profile:a aac_he -b:a 64k output.m4a
> @end example
> @end itemize
>
> +@anchor{liblc3-enc}
> +@section liblc3
> +
> +liblc3 LC3 (Low Complexity Communication Codec) encoder wrapper.
> +
> +Requires the presence of the liblc3 headers and library during configuration.
> +You need to explicitly configure the build with @code{--enable-liblc3}.
> +
> +This encoder has support for the Bluetooth SIG LC3 codec for the LE Audio
> +protocol, and the following features of LC3plus:
> +@itemize @minus
> +@item
> +Frame duration of 2.5 and 5ms.
> +@item
> +High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
> +@end itemize
> +
> +For more information see the liblc3 project at
> +@url{https://github.com/google/liblc3}.
> +
> +@subsection Options
> +
> +The following options are mapped on the shared FFmpeg codec options.
> +
> +@table @option
> +@item b
> +Set the bit rate in bits/s. This will determine the fixed size of the encoded
> +frames, for a selected frame duration.
> +
> +@item ar
> +Set the audio sampling rate (in Hz).
> +
> +@item channels
> +Set the number of audio channels.
> +
> +@item frame_duration
> +Set the audio frame duration in milliseconds. Default value is 10ms.
> +Allowed frame durations are 2.5ms, 5ms, 7.5ms and 10ms.
> +LC3 (Bluetooth LE Audio), allows 7.5ms and 10ms; and LC3plus 2.5ms, 5ms
> +and 10ms.
> +
> +The 10ms frame duration is available in LC3 and LC3 plus standard.
> +In this mode, the produced bitstream can be referenced either as LC3 or LC3plus.
> +
> +@item high_resolution
> +Enable the high-resolution mode if set to 1. The high-resolution mode is
> +available with all LC3plus frame durations and for a sampling rate 48 KHz,
> +and 96 KHz.
> +
> +The encoder automatically turns off this mode at lower sampling rates and
> +activates it at 96 KHz.
> +
> +This mode should be preferred at high bitrates. In this mode, the audio
> +bandwidth is always up to the Nyquist frequency, compared to LC3 at 48 KHz,
> +which limits the bandwidth to 20 KHz.
> +@end table
> +
> @anchor{libmp3lame}
> @section libmp3lame
>
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index f269cbd1a9..9e340fce7b 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -237,6 +237,14 @@ Go to @url{http://sourceforge.net/projects/opencore-amr/} and follow the
> instructions for installing the library.
> Then pass @code{--enable-libfdk-aac} to configure to enable it.
>
> +@subsection LC3 library
> +
> +FFmpeg can make use of the Google LC3 library for LC3 decoding & encoding.
> +
> +Go to @url{https://github.com/google/liblc3/} and follow the instructions for
> +installing the library.
> +Then pass @code{--enable-liblc3} to configure to enable it.
> +
> @section OpenH264
>
> FFmpeg can make use of the OpenH264 library for H.264 decoding and encoding.
> @@ -1300,7 +1308,9 @@ following image formats are supported:
> @tab encoding and decoding supported through external library libilbc
> @item IMC (Intel Music Coder) @tab @tab X
> @item Interplay ACM @tab @tab X
> -@item MACE (Macintosh Audio Compression/Expansion) 3:1 @tab @tab X
> +@item LC3 @tab E @tab E
> + @tab supported through external library liblc3
> +@item LC3plus @tab E @tab E
> @item MACE (Macintosh Audio Compression/Expansion) 6:1 @tab @tab X
> @item Marian's A-pac audio @tab @tab X
> @item MI-SC4 (Micronas SC-4 Audio) @tab @tab X
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index d9dd72e613..43dabe4499 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -132,6 +132,10 @@ to Annex B syntax if it's in length-prefixed mode.
> ITU-T H.265 / MPEG-H Part 2 HEVC video. Bitstream shall be converted
> to Annex B syntax if it's in length-prefixed mode.
>
> +@item lc3 @emph{audio} (lc3)
> +Bluetooth SIG Low Complexity Communication Codec audio.
> +ETSI TS 103 634 Low Complexity Communication Codec plus (LC3plus).
technically this is not a raw muxer, so it should stand in a dedicated section
[...]
Should be good othetwise.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-26 17:49 ` Stefano Sabatini
@ 2024-03-26 18:27 ` Andreas Rheinhardt
2024-03-26 19:09 ` Antoine Soulier via ffmpeg-devel
1 sibling, 1 reply; 22+ messages in thread
From: Andreas Rheinhardt @ 2024-03-26 18:27 UTC (permalink / raw)
To: ffmpeg-devel
Antoine Soulier via ffmpeg-devel:
> The LC3 audio codec is the default codec of Bluetooth LE audio.
> This is a wrapper over the liblc3 library (https://github.com/google/liblc3).
>
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> libavcodec/Makefile | 3 +
> libavcodec/allcodecs.c | 3 +
> libavcodec/codec_desc.c | 14 +++
> libavcodec/codec_id.h | 2 +
> libavcodec/liblc3dec.c | 146 ++++++++++++++++++++++++++++++
> libavcodec/liblc3enc.c | 191 ++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 359 insertions(+)
> create mode 100644 libavcodec/liblc3dec.c
> create mode 100644 libavcodec/liblc3enc.c
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 708434ac76..7d2cf3076d 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -1123,6 +1123,9 @@ OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o
> OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o
> OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o
> OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o
> +OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
> +OBJS-$(CONFIG_LIBLC3_LC3_DECODER) += liblc3dec.o
> +OBJS-$(CONFIG_LIBLC3_LC3PLUS_DECODER) += liblc3dec.o
> OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
> OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
> OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2386b450a6..29aedaeac6 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -776,6 +776,9 @@ extern const FFCodec ff_libilbc_encoder;
> extern const FFCodec ff_libilbc_decoder;
> extern const FFCodec ff_libjxl_decoder;
> extern const FFCodec ff_libjxl_encoder;
> +extern const FFCodec ff_liblc3_encoder;
> +extern const FFCodec ff_liblc3_lc3_decoder;
> +extern const FFCodec ff_liblc3_lc3plus_decoder;
> extern const FFCodec ff_libmp3lame_encoder;
> extern const FFCodec ff_libopencore_amrnb_encoder;
> extern const FFCodec ff_libopencore_amrnb_decoder;
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 3bab86db62..230bba2a09 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3425,6 +3425,20 @@ static const AVCodecDescriptor codec_descriptors[] = {
> .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"),
> .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> },
> + {
> + .id = AV_CODEC_ID_LC3,
> + .type = AVMEDIA_TYPE_AUDIO,
> + .name = "lc3",
> + .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_LC3_PLUS,
> + .type = AVMEDIA_TYPE_AUDIO,
> + .name = "lc3_plus",
> + .long_name = NULL_IF_CONFIG_SMALL("LC3plus (Low Complexity Communication Codec plus)"),
> + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> + },
>
> /* subtitle codecs */
> {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index c8dc21da74..7e4cb39049 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -543,6 +543,8 @@ enum AVCodecID {
> AV_CODEC_ID_AC4,
> AV_CODEC_ID_OSQ,
> AV_CODEC_ID_QOA,
> + AV_CODEC_ID_LC3,
> + AV_CODEC_ID_LC3_PLUS,
>
> /* subtitle codecs */
> AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs.
> diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
> new file mode 100644
> index 0000000000..e97cecc68f
> --- /dev/null
> +++ b/libavcodec/liblc3dec.c
> @@ -0,0 +1,146 @@
> +/*
> + * LC3 decoder wrapper
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavutil/intreadwrite.h"
> +
> +#include "avcodec.h"
> +#include "codec.h"
> +#include "codec_internal.h"
> +#include "decode.h"
> +#include "internal.h"
> +
> +#define DECODER_MAX_CHANNELS 2
> +
> +typedef struct LibLC3DecContext {
> + int frame_us, srate_hz;
> + bool hr_mode;
> + lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
> +} LibLC3DecContext;
> +
> +static av_cold int liblc3_decode_init(AVCodecContext *avctx)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> + unsigned decoder_size;
> +
> + if (avctx->extradata_size < 2)
> + return AVERROR_INVALIDDATA;
> +
> + liblc3->frame_us = AV_RL16(avctx->extradata + 0);
> + liblc3->srate_hz = avctx->sample_rate;
> + liblc3->hr_mode = avctx->extradata_size >= 6 &&
> + AV_RL16(avctx->extradata + 4);
> +
> + av_log(avctx, AV_LOG_INFO,
> + "Decoding %.1f ms frames\n", liblc3->frame_us / 1e3f);
> + if (liblc3->hr_mode)
> + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> +
> + decoder_size = lc3_hr_decoder_size(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + if (!decoder_size)
> + return AVERROR_INVALIDDATA;
> +
> + for (int ch = 0; ch < channels; ch++) {
> + liblc3->decoder[ch] = lc3_hr_setup_decoder(liblc3->hr_mode,
> + liblc3->frame_us, liblc3->srate_hz, 0, av_malloc(decoder_size));
1. Leak on error (if this fails for the second channel, then the first
channel's buffer will not be freed). Use FF_CODEC_CAP_INIT_CLEANUP for that.
2. Shouldn't there be a check for that av_malloc or is the idea that
lc3_hr_setup_decoder() checks for us?
3. You can allocate the decoders jointly: ptr =
av_malloc_array(decoder_size, channels) and then use (char*)ptr + ch *
decoder_size as context for the decoder for channel ch.
liblc3->decoder[0] would be the actually allocated pointer for freeing.
> + if (!liblc3->decoder[ch])
> + return AVERROR(ENOMEM);
What makes you believe that this is the only error that can happen?
> + }
> +
> + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
> + avctx->delay = lc3_hr_delay_samples(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + avctx->internal->skip_samples = avctx->delay;
> +
> + return 0;
> +}
> +
> +static av_cold int liblc3_decode_close(AVCodecContext *avctx)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> +
> + for (int ch = 0; ch < channels; ch++)
> + av_free(liblc3->decoder[ch]);
> +
> + return 0;
> +}
> +
> +static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
> + int *got_frame_ptr, AVPacket *avpkt)
> +{
> + LibLC3DecContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> + uint8_t *in = avpkt->data;
> + int block_bytes, ret;
> +
> + frame->nb_samples = lc3_hr_frame_samples(
> + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> + return ret;
> +
> + block_bytes = avpkt->size;
> + for (int ch = 0; ch < channels; ch++) {
> + int frame_bytes = block_bytes / channels
> + + (ch < block_bytes % channels);
> +
> +
> + ret = lc3_decode(liblc3->decoder[ch], in, frame_bytes,
> + LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
> + if (ret < 0)
> + return AVERROR_INVALIDDATA;
> +
> + in += frame_bytes;
> + }
> +
> + frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
> +
> + *got_frame_ptr = 1;
> +
> + return avpkt->size;
> +}
> +
> +const FFCodec ff_liblc3_lc3_decoder = {
> + .p.name = "liblc3",
> + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> + .p.type = AVMEDIA_TYPE_AUDIO,
> + .p.id = AV_CODEC_ID_LC3,
> + .p.capabilities = AV_CODEC_CAP_DR1,
> + .p.wrapper_name = "liblc3",
> + .priv_data_size = sizeof(LibLC3DecContext),
> + .init = liblc3_decode_init,
> + .close = liblc3_decode_close,
> + FF_CODEC_DECODE_CB(liblc3_decode),
> +};
> +
> +const FFCodec ff_liblc3_lc3plus_decoder = {
> + .p.name = "liblc3",
> + CODEC_LONG_NAME("LC3plus (Low Complexity Communication Codec plus)"),
> + .p.type = AVMEDIA_TYPE_AUDIO,
> + .p.id = AV_CODEC_ID_LC3_PLUS,
> + .p.capabilities = AV_CODEC_CAP_DR1,
> + .p.wrapper_name = "liblc3",
> + .priv_data_size = sizeof(LibLC3DecContext),
> + .init = liblc3_decode_init,
> + .close = liblc3_decode_close,
> + FF_CODEC_DECODE_CB(liblc3_decode),
> +};
> diff --git a/libavcodec/liblc3enc.c b/libavcodec/liblc3enc.c
> new file mode 100644
> index 0000000000..6495742c04
> --- /dev/null
> +++ b/libavcodec/liblc3enc.c
> @@ -0,0 +1,191 @@
> +/*
> + * LC3 encoder wrapper
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +
> +#include "avcodec.h"
> +#include "codec.h"
> +#include "codec_internal.h"
> +#include "encode.h"
> +
> +#define ENCODER_MAX_CHANNELS 2
> +
> +typedef struct LibLC3EncOpts {
> + float frame_duration;
> + bool hr_mode;
> +} LibLC3EncOpts;
> +
> +typedef struct LibLC3EncContext {
> + AVClass *av_class;
> + LibLC3EncOpts opts;
> + int block_bytes;
> + lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
> +} LibLC3EncContext;
> +
> +static av_cold int liblc3_encode_init(AVCodecContext *avctx)
> +{
> + LibLC3EncContext *liblc3 = avctx->priv_data;
> + bool hr_mode = liblc3->opts.hr_mode;
> + int frame_us = liblc3->opts.frame_duration * 1000;
> + int srate_hz = avctx->sample_rate;
> + int channels = avctx->ch_layout.nb_channels;
> + int effective_bit_rate;
> + unsigned encoder_size;
> +
> + if (frame_us != 2500 && frame_us != 5000 &&
> + frame_us != 7500 && frame_us != 10000 ) {
> + av_log(avctx, AV_LOG_ERROR,
> + "Unsupported frame duration %.1f ms\n", frame_us / 1e3f);
> + return AVERROR(EINVAL);
> + }
> +
> + hr_mode |= srate_hz > 48000;
> + hr_mode &= srate_hz >= 48000;
> +
> + if (frame_us <= 5000 || hr_mode)
> + avctx->codec_id = AV_CODEC_ID_LC3_PLUS;
The encoder has to encode the format it advertises (via AVCodec.id); it
must not change it.
(What is actually the point of this using two different codec ids? You
do not even need to tell the libraries whether you are decoding/encoding
the +-version or not.)
> +
> + if (frame_us == 7500 && hr_mode) {
> + av_log(avctx, AV_LOG_ERROR,
> + "High-reolution mode not supported with 7.5 ms frames\n");
> + return AVERROR(EINVAL);
> + }
> +
> + av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames\n", frame_us / 1e3f);
> + if (hr_mode)
> + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> +
> + liblc3->block_bytes = lc3_hr_frame_block_bytes(
> + hr_mode, frame_us, srate_hz, channels, avctx->bit_rate);
> +
> + effective_bit_rate = lc3_hr_resolve_bitrate(
> + hr_mode, frame_us, srate_hz, liblc3->block_bytes);
> +
> + if (avctx->bit_rate != effective_bit_rate)
> + av_log(avctx, AV_LOG_WARNING,
> + "Bitrate changed to %d bps\n", effective_bit_rate);
> + avctx->bit_rate = effective_bit_rate;
> +
> + encoder_size = lc3_hr_encoder_size(frame_us, frame_us, srate_hz);
> + if (!encoder_size)
> + return AVERROR(EINVAL);
> +
> + for (int ch = 0; ch < channels; ch++) {
> + liblc3->encoder[ch] = lc3_hr_setup_encoder(
> + hr_mode, frame_us, srate_hz, 0, av_malloc(encoder_size));
> + if (!liblc3->encoder[ch])
> + return AVERROR(ENOMEM);
> + }
> +
> + avctx->extradata = av_mallocz(6);
Padding
> + if (!avctx->extradata)
> + return AVERROR(ENOMEM);
> +
> + AV_WL16(avctx->extradata + 0, frame_us);
> + AV_WL16(avctx->extradata + 2, 0);
> + AV_WL16(avctx->extradata + 4, hr_mode);
> + avctx->extradata_size = 6;
> +
> + avctx->frame_size = lc3_hr_frame_samples(hr_mode, frame_us, srate_hz);
> +
> + return 0;
> +}
> +
> +static av_cold int liblc3_encode_close(AVCodecContext *avctx)
> +{
> + LibLC3EncContext *liblc3 = avctx->priv_data;
> + int channels = avctx->ch_layout.nb_channels;
> +
> + for (int ch = 0; ch < channels; ch++)
> + av_free(liblc3->encoder[ch]);
> +
> + return 0;
> +}
> +
> +static int liblc3_encode(AVCodecContext *avctx, AVPacket *avpkt,
> + const AVFrame *av_frame, int *got_packet_ptr)
> +{
> + LibLC3EncContext *liblc3 = avctx->priv_data;
> + int block_bytes = liblc3->block_bytes;
> + int channels = avctx->ch_layout.nb_channels;
> + uint8_t *data_ptr;
> + int ret;
> +
> + if ((ret = ff_alloc_packet(avctx, avpkt, block_bytes)) < 0)
> + return ret;
> +
> + data_ptr = avpkt->data;
> + for (int ch = 0; ch < channels; ch++) {
> + int frame_bytes = block_bytes / channels
> + + (ch < block_bytes % channels);
> +
> + lc3_encode(liblc3->encoder[ch],
> + LC3_PCM_FORMAT_FLOAT, av_frame->data[ch], 1,
> + frame_bytes, data_ptr);
> +
> + data_ptr += frame_bytes;
> + }
> +
> + *got_packet_ptr = 1;
> +
> + return 0;
> +}
> +
> +#define OFFSET(x) offsetof(LibLC3EncContext, opts.x)
> +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption options[] = {
> + { "frame_duration", "Duration of a frame in milliseconds",
> + OFFSET(frame_duration), AV_OPT_TYPE_FLOAT,
> + { .dbl = 10.0 }, 2.5, 10.0, FLAGS },
> + { "high_resolution", "Enable High-Resolution mode (48 KHz or 96 KHz)",
> + OFFSET(hr_mode), AV_OPT_TYPE_BOOL,
> + { .i64 = 0 }, 0, 1, FLAGS },
> + { NULL }
> +};
> +
> +static const AVClass class = {
> + .class_name = "liblc3 encoder",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const FFCodec ff_liblc3_encoder = {
> + .p.name = "liblc3",
> + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> + .p.type = AVMEDIA_TYPE_AUDIO,
> + .p.id = AV_CODEC_ID_LC3,
> + .p.capabilities = AV_CODEC_CAP_DR1,
> + .p.ch_layouts = (const AVChannelLayout[])
> + { { AV_CHANNEL_ORDER_UNSPEC, 1 },
> + { AV_CHANNEL_ORDER_UNSPEC, 2 }, { 0 } },
> + .p.supported_samplerates = (const int [])
> + { 96000, 48000, 32000, 24000, 16000, 8000, 0 },
> + .p.sample_fmts = (const enum AVSampleFormat[])
> + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE },
> + .p.priv_class = &class,
> + .p.wrapper_name = "liblc3",
> + .priv_data_size = sizeof(LibLC3EncContext),
> + .init = liblc3_encode_init,
> + .close = liblc3_encode_close,
> + FF_CODEC_ENCODE_CB(liblc3_encode),
> +};
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-03-26 17:59 ` Stefano Sabatini
@ 2024-03-26 18:33 ` Andreas Rheinhardt
1 sibling, 0 replies; 22+ messages in thread
From: Andreas Rheinhardt @ 2024-03-26 18:33 UTC (permalink / raw)
To: ffmpeg-devel
Antoine Soulier via ffmpeg-devel:
> A file format is described in Bluetooth SIG LC3 and ETSI TS 103 634, for
> test purpose. This is the format implemented here.
>
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> Signed-off-by: Antoine SOULIER <asoulier@google.com>
> ---
> libavformat/Makefile | 3 +
> libavformat/allformats.c | 3 +
> libavformat/lc3dec.c | 140 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 118 +++++++++++++++++++++++++++++++++
> 4 files changed, 264 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 94a949f555..29a38c1d94 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,9 @@ OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o
> OBJS-$(CONFIG_KVAG_MUXER) += kvag.o rawenc.o
> OBJS-$(CONFIG_LAF_DEMUXER) += lafdec.o
> OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o
> +OBJS-$(CONFIG_LC3_DEMUXER) += lc3dec.o
> +OBJS-$(CONFIG_LC3_MUXER) += lc3enc.o
> +OBJS-$(CONFIG_LC3_PLUS_MUXER) += lc3enc.o
> OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o
> OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o
> OBJS-$(CONFIG_LUODAT_DEMUXER) += luodatdec.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index e15d0fa6d7..551b0f0d7b 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,9 @@ extern const FFInputFormat ff_kvag_demuxer;
> extern const FFOutputFormat ff_kvag_muxer;
> extern const FFInputFormat ff_laf_demuxer;
> extern const FFOutputFormat ff_latm_muxer;
> +extern const FFInputFormat ff_lc3_demuxer;
> +extern const FFOutputFormat ff_lc3_muxer;
> +extern const FFOutputFormat ff_lc3_plus_muxer;
> extern const FFInputFormat ff_lmlm4_demuxer;
> extern const FFInputFormat ff_loas_demuxer;
> extern const FFInputFormat ff_luodat_demuxer;
> diff --git a/libavformat/lc3dec.c b/libavformat/lc3dec.c
> new file mode 100644
> index 0000000000..563384f786
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,140 @@
> +/*
> + * LC3 demuxer
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * Based on the file format specified by :
> + *
> + * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
> + * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
> + * 3.2.8.2 Reference LC3 Codec Bitstream Format
> + *
> + * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
> + * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
> + * LC3plus conformance script package
> + */
> +
> +#include <lc3.h>
> +
> +#include "libavcodec/avcodec.h"
What is that needed for? (De)muxers have almost no reason to ever need this.
> +#include "libavcodec/packet.h"
> +#include "libavutil/intreadwrite.h"
> +
> +#include "avformat.h"
> +#include "avio.h"
> +#include "demux.h"
> +#include "internal.h"
> +
> +typedef struct LC3DemuxContext {
> + int frame_samples;
> + int64_t position;
> + int64_t length;
> +} LC3DemuxContext;
> +
> +static int lc3_read_header(AVFormatContext *s)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVStream *st = NULL;
> + uint16_t tag, hdr_size;
> + uint16_t frame_us;
> + uint32_t length;
> + bool ep_mode, hr_mode;
> + int srate_hz, channels, bit_rate;
> + int num_extra_params, ret;
> +
> + tag = avio_rb16(s->pb);
> + hdr_size = avio_rl16(s->pb);
> +
> + if (tag != 0x1ccc || hdr_size < 9 * sizeof(uint16_t))
> + return AVERROR_INVALIDDATA;
> +
> + num_extra_params = hdr_size / sizeof(uint16_t) - 9;
> +
> + srate_hz = avio_rl16(s->pb) * 100;
> + bit_rate = avio_rl16(s->pb) * 100;
> + channels = avio_rl16(s->pb);
> + frame_us = avio_rl16(s->pb) * 10;
> + ep_mode = avio_rl16(s->pb) != 0;
> + length = avio_rl32(s->pb);
> + hr_mode = num_extra_params >= 1 && avio_rl16(s->pb);
> +
> + st = avformat_new_stream(s, NULL);
> + if (!st)
> + return AVERROR(ENOMEM);
> +
> + avpriv_set_pts_info(st, 32, 1, srate_hz);
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = frame_us <= 5000 || ep_mode || hr_mode ?
> + AV_CODEC_ID_LC3_PLUS : AV_CODEC_ID_LC3;
> + st->codecpar->sample_rate = srate_hz;
> + st->codecpar->bit_rate = bit_rate;
> + st->codecpar->ch_layout.nb_channels = channels;
> +
> + if ((ret = ff_alloc_extradata(st->codecpar, 6)) < 0) {
> + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
Pointless log
> + return ret;
> + }
> +
> + AV_WL16(st->codecpar->extradata + 0, frame_us);
> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> +
> + lc3->frame_samples = lc3_hr_frame_samples(hr_mode, frame_us, srate_hz);
> + lc3->position = 0;
> + lc3->length = st->duration +
> + lc3_hr_delay_samples(hr_mode, frame_us, srate_hz);
> +
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int ret;
> +
> + if ((ret = av_get_packet(s->pb, pkt, avio_rl16(pb))) < 0)
Can you avoid the "if ((ret = func()) < 0)" style? We had bugs due to
wrongly placed parentheses with it.
> + return ret;
> +
> + pkt->duration = lc3->frame_samples;
> + lc3->position += lc3->frame_samples;
> + if (lc3->position > lc3->length) {
> + pkt->duration -= lc3->position - lc3->length;
> + lc3->position = lc3->length;
> + }
> +
> + return 0;
> +}
> +
> +const FFInputFormat ff_lc3_demuxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL(
> + "LC3 / LC3plus (Low Complexity Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_GENERIC_INDEX |
> + AVFMT_NOTIMESTAMPS |
> + AVFMT_NO_BYTE_SEEK,
> + .priv_data_size = sizeof(LC3DemuxContext),
> + .read_header = lc3_read_header,
> + .read_packet = lc3_read_packet,
> +};
> diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
> new file mode 100644
> index 0000000000..e6a9ddb35d
> --- /dev/null
> +++ b/libavformat/lc3enc.c
> @@ -0,0 +1,118 @@
> +/*
> + * LC3 muxer
> + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * Based on the file format specified by :
> + *
> + * - Bluetooth SIG - Low Complexity Communication Codec Test Suite
> + * https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=502301
> + * 3.2.8.2 Reference LC3 Codec Bitstream Format
> + *
> + * - ETSI TI 103 634 V1.4.1 - Low Complexity Communication Codec plus
> + * https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.04.01_60/ts_103634v010401p.pdf
> + * LC3plus conformance script package
> + */
> +
> +#include <lc3.h>
If this header is needed (it doesn't seem to be, unless for bool (we
typically use int for this)), then these muxers can only be compiled if
liblc3 is available.
> +
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
This muxer doesn't have options.
> +
> +#include "avformat.h"
> +#include "avio.h"
> +#include "mux.h"
> +#include "internal.h"
> +
> +static av_cold int lc3_init(AVFormatContext *s)
> +{
> + if (s->nb_streams != 1) {
> + av_log(s, AV_LOG_ERROR, "This muxer only supports a single stream.\n");
> + return AVERROR(EINVAL);
> + }
> +
> + return 0;
> +}
> +
> +static int lc3_write_header(AVFormatContext *s)
> +{
> + AVStream *st = s->streams[0];
> + int channels = st->codecpar->ch_layout.nb_channels;
> + int srate_hz = st->codecpar->sample_rate;
> + int bit_rate = st->codecpar->bit_rate;
> + uint16_t frame_us = 10000;
> + bool ep_mode = false, hr_mode = false;
> + uint32_t nb_samples = av_rescale_q(
> + st->duration, st->time_base, (AVRational){ 1, srate_hz });
> +
> + if (st->codecpar->extradata_size >= 2)
> + frame_us = AV_RL16(st->codecpar->extradata + 0);
> + if (st->codecpar->extradata_size >= 4)
> + ep_mode = AV_RL16(st->codecpar->extradata + 2);
> + if (st->codecpar->extradata_size >= 6)
There is no reason to treat arbitrary extradata size as valid.
Why should extradata_size == 2 or == 4 even exist?
> + hr_mode = AV_RL16(st->codecpar->extradata + 4);
> +
> + avio_wb16(s->pb, 0x1ccc);
> + avio_wl16(s->pb, (9 + hr_mode) * sizeof(uint16_t));
> + avio_wl16(s->pb, srate_hz / 100);
> + avio_wl16(s->pb, bit_rate / 100);
> + avio_wl16(s->pb, channels);
> + avio_wl16(s->pb, frame_us / 10);
> + avio_wl16(s->pb, ep_mode);
> + avio_wl32(s->pb, nb_samples);
> + if (hr_mode)
> + avio_wl16(s->pb, hr_mode);
> +
> + return 0;
> +}
> +
> +static int lc3_write_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + avio_wl16(s->pb, pkt->size);
> + avio_write(s->pb, pkt->data, pkt->size);
> + return 0;
> +}
> +
> +const FFOutputFormat ff_lc3_muxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL(
> + "LC3 (Low Complexity Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.audio_codec = AV_CODEC_ID_LC3,
> + .p.video_codec = AV_CODEC_ID_NONE,
> + .p.flags = AVFMT_NOTIMESTAMPS,
> + .init = lc3_init,
> + .write_header = lc3_write_header,
> + .write_packet = lc3_write_packet,
> +};
> +
> +const FFOutputFormat ff_lc3_plus_muxer = {
> + .p.name = "lc3_plus",
> + .p.long_name = NULL_IF_CONFIG_SMALL(
> + "LC3Plus (Low Complexity Communication Codec plus)"),
> + .p.extensions = "lc3",
> + .p.audio_codec = AV_CODEC_ID_LC3_PLUS,
> + .p.video_codec = AV_CODEC_ID_NONE,
> + .p.flags = AVFMT_NOTIMESTAMPS,
> + .init = lc3_init,
> + .write_header = lc3_write_header,
> + .write_packet = lc3_write_packet,
> +};
I do not see the point of having duplicate muxers. It is common for
muxers to support multiple codecs.
But given that both the codecs and the muxers don't differentiate
between the different codec_ids at all, there does not seem to be a
point of using two different codec ids at all.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 17:49 ` Stefano Sabatini
@ 2024-03-26 18:50 ` Antoine Soulier via ffmpeg-devel
0 siblings, 0 replies; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 18:50 UTC (permalink / raw)
To: FFmpeg development discussions and patches, Antoine Soulier
Cc: Antoine Soulier
Thanks for your review.
I am not sure to understand "1e3f might be replaced by a symbolic
constant/macro".
It's for a conversion from microseconds to milliseconds (standard unit used
for the frame duration).
It's not a magical constant, I do some greps and it looks like such
constant "1000.f" are already defined elsewhere.
On Tue, Mar 26, 2024 at 10:49 AM Stefano Sabatini <stefasab@gmail.com>
wrote:
> On date Tuesday 2024-03-26 16:47:36 +0000, ffmpeg-devel Mailing List wrote:
> > The LC3 audio codec is the default codec of Bluetooth LE audio.
> > This is a wrapper over the liblc3 library (
> https://github.com/google/liblc3).
> >
> > Signed-off-by: Antoine Soulier <asoulier@google.com>
> > Signed-off-by: Antoine SOULIER <asoulier@google.com>
> > ---
> > libavcodec/Makefile | 3 +
> > libavcodec/allcodecs.c | 3 +
> > libavcodec/codec_desc.c | 14 +++
> > libavcodec/codec_id.h | 2 +
> > libavcodec/liblc3dec.c | 146 ++++++++++++++++++++++++++++++
> > libavcodec/liblc3enc.c | 191 ++++++++++++++++++++++++++++++++++++++++
> > 6 files changed, 359 insertions(+)
> > create mode 100644 libavcodec/liblc3dec.c
> > create mode 100644 libavcodec/liblc3enc.c
> >
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> > index 708434ac76..7d2cf3076d 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -1123,6 +1123,9 @@ OBJS-$(CONFIG_LIBILBC_ENCODER) +=
> libilbc.o
> > OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o
> > OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o
> > OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o
> > +OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
> > +OBJS-$(CONFIG_LIBLC3_LC3_DECODER) += liblc3dec.o
> > +OBJS-$(CONFIG_LIBLC3_LC3PLUS_DECODER) += liblc3dec.o
> > OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
> > OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
> > OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
> > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> > index 2386b450a6..29aedaeac6 100644
> > --- a/libavcodec/allcodecs.c
> > +++ b/libavcodec/allcodecs.c
> > @@ -776,6 +776,9 @@ extern const FFCodec ff_libilbc_encoder;
> > extern const FFCodec ff_libilbc_decoder;
> > extern const FFCodec ff_libjxl_decoder;
> > extern const FFCodec ff_libjxl_encoder;
> > +extern const FFCodec ff_liblc3_encoder;
> > +extern const FFCodec ff_liblc3_lc3_decoder;
> > +extern const FFCodec ff_liblc3_lc3plus_decoder;
> > extern const FFCodec ff_libmp3lame_encoder;
> > extern const FFCodec ff_libopencore_amrnb_encoder;
> > extern const FFCodec ff_libopencore_amrnb_decoder;
> > diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> > index 3bab86db62..230bba2a09 100644
> > --- a/libavcodec/codec_desc.c
> > +++ b/libavcodec/codec_desc.c
> > @@ -3425,6 +3425,20 @@ static const AVCodecDescriptor
> codec_descriptors[] = {
> > .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"),
> > .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> > },
> > + {
> > + .id = AV_CODEC_ID_LC3,
> > + .type = AVMEDIA_TYPE_AUDIO,
> > + .name = "lc3",
> > + .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_LC3_PLUS,
> > + .type = AVMEDIA_TYPE_AUDIO,
> > + .name = "lc3_plus",
> > + .long_name = NULL_IF_CONFIG_SMALL("LC3plus (Low Complexity
> Communication Codec plus)"),
> > + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> > + },
> >
> > /* subtitle codecs */
> > {
> > diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> > index c8dc21da74..7e4cb39049 100644
> > --- a/libavcodec/codec_id.h
> > +++ b/libavcodec/codec_id.h
> > @@ -543,6 +543,8 @@ enum AVCodecID {
> > AV_CODEC_ID_AC4,
> > AV_CODEC_ID_OSQ,
> > AV_CODEC_ID_QOA,
> > + AV_CODEC_ID_LC3,
> > + AV_CODEC_ID_LC3_PLUS,
> >
> > /* subtitle codecs */
> > AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID
> pointing at the start of subtitle codecs.
> > diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
> > new file mode 100644
> > index 0000000000..e97cecc68f
> > --- /dev/null
> > +++ b/libavcodec/liblc3dec.c
> > @@ -0,0 +1,146 @@
> > +/*
> > + * LC3 decoder wrapper
> > + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * Permission to use, copy, modify, and/or distribute this software for
> any
> > + * purpose with or without fee is hereby granted, provided that the
> above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
> FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
> DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
> OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + */
> > +
> > +#include <lc3.h>
> > +
> > +#include "libavutil/intreadwrite.h"
> > +
> > +#include "avcodec.h"
> > +#include "codec.h"
> > +#include "codec_internal.h"
> > +#include "decode.h"
> > +#include "internal.h"
> > +
> > +#define DECODER_MAX_CHANNELS 2
> > +
> > +typedef struct LibLC3DecContext {
> > + int frame_us, srate_hz;
> > + bool hr_mode;
> > + lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
> > +} LibLC3DecContext;
> > +
> > +static av_cold int liblc3_decode_init(AVCodecContext *avctx)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > + unsigned decoder_size;
> > +
> > + if (avctx->extradata_size < 2)
> > + return AVERROR_INVALIDDATA;
> > +
> > + liblc3->frame_us = AV_RL16(avctx->extradata + 0);
> > + liblc3->srate_hz = avctx->sample_rate;
> > + liblc3->hr_mode = avctx->extradata_size >= 6 &&
> > + AV_RL16(avctx->extradata + 4);
> > +
> > + av_log(avctx, AV_LOG_INFO,
>
> > + "Decoding %.1f ms frames\n", liblc3->frame_us / 1e3f);
>
> 1e3f might be replaced by a symbolic constant/macro
>
> > + if (liblc3->hr_mode)
> > + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> > +
> > + decoder_size = lc3_hr_decoder_size(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + if (!decoder_size)
> > + return AVERROR_INVALIDDATA;
> > +
> > + for (int ch = 0; ch < channels; ch++) {
> > + liblc3->decoder[ch] = lc3_hr_setup_decoder(liblc3->hr_mode,
> > + liblc3->frame_us, liblc3->srate_hz, 0,
> av_malloc(decoder_size));
> > + if (!liblc3->decoder[ch])
> > + return AVERROR(ENOMEM);
> > + }
> > +
> > + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
> > + avctx->delay = lc3_hr_delay_samples(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + avctx->internal->skip_samples = avctx->delay;
> > +
> > + return 0;
> > +}
> > +
> > +static av_cold int liblc3_decode_close(AVCodecContext *avctx)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > +
> > + for (int ch = 0; ch < channels; ch++)
> > + av_free(liblc3->decoder[ch]);
> > +
> > + return 0;
> > +}
> > +
> > +static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
> > + int *got_frame_ptr, AVPacket *avpkt)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > + uint8_t *in = avpkt->data;
> > + int block_bytes, ret;
> > +
> > + frame->nb_samples = lc3_hr_frame_samples(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> > + return ret;
> > +
> > + block_bytes = avpkt->size;
> > + for (int ch = 0; ch < channels; ch++) {
> > + int frame_bytes = block_bytes / channels
> > + + (ch < block_bytes % channels);
> > +
> > +
> > + ret = lc3_decode(liblc3->decoder[ch], in, frame_bytes,
> > + LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
> > + if (ret < 0)
> > + return AVERROR_INVALIDDATA;
> > +
> > + in += frame_bytes;
> > + }
> > +
> > + frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
> > +
> > + *got_frame_ptr = 1;
> > +
> > + return avpkt->size;
> > +}
> > +
> > +const FFCodec ff_liblc3_lc3_decoder = {
> > + .p.name = "liblc3",
> > + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> > + .p.type = AVMEDIA_TYPE_AUDIO,
> > + .p.id = AV_CODEC_ID_LC3,
> > + .p.capabilities = AV_CODEC_CAP_DR1,
> > + .p.wrapper_name = "liblc3",
> > + .priv_data_size = sizeof(LibLC3DecContext),
> > + .init = liblc3_decode_init,
> > + .close = liblc3_decode_close,
> > + FF_CODEC_DECODE_CB(liblc3_decode),
> > +};
> > +
> > +const FFCodec ff_liblc3_lc3plus_decoder = {
> > + .p.name = "liblc3",
> > + CODEC_LONG_NAME("LC3plus (Low Complexity Communication Codec
> plus)"),
> > + .p.type = AVMEDIA_TYPE_AUDIO,
> > + .p.id = AV_CODEC_ID_LC3_PLUS,
> > + .p.capabilities = AV_CODEC_CAP_DR1,
> > + .p.wrapper_name = "liblc3",
> > + .priv_data_size = sizeof(LibLC3DecContext),
> > + .init = liblc3_decode_init,
> > + .close = liblc3_decode_close,
> > + FF_CODEC_DECODE_CB(liblc3_decode),
> > +};
> > diff --git a/libavcodec/liblc3enc.c b/libavcodec/liblc3enc.c
> > new file mode 100644
> > index 0000000000..6495742c04
> > --- /dev/null
> > +++ b/libavcodec/liblc3enc.c
> > @@ -0,0 +1,191 @@
> > +/*
> > + * LC3 encoder wrapper
> > + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * Permission to use, copy, modify, and/or distribute this software for
> any
> > + * purpose with or without fee is hereby granted, provided that the
> above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
> FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
> DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
> OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + */
> > +
> > +#include <lc3.h>
> > +
> > +#include "libavutil/intreadwrite.h"
> > +#include "libavutil/opt.h"
> > +
> > +#include "avcodec.h"
> > +#include "codec.h"
> > +#include "codec_internal.h"
> > +#include "encode.h"
> > +
> > +#define ENCODER_MAX_CHANNELS 2
> > +
> > +typedef struct LibLC3EncOpts {
> > + float frame_duration;
> > + bool hr_mode;
> > +} LibLC3EncOpts;
> > +
> > +typedef struct LibLC3EncContext {
> > + AVClass *av_class;
> > + LibLC3EncOpts opts;
> > + int block_bytes;
> > + lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
> > +} LibLC3EncContext;
> > +
> > +static av_cold int liblc3_encode_init(AVCodecContext *avctx)
> > +{
> > + LibLC3EncContext *liblc3 = avctx->priv_data;
> > + bool hr_mode = liblc3->opts.hr_mode;
> > + int frame_us = liblc3->opts.frame_duration * 1000;
> > + int srate_hz = avctx->sample_rate;
> > + int channels = avctx->ch_layout.nb_channels;
> > + int effective_bit_rate;
> > + unsigned encoder_size;
> > +
> > + if (frame_us != 2500 && frame_us != 5000 &&
> > + frame_us != 7500 && frame_us != 10000 ) {
> > + av_log(avctx, AV_LOG_ERROR,
> > + "Unsupported frame duration %.1f ms\n", frame_us / 1e3f);
> > + return AVERROR(EINVAL);
> > + }
> > +
> > + hr_mode |= srate_hz > 48000;
> > + hr_mode &= srate_hz >= 48000;
> > +
> > + if (frame_us <= 5000 || hr_mode)
> > + avctx->codec_id = AV_CODEC_ID_LC3_PLUS;
> > +
> > + if (frame_us == 7500 && hr_mode) {
>
> > + av_log(avctx, AV_LOG_ERROR,
>
> > + "High-reolution mode not supported with 7.5 ms frames\n");
>
> typo
>
> > + return AVERROR(EINVAL);
> > + }
> > +
> > + av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames\n", frame_us /
> 1e3f);
> > + if (hr_mode)
> > + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> > +
> > + liblc3->block_bytes = lc3_hr_frame_block_bytes(
> > + hr_mode, frame_us, srate_hz, channels, avctx->bit_rate);
> > +
> > + effective_bit_rate = lc3_hr_resolve_bitrate(
> > + hr_mode, frame_us, srate_hz, liblc3->block_bytes);
> > +
> > + if (avctx->bit_rate != effective_bit_rate)
> > + av_log(avctx, AV_LOG_WARNING,
> > + "Bitrate changed to %d bps\n", effective_bit_rate);
> > + avctx->bit_rate = effective_bit_rate;
> > +
> > + encoder_size = lc3_hr_encoder_size(frame_us, frame_us, srate_hz);
> > + if (!encoder_size)
> > + return AVERROR(EINVAL);
> > +
> > + for (int ch = 0; ch < channels; ch++) {
>
> > + liblc3->encoder[ch] = lc3_hr_setup_encoder(
> > + hr_mode, frame_us, srate_hz, 0, av_malloc(encoder_size));
>
> not sure if this can fail for different reasons (e.g. if it performs
> validation), if this is the case maybe you want to add an explicit
> check for malloc/ENOMEM
>
> [...]
>
> No more comments from me, thanks.
>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 18:27 ` Andreas Rheinhardt
@ 2024-03-26 19:09 ` Antoine Soulier via ffmpeg-devel
2024-03-26 21:39 ` Andreas Rheinhardt
0 siblings, 1 reply; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 19:09 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
1. Thanks, I will add the caps.
2. Yes, the `lc3_hr_setup_decoder()` returns the given memory address (And
do not write anything when 'NULL').
But bad parameters also return `NULL`, even if parameters are already
validated elsewhere, I will change this call to be more clean.
> "What makes you believe that this is the only error that can happen?"
The "lc3_hr_encoder_size()" validates the other parameters.
3. Yes, I will.
For the change of the codec ID, I did not know how to implement it.
There are 2 codecs covered:
- LC3, standardized by the Bluetooth SIG, free to use over Bluetooth, and
should only be used over Bluetooth.
- LC3plus, a sibling of LC3, defined by another standard, (ETSI TS 103
634), that can be used anywhere. The chosen parameters conform to one or
other.
Should I declare a second FFCodec and add an option "lc3plus" ?
Thanks.
On Tue, Mar 26, 2024 at 11:28 AM Andreas Rheinhardt <
andreas.rheinhardt@outlook.com> wrote:
> Antoine Soulier via ffmpeg-devel:
> > The LC3 audio codec is the default codec of Bluetooth LE audio.
> > This is a wrapper over the liblc3 library (
> https://github.com/google/liblc3).
> >
> > Signed-off-by: Antoine Soulier <asoulier@google.com>
> > Signed-off-by: Antoine SOULIER <asoulier@google.com>
> > ---
> > libavcodec/Makefile | 3 +
> > libavcodec/allcodecs.c | 3 +
> > libavcodec/codec_desc.c | 14 +++
> > libavcodec/codec_id.h | 2 +
> > libavcodec/liblc3dec.c | 146 ++++++++++++++++++++++++++++++
> > libavcodec/liblc3enc.c | 191 ++++++++++++++++++++++++++++++++++++++++
> > 6 files changed, 359 insertions(+)
> > create mode 100644 libavcodec/liblc3dec.c
> > create mode 100644 libavcodec/liblc3enc.c
> >
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> > index 708434ac76..7d2cf3076d 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -1123,6 +1123,9 @@ OBJS-$(CONFIG_LIBILBC_ENCODER) +=
> libilbc.o
> > OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o
> > OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o
> > OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o
> > +OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
> > +OBJS-$(CONFIG_LIBLC3_LC3_DECODER) += liblc3dec.o
> > +OBJS-$(CONFIG_LIBLC3_LC3PLUS_DECODER) += liblc3dec.o
> > OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
> > OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
> > OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
> > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> > index 2386b450a6..29aedaeac6 100644
> > --- a/libavcodec/allcodecs.c
> > +++ b/libavcodec/allcodecs.c
> > @@ -776,6 +776,9 @@ extern const FFCodec ff_libilbc_encoder;
> > extern const FFCodec ff_libilbc_decoder;
> > extern const FFCodec ff_libjxl_decoder;
> > extern const FFCodec ff_libjxl_encoder;
> > +extern const FFCodec ff_liblc3_encoder;
> > +extern const FFCodec ff_liblc3_lc3_decoder;
> > +extern const FFCodec ff_liblc3_lc3plus_decoder;
> > extern const FFCodec ff_libmp3lame_encoder;
> > extern const FFCodec ff_libopencore_amrnb_encoder;
> > extern const FFCodec ff_libopencore_amrnb_decoder;
> > diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> > index 3bab86db62..230bba2a09 100644
> > --- a/libavcodec/codec_desc.c
> > +++ b/libavcodec/codec_desc.c
> > @@ -3425,6 +3425,20 @@ static const AVCodecDescriptor
> codec_descriptors[] = {
> > .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"),
> > .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> > },
> > + {
> > + .id = AV_CODEC_ID_LC3,
> > + .type = AVMEDIA_TYPE_AUDIO,
> > + .name = "lc3",
> > + .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_LC3_PLUS,
> > + .type = AVMEDIA_TYPE_AUDIO,
> > + .name = "lc3_plus",
> > + .long_name = NULL_IF_CONFIG_SMALL("LC3plus (Low Complexity
> Communication Codec plus)"),
> > + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> > + },
> >
> > /* subtitle codecs */
> > {
> > diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> > index c8dc21da74..7e4cb39049 100644
> > --- a/libavcodec/codec_id.h
> > +++ b/libavcodec/codec_id.h
> > @@ -543,6 +543,8 @@ enum AVCodecID {
> > AV_CODEC_ID_AC4,
> > AV_CODEC_ID_OSQ,
> > AV_CODEC_ID_QOA,
> > + AV_CODEC_ID_LC3,
> > + AV_CODEC_ID_LC3_PLUS,
> >
> > /* subtitle codecs */
> > AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID
> pointing at the start of subtitle codecs.
> > diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
> > new file mode 100644
> > index 0000000000..e97cecc68f
> > --- /dev/null
> > +++ b/libavcodec/liblc3dec.c
> > @@ -0,0 +1,146 @@
> > +/*
> > + * LC3 decoder wrapper
> > + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * Permission to use, copy, modify, and/or distribute this software for
> any
> > + * purpose with or without fee is hereby granted, provided that the
> above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
> FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
> DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
> OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + */
> > +
> > +#include <lc3.h>
> > +
> > +#include "libavutil/intreadwrite.h"
> > +
> > +#include "avcodec.h"
> > +#include "codec.h"
> > +#include "codec_internal.h"
> > +#include "decode.h"
> > +#include "internal.h"
> > +
> > +#define DECODER_MAX_CHANNELS 2
> > +
> > +typedef struct LibLC3DecContext {
> > + int frame_us, srate_hz;
> > + bool hr_mode;
> > + lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
> > +} LibLC3DecContext;
> > +
> > +static av_cold int liblc3_decode_init(AVCodecContext *avctx)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > + unsigned decoder_size;
> > +
> > + if (avctx->extradata_size < 2)
> > + return AVERROR_INVALIDDATA;
> > +
> > + liblc3->frame_us = AV_RL16(avctx->extradata + 0);
> > + liblc3->srate_hz = avctx->sample_rate;
> > + liblc3->hr_mode = avctx->extradata_size >= 6 &&
> > + AV_RL16(avctx->extradata + 4);
> > +
> > + av_log(avctx, AV_LOG_INFO,
> > + "Decoding %.1f ms frames\n", liblc3->frame_us / 1e3f);
> > + if (liblc3->hr_mode)
> > + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> > +
> > + decoder_size = lc3_hr_decoder_size(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + if (!decoder_size)
> > + return AVERROR_INVALIDDATA;
> > +
> > + for (int ch = 0; ch < channels; ch++) {
> > + liblc3->decoder[ch] = lc3_hr_setup_decoder(liblc3->hr_mode,
> > + liblc3->frame_us, liblc3->srate_hz, 0,
> av_malloc(decoder_size));
>
> 1. Leak on error (if this fails for the second channel, then the first
> channel's buffer will not be freed). Use FF_CODEC_CAP_INIT_CLEANUP for
> that.
> 2. Shouldn't there be a check for that av_malloc or is the idea that
> lc3_hr_setup_decoder() checks for us?
> 3. You can allocate the decoders jointly: ptr =
> av_malloc_array(decoder_size, channels) and then use (char*)ptr + ch *
> decoder_size as context for the decoder for channel ch.
> liblc3->decoder[0] would be the actually allocated pointer for freeing.
>
> > + if (!liblc3->decoder[ch])
> > + return AVERROR(ENOMEM);
>
> What makes you believe that this is the only error that can happen?
>
> > + }
> > +
> > + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
> > + avctx->delay = lc3_hr_delay_samples(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + avctx->internal->skip_samples = avctx->delay;
> > +
> > + return 0;
> > +}
> > +
> > +static av_cold int liblc3_decode_close(AVCodecContext *avctx)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > +
> > + for (int ch = 0; ch < channels; ch++)
> > + av_free(liblc3->decoder[ch]);
> > +
> > + return 0;
> > +}
> > +
> > +static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
> > + int *got_frame_ptr, AVPacket *avpkt)
> > +{
> > + LibLC3DecContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > + uint8_t *in = avpkt->data;
> > + int block_bytes, ret;
> > +
> > + frame->nb_samples = lc3_hr_frame_samples(
> > + liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz);
> > + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> > + return ret;
> > +
> > + block_bytes = avpkt->size;
> > + for (int ch = 0; ch < channels; ch++) {
> > + int frame_bytes = block_bytes / channels
> > + + (ch < block_bytes % channels);
> > +
> > +
> > + ret = lc3_decode(liblc3->decoder[ch], in, frame_bytes,
> > + LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
> > + if (ret < 0)
> > + return AVERROR_INVALIDDATA;
> > +
> > + in += frame_bytes;
> > + }
> > +
> > + frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
> > +
> > + *got_frame_ptr = 1;
> > +
> > + return avpkt->size;
> > +}
> > +
> > +const FFCodec ff_liblc3_lc3_decoder = {
> > + .p.name = "liblc3",
> > + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> > + .p.type = AVMEDIA_TYPE_AUDIO,
> > + .p.id = AV_CODEC_ID_LC3,
> > + .p.capabilities = AV_CODEC_CAP_DR1,
> > + .p.wrapper_name = "liblc3",
> > + .priv_data_size = sizeof(LibLC3DecContext),
> > + .init = liblc3_decode_init,
> > + .close = liblc3_decode_close,
> > + FF_CODEC_DECODE_CB(liblc3_decode),
> > +};
> > +
> > +const FFCodec ff_liblc3_lc3plus_decoder = {
> > + .p.name = "liblc3",
> > + CODEC_LONG_NAME("LC3plus (Low Complexity Communication Codec
> plus)"),
> > + .p.type = AVMEDIA_TYPE_AUDIO,
> > + .p.id = AV_CODEC_ID_LC3_PLUS,
> > + .p.capabilities = AV_CODEC_CAP_DR1,
> > + .p.wrapper_name = "liblc3",
> > + .priv_data_size = sizeof(LibLC3DecContext),
> > + .init = liblc3_decode_init,
> > + .close = liblc3_decode_close,
> > + FF_CODEC_DECODE_CB(liblc3_decode),
> > +};
> > diff --git a/libavcodec/liblc3enc.c b/libavcodec/liblc3enc.c
> > new file mode 100644
> > index 0000000000..6495742c04
> > --- /dev/null
> > +++ b/libavcodec/liblc3enc.c
> > @@ -0,0 +1,191 @@
> > +/*
> > + * LC3 encoder wrapper
> > + * Copyright (C) 2024 Antoine Soulier <asoulier@google.com>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * Permission to use, copy, modify, and/or distribute this software for
> any
> > + * purpose with or without fee is hereby granted, provided that the
> above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
> FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
> DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
> OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + */
> > +
> > +#include <lc3.h>
> > +
> > +#include "libavutil/intreadwrite.h"
> > +#include "libavutil/opt.h"
> > +
> > +#include "avcodec.h"
> > +#include "codec.h"
> > +#include "codec_internal.h"
> > +#include "encode.h"
> > +
> > +#define ENCODER_MAX_CHANNELS 2
> > +
> > +typedef struct LibLC3EncOpts {
> > + float frame_duration;
> > + bool hr_mode;
> > +} LibLC3EncOpts;
> > +
> > +typedef struct LibLC3EncContext {
> > + AVClass *av_class;
> > + LibLC3EncOpts opts;
> > + int block_bytes;
> > + lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
> > +} LibLC3EncContext;
> > +
> > +static av_cold int liblc3_encode_init(AVCodecContext *avctx)
> > +{
> > + LibLC3EncContext *liblc3 = avctx->priv_data;
> > + bool hr_mode = liblc3->opts.hr_mode;
> > + int frame_us = liblc3->opts.frame_duration * 1000;
> > + int srate_hz = avctx->sample_rate;
> > + int channels = avctx->ch_layout.nb_channels;
> > + int effective_bit_rate;
> > + unsigned encoder_size;
> > +
> > + if (frame_us != 2500 && frame_us != 5000 &&
> > + frame_us != 7500 && frame_us != 10000 ) {
> > + av_log(avctx, AV_LOG_ERROR,
> > + "Unsupported frame duration %.1f ms\n", frame_us / 1e3f);
> > + return AVERROR(EINVAL);
> > + }
> > +
> > + hr_mode |= srate_hz > 48000;
> > + hr_mode &= srate_hz >= 48000;
> > +
> > + if (frame_us <= 5000 || hr_mode)
> > + avctx->codec_id = AV_CODEC_ID_LC3_PLUS;
>
> The encoder has to encode the format it advertises (via AVCodec.id); it
> must not change it.
> (What is actually the point of this using two different codec ids? You
> do not even need to tell the libraries whether you are decoding/encoding
> the +-version or not.)
>
> > +
> > + if (frame_us == 7500 && hr_mode) {
> > + av_log(avctx, AV_LOG_ERROR,
> > + "High-reolution mode not supported with 7.5 ms frames\n");
> > + return AVERROR(EINVAL);
> > + }
> > +
> > + av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames\n", frame_us /
> 1e3f);
> > + if (hr_mode)
> > + av_log(avctx, AV_LOG_INFO, "High-resolution mode enabled\n");
> > +
> > + liblc3->block_bytes = lc3_hr_frame_block_bytes(
> > + hr_mode, frame_us, srate_hz, channels, avctx->bit_rate);
> > +
> > + effective_bit_rate = lc3_hr_resolve_bitrate(
> > + hr_mode, frame_us, srate_hz, liblc3->block_bytes);
> > +
> > + if (avctx->bit_rate != effective_bit_rate)
> > + av_log(avctx, AV_LOG_WARNING,
> > + "Bitrate changed to %d bps\n", effective_bit_rate);
> > + avctx->bit_rate = effective_bit_rate;
> > +
> > + encoder_size = lc3_hr_encoder_size(frame_us, frame_us, srate_hz);
> > + if (!encoder_size)
> > + return AVERROR(EINVAL);
> > +
> > + for (int ch = 0; ch < channels; ch++) {
> > + liblc3->encoder[ch] = lc3_hr_setup_encoder(
> > + hr_mode, frame_us, srate_hz, 0, av_malloc(encoder_size));
> > + if (!liblc3->encoder[ch])
> > + return AVERROR(ENOMEM);
> > + }
> > +
> > + avctx->extradata = av_mallocz(6);
>
> Padding
>
> > + if (!avctx->extradata)
> > + return AVERROR(ENOMEM);
> > +
> > + AV_WL16(avctx->extradata + 0, frame_us);
> > + AV_WL16(avctx->extradata + 2, 0);
> > + AV_WL16(avctx->extradata + 4, hr_mode);
> > + avctx->extradata_size = 6;
> > +
> > + avctx->frame_size = lc3_hr_frame_samples(hr_mode, frame_us,
> srate_hz);
> > +
> > + return 0;
> > +}
> > +
> > +static av_cold int liblc3_encode_close(AVCodecContext *avctx)
> > +{
> > + LibLC3EncContext *liblc3 = avctx->priv_data;
> > + int channels = avctx->ch_layout.nb_channels;
> > +
> > + for (int ch = 0; ch < channels; ch++)
> > + av_free(liblc3->encoder[ch]);
> > +
> > + return 0;
> > +}
> > +
> > +static int liblc3_encode(AVCodecContext *avctx, AVPacket *avpkt,
> > + const AVFrame *av_frame, int *got_packet_ptr)
> > +{
> > + LibLC3EncContext *liblc3 = avctx->priv_data;
> > + int block_bytes = liblc3->block_bytes;
> > + int channels = avctx->ch_layout.nb_channels;
> > + uint8_t *data_ptr;
> > + int ret;
> > +
> > + if ((ret = ff_alloc_packet(avctx, avpkt, block_bytes)) < 0)
> > + return ret;
> > +
> > + data_ptr = avpkt->data;
> > + for (int ch = 0; ch < channels; ch++) {
> > + int frame_bytes = block_bytes / channels
> > + + (ch < block_bytes % channels);
> > +
> > + lc3_encode(liblc3->encoder[ch],
> > + LC3_PCM_FORMAT_FLOAT, av_frame->data[ch], 1,
> > + frame_bytes, data_ptr);
> > +
> > + data_ptr += frame_bytes;
> > + }
> > +
> > + *got_packet_ptr = 1;
> > +
> > + return 0;
> > +}
> > +
> > +#define OFFSET(x) offsetof(LibLC3EncContext, opts.x)
> > +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> > +static const AVOption options[] = {
> > + { "frame_duration", "Duration of a frame in milliseconds",
> > + OFFSET(frame_duration), AV_OPT_TYPE_FLOAT,
> > + { .dbl = 10.0 }, 2.5, 10.0, FLAGS },
> > + { "high_resolution", "Enable High-Resolution mode (48 KHz or 96
> KHz)",
> > + OFFSET(hr_mode), AV_OPT_TYPE_BOOL,
> > + { .i64 = 0 }, 0, 1, FLAGS },
> > + { NULL }
> > +};
> > +
> > +static const AVClass class = {
> > + .class_name = "liblc3 encoder",
> > + .item_name = av_default_item_name,
> > + .option = options,
> > + .version = LIBAVUTIL_VERSION_INT,
> > +};
> > +
> > +const FFCodec ff_liblc3_encoder = {
> > + .p.name = "liblc3",
> > + CODEC_LONG_NAME("LC3 (Low Complexity Communication Codec)"),
> > + .p.type = AVMEDIA_TYPE_AUDIO,
> > + .p.id = AV_CODEC_ID_LC3,
> > + .p.capabilities = AV_CODEC_CAP_DR1,
> > + .p.ch_layouts = (const AVChannelLayout[])
> > + { { AV_CHANNEL_ORDER_UNSPEC, 1 },
> > + { AV_CHANNEL_ORDER_UNSPEC, 2 }, { 0 } },
> > + .p.supported_samplerates = (const int [])
> > + { 96000, 48000, 32000, 24000, 16000, 8000, 0 },
> > + .p.sample_fmts = (const enum AVSampleFormat[])
> > + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE },
> > + .p.priv_class = &class,
> > + .p.wrapper_name = "liblc3",
> > + .priv_data_size = sizeof(LibLC3EncContext),
> > + .init = liblc3_encode_init,
> > + .close = liblc3_encode_close,
> > + FF_CODEC_ENCODE_CB(liblc3_encode),
> > +};
>
> _______________________________________________
> 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".
>
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
2024-03-26 19:09 ` Antoine Soulier via ffmpeg-devel
@ 2024-03-26 21:39 ` Andreas Rheinhardt
0 siblings, 0 replies; 22+ messages in thread
From: Andreas Rheinhardt @ 2024-03-26 21:39 UTC (permalink / raw)
To: ffmpeg-devel
Antoine Soulier via ffmpeg-devel:
> 1. Thanks, I will add the caps.
> 2. Yes, the `lc3_hr_setup_decoder()` returns the given memory address (And
> do not write anything when 'NULL').
> But bad parameters also return `NULL`, even if parameters are already
> validated elsewhere, I will change this call to be more clean.
>> "What makes you believe that this is the only error that can happen?"
> The "lc3_hr_encoder_size()" validates the other parameters.
> 3. Yes, I will.
>
> For the change of the codec ID, I did not know how to implement it.
> There are 2 codecs covered:
> - LC3, standardized by the Bluetooth SIG, free to use over Bluetooth, and
> should only be used over Bluetooth.
> - LC3plus, a sibling of LC3, defined by another standard, (ETSI TS 103
> 634), that can be used anywhere. The chosen parameters conform to one or
> other.
> Should I declare a second FFCodec and add an option "lc3plus" ?
>
1. Stop top-posting.
2. I would rather use only one codec id. There is no point in
distinguishing them on the AVCodecID level.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation
2024-03-26 23:07 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
@ 2024-03-27 11:59 ` Stefano Sabatini
0 siblings, 0 replies; 22+ messages in thread
From: Stefano Sabatini @ 2024-03-27 11:59 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Tuesday 2024-03-26 23:07:56 +0000, ffmpeg-devel Mailing List wrote:
> Signed-off-by: Antoine Soulier <asoulier@google.com>
> ---
> doc/encoders.texi | 57 +++++++++++++++++++++++++++++++++++++++
> doc/general_contents.texi | 11 +++++++-
> doc/muxers.texi | 6 +++++
> 3 files changed, 73 insertions(+), 1 deletion(-)
>
> diff --git a/doc/encoders.texi b/doc/encoders.texi
> index 7c223ed74c..0719ba13cc 100644
> --- a/doc/encoders.texi
> +++ b/doc/encoders.texi
> @@ -814,6 +814,63 @@ ffmpeg -i input.wav -c:a libfdk_aac -profile:a aac_he -b:a 64k output.m4a
> @end example
> @end itemize
>
> +@anchor{liblc3-enc}
> +@section liblc3
> +
> +liblc3 LC3 (Low Complexity Communication Codec) encoder wrapper.
> +
> +Requires the presence of the liblc3 headers and library during configuration.
> +You need to explicitly configure the build with @code{--enable-liblc3}.
> +
> +This encoder has support for the Bluetooth SIG LC3 codec for the LE Audio
> +protocol, and the following features of LC3plus:
> +@itemize @minus
nit: you can skip @minus (I don't think it's much used in other
places)
> +@item
> +Frame duration of 2.5 and 5ms.
> +@item
> +High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
> +@end itemize
> +
> +For more information see the liblc3 project at
> +@url{https://github.com/google/liblc3}.
> +
> +@subsection Options
> +
> +The following options are mapped on the shared FFmpeg codec options.
> +
> +@table @option
> +@item b
nit: here and below you can use:
@item b @var{bitrate}
to clarify the argument content
> +Set the bit rate in bits/s. This will determine the fixed size of the encoded
> +frames, for a selected frame duration.
> +
> +@item ar
> +Set the audio sampling rate (in Hz).
> +
> +@item channels
> +Set the number of audio channels.
> +
> +@item frame_duration
> +Set the audio frame duration in milliseconds. Default value is 10ms.
> +Allowed frame durations are 2.5ms, 5ms, 7.5ms and 10ms.
> +LC3 (Bluetooth LE Audio), allows 7.5ms and 10ms; and LC3plus 2.5ms, 5ms
> +and 10ms.
> +
> +The 10ms frame duration is available in LC3 and LC3 plus standard.
> +In this mode, the produced bitstream can be referenced either as LC3 or LC3plus.
> +
> +@item high_resolution
in this case it would be @var{bool}
> +Enable the high-resolution mode if set to 1. The high-resolution mode is
> +available with all LC3plus frame durations and for a sampling rate 48 KHz,
something is missing, for a sampling rate _of_ 48 KHz ?
> +and 96 KHz.
> +
> +The encoder automatically turns off this mode at lower sampling rates and
> +activates it at 96 KHz.
> +
> +This mode should be preferred at high bitrates. In this mode, the audio
> +bandwidth is always up to the Nyquist frequency, compared to LC3 at 48 KHz,
> +which limits the bandwidth to 20 KHz.
> +@end table
> +
> @anchor{libmp3lame}
> @section libmp3lame
>
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index f269cbd1a9..e7cf4f8239 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -237,6 +237,14 @@ Go to @url{http://sourceforge.net/projects/opencore-amr/} and follow the
> instructions for installing the library.
> Then pass @code{--enable-libfdk-aac} to configure to enable it.
>
> +@subsection LC3 library
> +
> +FFmpeg can make use of the Google LC3 library for LC3 decoding & encoding.
> +
> +Go to @url{https://github.com/google/liblc3/} and follow the instructions for
> +installing the library.
> +Then pass @code{--enable-liblc3} to configure to enable it.
> +
> @section OpenH264
>
> FFmpeg can make use of the OpenH264 library for H.264 decoding and encoding.
> @@ -1300,7 +1308,8 @@ following image formats are supported:
> @tab encoding and decoding supported through external library libilbc
> @item IMC (Intel Music Coder) @tab @tab X
> @item Interplay ACM @tab @tab X
> -@item MACE (Macintosh Audio Compression/Expansion) 3:1 @tab @tab X
> +@item LC3 @tab E @tab E
> + @tab supported through external library liblc3
> @item MACE (Macintosh Audio Compression/Expansion) 6:1 @tab @tab X
> @item Marian's A-pac audio @tab @tab X
> @item MI-SC4 (Micronas SC-4 Audio) @tab @tab X
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index a10a8e216f..9687746c30 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -2612,6 +2612,12 @@ WebDAV server every second:
> ffmpeg -f x11grab -framerate 1 -i :0.0 -q:v 6 -update 1 -protocol_opts method=PUT http://example.com/desktop.jpg
> @end example
>
> +@section lc3
> +Bluetooth SIG Low Complexity Communication Codec audio (LC3), or
> +ETSI TS 103 634 Low Complexity Communication Codec plus (LC3plus).
> +
> +This muxer accepts a single @code{lc3} audio stream.
> +
> @section matroska
LGTM otherwise, thanks.
Note: you might move the doc bits to the relevent functional changes
and drop this patch.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
* [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation
2024-03-26 23:07 Antoine Soulier via ffmpeg-devel
@ 2024-03-26 23:07 ` Antoine Soulier via ffmpeg-devel
2024-03-27 11:59 ` Stefano Sabatini
0 siblings, 1 reply; 22+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-26 23:07 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
Signed-off-by: Antoine Soulier <asoulier@google.com>
---
doc/encoders.texi | 57 +++++++++++++++++++++++++++++++++++++++
doc/general_contents.texi | 11 +++++++-
doc/muxers.texi | 6 +++++
3 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..0719ba13cc 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -814,6 +814,63 @@ ffmpeg -i input.wav -c:a libfdk_aac -profile:a aac_he -b:a 64k output.m4a
@end example
@end itemize
+@anchor{liblc3-enc}
+@section liblc3
+
+liblc3 LC3 (Low Complexity Communication Codec) encoder wrapper.
+
+Requires the presence of the liblc3 headers and library during configuration.
+You need to explicitly configure the build with @code{--enable-liblc3}.
+
+This encoder has support for the Bluetooth SIG LC3 codec for the LE Audio
+protocol, and the following features of LC3plus:
+@itemize @minus
+@item
+Frame duration of 2.5 and 5ms.
+@item
+High-Resolution mode, 48 KHz, and 96 kHz sampling rates.
+@end itemize
+
+For more information see the liblc3 project at
+@url{https://github.com/google/liblc3}.
+
+@subsection Options
+
+The following options are mapped on the shared FFmpeg codec options.
+
+@table @option
+@item b
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar
+Set the audio sampling rate (in Hz).
+
+@item channels
+Set the number of audio channels.
+
+@item frame_duration
+Set the audio frame duration in milliseconds. Default value is 10ms.
+Allowed frame durations are 2.5ms, 5ms, 7.5ms and 10ms.
+LC3 (Bluetooth LE Audio), allows 7.5ms and 10ms; and LC3plus 2.5ms, 5ms
+and 10ms.
+
+The 10ms frame duration is available in LC3 and LC3 plus standard.
+In this mode, the produced bitstream can be referenced either as LC3 or LC3plus.
+
+@item high_resolution
+Enable the high-resolution mode if set to 1. The high-resolution mode is
+available with all LC3plus frame durations and for a sampling rate 48 KHz,
+and 96 KHz.
+
+The encoder automatically turns off this mode at lower sampling rates and
+activates it at 96 KHz.
+
+This mode should be preferred at high bitrates. In this mode, the audio
+bandwidth is always up to the Nyquist frequency, compared to LC3 at 48 KHz,
+which limits the bandwidth to 20 KHz.
+@end table
+
@anchor{libmp3lame}
@section libmp3lame
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index f269cbd1a9..e7cf4f8239 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -237,6 +237,14 @@ Go to @url{http://sourceforge.net/projects/opencore-amr/} and follow the
instructions for installing the library.
Then pass @code{--enable-libfdk-aac} to configure to enable it.
+@subsection LC3 library
+
+FFmpeg can make use of the Google LC3 library for LC3 decoding & encoding.
+
+Go to @url{https://github.com/google/liblc3/} and follow the instructions for
+installing the library.
+Then pass @code{--enable-liblc3} to configure to enable it.
+
@section OpenH264
FFmpeg can make use of the OpenH264 library for H.264 decoding and encoding.
@@ -1300,7 +1308,8 @@ following image formats are supported:
@tab encoding and decoding supported through external library libilbc
@item IMC (Intel Music Coder) @tab @tab X
@item Interplay ACM @tab @tab X
-@item MACE (Macintosh Audio Compression/Expansion) 3:1 @tab @tab X
+@item LC3 @tab E @tab E
+ @tab supported through external library liblc3
@item MACE (Macintosh Audio Compression/Expansion) 6:1 @tab @tab X
@item Marian's A-pac audio @tab @tab X
@item MI-SC4 (Micronas SC-4 Audio) @tab @tab X
diff --git a/doc/muxers.texi b/doc/muxers.texi
index a10a8e216f..9687746c30 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -2612,6 +2612,12 @@ WebDAV server every second:
ffmpeg -f x11grab -framerate 1 -i :0.0 -q:v 6 -update 1 -protocol_opts method=PUT http://example.com/desktop.jpg
@end example
+@section lc3
+Bluetooth SIG Low Complexity Communication Codec audio (LC3), or
+ETSI TS 103 634 Low Complexity Communication Codec plus (LC3plus).
+
+This muxer accepts a single @code{lc3} audio stream.
+
@section matroska
Matroska container muxer.
--
2.44.0.396.g6e790dbe36-goog
_______________________________________________
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".
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2024-03-27 12:05 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-26 16:47 [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Antoine Soulier via ffmpeg-devel
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 2/5] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-26 17:49 ` Stefano Sabatini
2024-03-26 18:50 ` Antoine Soulier via ffmpeg-devel
2024-03-26 18:27 ` Andreas Rheinhardt
2024-03-26 19:09 ` Antoine Soulier via ffmpeg-devel
2024-03-26 21:39 ` Andreas Rheinhardt
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 3/5] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-03-26 17:59 ` Stefano Sabatini
2024-03-26 18:33 ` Andreas Rheinhardt
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
2024-03-26 18:17 ` Stefano Sabatini
2024-03-26 16:47 ` [FFmpeg-devel] [PATCH 5/5] Changelog: Add LC3/LC3plus decoding/encoding support Antoine Soulier via ffmpeg-devel
2024-03-26 18:01 ` Stefano Sabatini
2024-03-26 16:58 ` [FFmpeg-devel] [PATCH 1/5] configure: Add option for enabling LC3/LC3plus wrapper Paul B Mahol
2024-03-26 17:07 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:44 ` Paul B Mahol
2024-03-26 17:57 ` Antoine Soulier via ffmpeg-devel
2024-03-26 17:31 ` Stefano Sabatini
2024-03-26 17:35 ` Antoine Soulier via ffmpeg-devel
2024-03-26 23:07 Antoine Soulier via ffmpeg-devel
2024-03-26 23:07 ` [FFmpeg-devel] [PATCH 4/5] doc: Add LC3/LC3plus muxer and encoder parameters documentation Antoine Soulier via ffmpeg-devel
2024-03-27 11:59 ` Stefano Sabatini
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git