* [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
@ 2024-03-28 21:35 Antoine Soulier via ffmpeg-devel
2024-03-28 21:35 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-28 21:35 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>
---
Changelog | 4 +
configure | 6 ++
doc/encoders.texi | 57 ++++++++++++
doc/general_contents.texi | 11 ++-
libavcodec/Makefile | 2 +
libavcodec/allcodecs.c | 2 +
libavcodec/codec_desc.c | 7 ++
libavcodec/codec_id.h | 1 +
libavcodec/liblc3dec.c | 135 +++++++++++++++++++++++++++
libavcodec/liblc3enc.c | 190 ++++++++++++++++++++++++++++++++++++++
10 files changed, 414 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/liblc3dec.c
create mode 100644 libavcodec/liblc3enc.c
diff --git a/Changelog b/Changelog
index e83a00e35c..83a4cf7888 100644
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,10 @@
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
+
+
version 7.0:
- DXV DXT1 encoder
- LEAD MCMP decoder
diff --git a/configure b/configure
index 2d46ef0b9c..e5d9ba9f53 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
@@ -3499,6 +3501,9 @@ libilbc_encoder_deps="libilbc"
libjxl_decoder_deps="libjxl libjxl_threads"
libjxl_encoder_deps="libjxl libjxl_threads"
libkvazaar_encoder_deps="libkvazaar"
+liblc3_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"
@@ -6869,6 +6874,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
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..66847191e1 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
+@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 @var{bitrate}
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar @var{frequency}
+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 @var{boolean}
+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 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/libavcodec/Makefile b/libavcodec/Makefile
index 9ce6d445c1..e70811dbd6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1123,6 +1123,8 @@ 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_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..f4705651fb 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -776,6 +776,8 @@ 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_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..7dba61dc8b 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3425,6 +3425,13 @@ 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,
+ },
/* subtitle codecs */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index c8dc21da74..0ab1e34a61 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -543,6 +543,7 @@ enum AVCodecID {
AV_CODEC_ID_AC4,
AV_CODEC_ID_OSQ,
AV_CODEC_ID_QOA,
+ AV_CODEC_ID_LC3,
/* 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..3f747e70c9
--- /dev/null
+++ b/libavcodec/liblc3dec.c
@@ -0,0 +1,135 @@
+/*
+ * 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;
+ void *decoder_mem;
+ 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 / 1000.f);
+ 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;
+
+ liblc3->decoder_mem = av_malloc_array(channels, decoder_size);
+ if (!liblc3->decoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->decoder[ch] = lc3_hr_setup_decoder(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz, 0,
+ (char *)liblc3->decoder_mem + ch * decoder_size);
+ }
+
+ 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;
+
+ av_freep(&liblc3->decoder_mem);
+
+ 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 = av_rescale(
+ liblc3->frame_us, liblc3->srate_hz, 1000*1000);
+ 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_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),
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+ .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..9a0953e960
--- /dev/null
+++ b/libavcodec/liblc3enc.c
@@ -0,0 +1,190 @@
+/*
+ * 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;
+ int hr_mode;
+} LibLC3EncOpts;
+
+typedef struct LibLC3EncContext {
+ const AVClass *av_class;
+ LibLC3EncOpts opts;
+ int block_bytes;
+ void *encoder_mem;
+ 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 / 1000.f);
+ return AVERROR(EINVAL);
+ }
+
+ hr_mode |= srate_hz > 48000;
+ hr_mode &= srate_hz >= 48000;
+
+ if (frame_us == 7500 && hr_mode) {
+ av_log(avctx, AV_LOG_ERROR,
+ "High-resolution 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 / 1000.f);
+ 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(hr_mode, frame_us, srate_hz);
+ if (!encoder_size)
+ return AVERROR(EINVAL);
+
+ liblc3->encoder_mem = av_malloc_array(channels, encoder_size);
+ if (!liblc3->encoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->encoder[ch] = lc3_hr_setup_encoder(
+ hr_mode, frame_us, srate_hz, 0,
+ (char *)liblc3->encoder_mem + ch * encoder_size);
+ }
+
+ avctx->extradata = av_mallocz(6 + AV_INPUT_BUFFER_PADDING_SIZE);
+ 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 = av_rescale(frame_us, srate_hz, 1000*1000);
+
+ return 0;
+}
+
+static av_cold int liblc3_encode_close(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+
+ av_freep(&liblc3->encoder_mem);
+
+ 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_get_encode_buffer(avctx, avpkt, block_bytes, 0)) < 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.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-28 21:35 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-03-28 21:35 ` Antoine Soulier via ffmpeg-devel
2024-03-28 21:49 ` Andreas Rheinhardt
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-28 21:35 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>
---
Changelog | 1 +
doc/muxers.texi | 6 ++
libavformat/Makefile | 2 +
libavformat/allformats.c | 2 +
libavformat/lc3dec.c | 135 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 102 +++++++++++++++++++++++++++++
6 files changed, 248 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/Changelog b/Changelog
index 83a4cf7888..08c200a41d 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 demuxer and muxer
- LC3/LC3plus decoding/encoding using external library liblc3
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.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 44aa485029..4961c42852 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,8 @@ 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 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..9ca9825f1b
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,135 @@
+/*
+ * 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 "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;
+ int 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 = 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 = av_rescale(frame_us, srate_hz, 1000*1000);
+
+ lc3->position = 0;
+ lc3->length = st->duration +
+ av_rescale(frame_us == 7500 ? 4000 : 2500, srate_hz, 1000*1000);
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
+ if (ret < 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 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS,
+ .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..a7e3e3f4f7
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,102 @@
+/*
+ * 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 "libavutil/intreadwrite.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;
+ int ep_mode = 0, hr_mode = 0;
+ 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 >= 6) {
+ ep_mode = AV_RL16(st->codecpar->extradata + 2);
+ 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,
+};
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-28 21:35 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-03-28 21:49 ` Andreas Rheinhardt
0 siblings, 0 replies; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-03-28 21:49 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>
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 135 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 102 +++++++++++++++++++++++++++++
> 6 files changed, 248 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/Changelog b/Changelog
> index 83a4cf7888..08c200a41d 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 demuxer and muxer
> - LC3/LC3plus decoding/encoding using external library liblc3
>
>
> 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.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 44aa485029..4961c42852 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,8 @@ 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 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..9ca9825f1b
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,135 @@
> +/*
> + * 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 "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;
> + int 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;
There is nothing that guarantees that the RHS will fit into an uint16_t.
Is there a reason why you are using a more precise timebase for the
extradata than the container format?
> + 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);
Why 32? This format provides no timestamps, so it makes no sense for
them to be wrapped.
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = 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 logmessage
> + 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 = av_rescale(frame_us, srate_hz, 1000*1000);
> +
> + lc3->position = 0;
> + lc3->length = st->duration +
> + av_rescale(frame_us == 7500 ? 4000 : 2500, srate_hz, 1000*1000);
> +
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int ret;
> +
> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
> + if (ret < 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 (Low Complexity Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS,
> + .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..a7e3e3f4f7
> --- /dev/null
> +++ b/libavformat/lc3enc.c
> @@ -0,0 +1,102 @@
> +/*
> + * 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 "libavutil/intreadwrite.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;
> + int ep_mode = 0, hr_mode = 0;
> + 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 >= 6) {
> + ep_mode = AV_RL16(st->codecpar->extradata + 2);
> + hr_mode = AV_RL16(st->codecpar->extradata + 4);
> + }
Given that this is not an established format, we can simply demand that
extradata_size has to be >= 6.
> +
> + 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,
> +};
_______________________________________________
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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
@ 2024-03-28 22:33 Antoine Soulier via ffmpeg-devel
2024-03-28 22:33 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-28 22:33 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>
---
Changelog | 4 +
configure | 6 ++
doc/encoders.texi | 57 ++++++++++++
doc/general_contents.texi | 11 ++-
libavcodec/Makefile | 2 +
libavcodec/allcodecs.c | 2 +
libavcodec/codec_desc.c | 7 ++
libavcodec/codec_id.h | 1 +
libavcodec/liblc3dec.c | 141 ++++++++++++++++++++++++++++
libavcodec/liblc3enc.c | 190 ++++++++++++++++++++++++++++++++++++++
10 files changed, 420 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/liblc3dec.c
create mode 100644 libavcodec/liblc3enc.c
diff --git a/Changelog b/Changelog
index e83a00e35c..83a4cf7888 100644
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,10 @@
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
+
+
version 7.0:
- DXV DXT1 encoder
- LEAD MCMP decoder
diff --git a/configure b/configure
index 2d46ef0b9c..e5d9ba9f53 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
@@ -3499,6 +3501,9 @@ libilbc_encoder_deps="libilbc"
libjxl_decoder_deps="libjxl libjxl_threads"
libjxl_encoder_deps="libjxl libjxl_threads"
libkvazaar_encoder_deps="libkvazaar"
+liblc3_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"
@@ -6869,6 +6874,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
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..66847191e1 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
+@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 @var{bitrate}
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar @var{frequency}
+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 @var{boolean}
+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 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/libavcodec/Makefile b/libavcodec/Makefile
index 9ce6d445c1..e70811dbd6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1123,6 +1123,8 @@ 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_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..f4705651fb 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -776,6 +776,8 @@ 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_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..7dba61dc8b 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3425,6 +3425,13 @@ 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,
+ },
/* subtitle codecs */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index c8dc21da74..0ab1e34a61 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -543,6 +543,7 @@ enum AVCodecID {
AV_CODEC_ID_AC4,
AV_CODEC_ID_OSQ,
AV_CODEC_ID_QOA,
+ AV_CODEC_ID_LC3,
/* 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..32d8fade91
--- /dev/null
+++ b/libavcodec/liblc3dec.c
@@ -0,0 +1,141 @@
+/*
+ * 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;
+ void *decoder_mem;
+ 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;
+ int ep_mode;
+ unsigned decoder_size;
+
+ if (avctx->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ liblc3->frame_us = AV_RL16(avctx->extradata + 0) * 10;
+ liblc3->srate_hz = avctx->sample_rate;
+ ep_mode = AV_RL16(avctx->extradata + 2);
+ liblc3->hr_mode = AV_RL16(avctx->extradata + 4);
+ if (ep_mode != 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Error protection mode is not supported.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO,
+ "Decoding %.1f ms frames.\n", liblc3->frame_us / 1000.f);
+ 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;
+
+ liblc3->decoder_mem = av_malloc_array(channels, decoder_size);
+ if (!liblc3->decoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->decoder[ch] = lc3_hr_setup_decoder(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz, 0,
+ (char *)liblc3->decoder_mem + ch * decoder_size);
+ }
+
+ 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;
+
+ av_freep(&liblc3->decoder_mem);
+
+ 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 = av_rescale(
+ liblc3->frame_us, liblc3->srate_hz, 1000*1000);
+ 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_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),
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+ .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..7d5d92303c
--- /dev/null
+++ b/libavcodec/liblc3enc.c
@@ -0,0 +1,190 @@
+/*
+ * 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;
+ int hr_mode;
+} LibLC3EncOpts;
+
+typedef struct LibLC3EncContext {
+ const AVClass *av_class;
+ LibLC3EncOpts opts;
+ int block_bytes;
+ void *encoder_mem;
+ 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 / 1000.f);
+ return AVERROR(EINVAL);
+ }
+
+ hr_mode |= srate_hz > 48000;
+ hr_mode &= srate_hz >= 48000;
+
+ if (frame_us == 7500 && hr_mode) {
+ av_log(avctx, AV_LOG_ERROR,
+ "High-resolution mode is not supported with 7.5 ms frames.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames.\n", frame_us / 1000.f);
+ if (hr_mode)
+ av_log(avctx, AV_LOG_INFO, "High-resolution mode is 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(hr_mode, frame_us, srate_hz);
+ if (!encoder_size)
+ return AVERROR(EINVAL);
+
+ liblc3->encoder_mem = av_malloc_array(channels, encoder_size);
+ if (!liblc3->encoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->encoder[ch] = lc3_hr_setup_encoder(
+ hr_mode, frame_us, srate_hz, 0,
+ (char *)liblc3->encoder_mem + ch * encoder_size);
+ }
+
+ avctx->extradata = av_mallocz(6 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+
+ AV_WL16(avctx->extradata + 0, frame_us / 10);
+ AV_WL16(avctx->extradata + 2, 0);
+ AV_WL16(avctx->extradata + 4, hr_mode);
+ avctx->extradata_size = 6;
+
+ avctx->frame_size = av_rescale(frame_us, srate_hz, 1000*1000);
+
+ return 0;
+}
+
+static av_cold int liblc3_encode_close(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+
+ av_freep(&liblc3->encoder_mem);
+
+ 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_get_encode_buffer(avctx, avpkt, block_bytes, 0)) < 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.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-28 22:33 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-03-28 22:33 ` Antoine Soulier via ffmpeg-devel
0 siblings, 0 replies; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-28 22:33 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>
---
Changelog | 1 +
doc/muxers.texi | 6 ++
libavformat/Makefile | 2 +
libavformat/allformats.c | 2 +
libavformat/lc3dec.c | 135 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 100 +++++++++++++++++++++++++++++
6 files changed, 246 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/Changelog b/Changelog
index 83a4cf7888..08c200a41d 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 demuxer and muxer
- LC3/LC3plus decoding/encoding using external library liblc3
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.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 44aa485029..4961c42852 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,8 @@ 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 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..c377df4c63
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,135 @@
+/*
+ * 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 "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_10us;
+ uint32_t length;
+ int 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_10us = avio_rl16(s->pb);
+ 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, 64, 1, srate_hz);
+ st->duration = length;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = 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_10us);
+ AV_WL16(st->codecpar->extradata + 2, ep_mode);
+ AV_WL16(st->codecpar->extradata + 4, hr_mode);
+
+ lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
+
+ lc3->position = 0;
+ lc3->length = st->duration +
+ av_rescale(frame_10us == 750 ? 400 : 250, srate_hz, 100*1000);
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
+ if (ret < 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 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS,
+ .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..e768212f63
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "libavutil/intreadwrite.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;
+ int frame_us, ep_mode, hr_mode;
+ uint32_t nb_samples = av_rescale_q(
+ st->duration, st->time_base, (AVRational){ 1, srate_hz });
+
+ if (st->codecpar->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
+ ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
+ hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
+
+ 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,
+};
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
@ 2024-03-29 17:30 Antoine Soulier via ffmpeg-devel
2024-03-29 17:30 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-29 17:30 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>
---
Changelog | 4 +
configure | 6 ++
doc/encoders.texi | 57 ++++++++++++
doc/general_contents.texi | 11 ++-
libavcodec/Makefile | 2 +
libavcodec/allcodecs.c | 2 +
libavcodec/codec_desc.c | 7 ++
libavcodec/codec_id.h | 1 +
libavcodec/liblc3dec.c | 141 ++++++++++++++++++++++++++++
libavcodec/liblc3enc.c | 190 ++++++++++++++++++++++++++++++++++++++
10 files changed, 420 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/liblc3dec.c
create mode 100644 libavcodec/liblc3enc.c
diff --git a/Changelog b/Changelog
index e83a00e35c..83a4cf7888 100644
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,10 @@
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
+
+
version 7.0:
- DXV DXT1 encoder
- LEAD MCMP decoder
diff --git a/configure b/configure
index 2d46ef0b9c..e5d9ba9f53 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
@@ -3499,6 +3501,9 @@ libilbc_encoder_deps="libilbc"
libjxl_decoder_deps="libjxl libjxl_threads"
libjxl_encoder_deps="libjxl libjxl_threads"
libkvazaar_encoder_deps="libkvazaar"
+liblc3_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"
@@ -6869,6 +6874,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
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..66847191e1 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
+@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 @var{bitrate}
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar @var{frequency}
+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 @var{boolean}
+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 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/libavcodec/Makefile b/libavcodec/Makefile
index 9ce6d445c1..e70811dbd6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1123,6 +1123,8 @@ 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_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..f4705651fb 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -776,6 +776,8 @@ 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_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..7dba61dc8b 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3425,6 +3425,13 @@ 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,
+ },
/* subtitle codecs */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index c8dc21da74..0ab1e34a61 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -543,6 +543,7 @@ enum AVCodecID {
AV_CODEC_ID_AC4,
AV_CODEC_ID_OSQ,
AV_CODEC_ID_QOA,
+ AV_CODEC_ID_LC3,
/* 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..32d8fade91
--- /dev/null
+++ b/libavcodec/liblc3dec.c
@@ -0,0 +1,141 @@
+/*
+ * 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;
+ void *decoder_mem;
+ 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;
+ int ep_mode;
+ unsigned decoder_size;
+
+ if (avctx->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ liblc3->frame_us = AV_RL16(avctx->extradata + 0) * 10;
+ liblc3->srate_hz = avctx->sample_rate;
+ ep_mode = AV_RL16(avctx->extradata + 2);
+ liblc3->hr_mode = AV_RL16(avctx->extradata + 4);
+ if (ep_mode != 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Error protection mode is not supported.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO,
+ "Decoding %.1f ms frames.\n", liblc3->frame_us / 1000.f);
+ 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;
+
+ liblc3->decoder_mem = av_malloc_array(channels, decoder_size);
+ if (!liblc3->decoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->decoder[ch] = lc3_hr_setup_decoder(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz, 0,
+ (char *)liblc3->decoder_mem + ch * decoder_size);
+ }
+
+ 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;
+
+ av_freep(&liblc3->decoder_mem);
+
+ 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 = av_rescale(
+ liblc3->frame_us, liblc3->srate_hz, 1000*1000);
+ 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_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),
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+ .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..7d5d92303c
--- /dev/null
+++ b/libavcodec/liblc3enc.c
@@ -0,0 +1,190 @@
+/*
+ * 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;
+ int hr_mode;
+} LibLC3EncOpts;
+
+typedef struct LibLC3EncContext {
+ const AVClass *av_class;
+ LibLC3EncOpts opts;
+ int block_bytes;
+ void *encoder_mem;
+ 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 / 1000.f);
+ return AVERROR(EINVAL);
+ }
+
+ hr_mode |= srate_hz > 48000;
+ hr_mode &= srate_hz >= 48000;
+
+ if (frame_us == 7500 && hr_mode) {
+ av_log(avctx, AV_LOG_ERROR,
+ "High-resolution mode is not supported with 7.5 ms frames.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames.\n", frame_us / 1000.f);
+ if (hr_mode)
+ av_log(avctx, AV_LOG_INFO, "High-resolution mode is 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(hr_mode, frame_us, srate_hz);
+ if (!encoder_size)
+ return AVERROR(EINVAL);
+
+ liblc3->encoder_mem = av_malloc_array(channels, encoder_size);
+ if (!liblc3->encoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->encoder[ch] = lc3_hr_setup_encoder(
+ hr_mode, frame_us, srate_hz, 0,
+ (char *)liblc3->encoder_mem + ch * encoder_size);
+ }
+
+ avctx->extradata = av_mallocz(6 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+
+ AV_WL16(avctx->extradata + 0, frame_us / 10);
+ AV_WL16(avctx->extradata + 2, 0);
+ AV_WL16(avctx->extradata + 4, hr_mode);
+ avctx->extradata_size = 6;
+
+ avctx->frame_size = av_rescale(frame_us, srate_hz, 1000*1000);
+
+ return 0;
+}
+
+static av_cold int liblc3_encode_close(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+
+ av_freep(&liblc3->encoder_mem);
+
+ 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_get_encode_buffer(avctx, avpkt, block_bytes, 0)) < 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.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-29 17:30 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-03-29 17:30 ` Antoine Soulier via ffmpeg-devel
2024-03-30 11:45 ` Paul B Mahol
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-29 17:30 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>
---
Changelog | 1 +
doc/muxers.texi | 6 ++
libavformat/Makefile | 2 +
libavformat/allformats.c | 2 +
libavformat/lc3dec.c | 164 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
6 files changed, 275 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/Changelog b/Changelog
index 83a4cf7888..08c200a41d 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 demuxer and muxer
- LC3/LC3plus decoding/encoding using external library liblc3
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.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 44aa485029..4961c42852 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,8 @@ 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 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..74d6794d00
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,164 @@
+/*
+ * 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 "libavcodec/packet.h"
+#include "libavutil/intreadwrite.h"
+
+#include "avformat.h"
+#include "avio.h"
+#include "demux.h"
+#include "internal.h"
+
+typedef struct LC3DemuxContext {
+ int dt, delay;
+ int64_t timestamp;
+ 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_10us;
+ uint32_t length;
+ int 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_10us = avio_rl16(s->pb);
+ 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, 64, 1, srate_hz);
+ st->start_time = 0;
+ st->duration = length;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = 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_10us);
+ AV_WL16(st->codecpar->extradata + 2, ep_mode);
+ AV_WL16(st->codecpar->extradata + 4, hr_mode);
+
+ lc3->dt = av_rescale(frame_10us, srate_hz, 100*1000);
+ lc3->delay = av_rescale(frame_10us == 750 ? 400 : 250, srate_hz, 100*1000);
+
+ lc3->timestamp = -lc3->delay;
+ lc3->length = st->duration;
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
+ if (ret < 0)
+ return ret;
+
+ pkt->pos -= 2;
+ pkt->pts = FFMAX(lc3->timestamp, 0);
+ pkt->duration = lc3->dt;
+
+ lc3->timestamp += lc3->dt;
+ if (lc3->timestamp > lc3->length) {
+ pkt->duration -= lc3->timestamp - lc3->length;
+ lc3->timestamp = lc3->length;
+ }
+
+ return 0;
+}
+
+static int lc3_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
+{
+ AVStream *st = s->streams[stream_index];
+ FFStream *const sti = ffstream(st);
+ LC3DemuxContext *lc3 = s->priv_data;
+ const AVIndexEntry *ie;
+ int index;
+
+ timestamp = FFMAX(timestamp - lc3->delay, 0);
+ if (timestamp >= lc3->length)
+ return AVERROR_EOF;
+
+ index = av_index_search_timestamp(st, timestamp, flags);
+ ie = index < 0 ? NULL : &sti->index_entries[index];
+ if (!ie || avio_seek(s->pb, ie->pos, SEEK_SET) < 0)
+ return -1;
+
+ lc3->timestamp = ie->timestamp;
+ if (lc3->timestamp == 0)
+ lc3->timestamp -= lc3->delay;
+
+ return 0;
+}
+
+const FFInputFormat ff_lc3_demuxer = {
+ .p.name = "lc3",
+ .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_GENERIC_INDEX,
+ .priv_data_size = sizeof(LC3DemuxContext),
+ .read_header = lc3_read_header,
+ .read_packet = lc3_read_packet,
+ .read_seek = lc3_read_seek,
+};
diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
new file mode 100644
index 0000000000..e768212f63
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "libavutil/intreadwrite.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;
+ int frame_us, ep_mode, hr_mode;
+ uint32_t nb_samples = av_rescale_q(
+ st->duration, st->time_base, (AVRational){ 1, srate_hz });
+
+ if (st->codecpar->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
+ ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
+ hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
+
+ 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,
+};
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-29 17:30 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-03-30 11:45 ` Paul B Mahol
2024-03-30 21:51 ` Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Paul B Mahol @ 2024-03-30 11:45 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On Fri, Mar 29, 2024 at 6:30 PM Antoine Soulier via ffmpeg-devel <
ffmpeg-devel@ffmpeg.org> 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>
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 164 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
> 6 files changed, 275 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/Changelog b/Changelog
> index 83a4cf7888..08c200a41d 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 demuxer and muxer
> - LC3/LC3plus decoding/encoding using external library liblc3
>
>
> 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.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 44aa485029..4961c42852 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,8 @@ 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 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..74d6794d00
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,164 @@
> +/*
> + * 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 "libavcodec/packet.h"
> +#include "libavutil/intreadwrite.h"
> +
> +#include "avformat.h"
> +#include "avio.h"
> +#include "demux.h"
> +#include "internal.h"
> +
> +typedef struct LC3DemuxContext {
> + int dt, delay;
> + int64_t timestamp;
> + 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_10us;
> + uint32_t length;
> + int 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_10us = avio_rl16(s->pb);
> + 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, 64, 1, srate_hz);
> + st->start_time = 0;
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = 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_10us);
> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> +
> + lc3->dt = av_rescale(frame_10us, srate_hz, 100*1000);
> + lc3->delay = av_rescale(frame_10us == 750 ? 400 : 250, srate_hz,
> 100*1000);
> +
> + lc3->timestamp = -lc3->delay;
> + lc3->length = st->duration;
> +
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int ret;
> +
>
int64_t pos = avio_tell(pb);
> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
> + if (ret < 0)
> + return ret;
> +
> + pkt->pos -= 2;
>
pkt->pos = pos;
+ pkt->pts = FFMAX(lc3->timestamp, 0);
>
Remove that line above.
> + pkt->duration = lc3->dt;
> +
> + lc3->timestamp += lc3->dt;
> + if (lc3->timestamp > lc3->length) {
> + pkt->duration -= lc3->timestamp - lc3->length;
> + lc3->timestamp = lc3->length;
> + }
> +
>
Remove above block.
> + return 0;
> +}
> +
> +static int lc3_read_seek(AVFormatContext *s, int stream_index, int64_t
> timestamp, int flags)
> +{
> + AVStream *st = s->streams[stream_index];
> + FFStream *const sti = ffstream(st);
> + LC3DemuxContext *lc3 = s->priv_data;
> + const AVIndexEntry *ie;
> + int index;
> +
> + timestamp = FFMAX(timestamp - lc3->delay, 0);
> + if (timestamp >= lc3->length)
> + return AVERROR_EOF;
> +
> + index = av_index_search_timestamp(st, timestamp, flags);
> + ie = index < 0 ? NULL : &sti->index_entries[index];
> + if (!ie || avio_seek(s->pb, ie->pos, SEEK_SET) < 0)
> + return -1;
> +
> + lc3->timestamp = ie->timestamp;
> + if (lc3->timestamp == 0)
> + lc3->timestamp -= lc3->delay;
> +
> + return 0;
> +}
>
This logic is unnecessary.
Format does not store timestamp internally in bitstream.
Just set packet duration to number of samples stored in that packet.
No need for pts overdesign, and remove this read_seek_function.
And just leave GENERIC_INDEX flags bellow.
> +
> +const FFInputFormat ff_lc3_demuxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity
> Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_GENERIC_INDEX,
> + .priv_data_size = sizeof(LC3DemuxContext),
> + .read_header = lc3_read_header,
> + .read_packet = lc3_read_packet,
> + .read_seek = lc3_read_seek,
> +};
> diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
> new file mode 100644
> index 0000000000..e768212f63
> --- /dev/null
> +++ b/libavformat/lc3enc.c
> @@ -0,0 +1,100 @@
> +/*
> + * 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 "libavutil/intreadwrite.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;
> + int frame_us, ep_mode, hr_mode;
> + uint32_t nb_samples = av_rescale_q(
> + st->duration, st->time_base, (AVRational){ 1, srate_hz });
> +
> + if (st->codecpar->extradata_size < 6)
> + return AVERROR_INVALIDDATA;
> +
> + frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
> + ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
> + hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
> +
> + 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,
> +};
> --
> 2.44.0.478.gd926399ef9-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".
>
_______________________________________________
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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-30 11:45 ` Paul B Mahol
@ 2024-03-30 21:51 ` Antoine Soulier via ffmpeg-devel
2024-03-30 23:15 ` Paul B Mahol
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-03-30 21:51 UTC (permalink / raw)
To: Paul B Mahol; +Cc: Antoine Soulier, FFmpeg development discussions and patches
I am not sure about the block:
> + lc3->timestamp += lc3->dt;
> + if (lc3->timestamp > lc3->length) {
> + pkt->duration -= lc3->timestamp - lc3->length;
> + lc3->timestamp = lc3->length;
> + }
The purpose is to reduce the duration of the last packet.
When converting a file, the last frame can contain "zeros" (or irrelevant)
samples that are not present in the source file (alignment to frame
boundary). These samples should be discarded.
> If I remove the "read_seek()" implementation, I lose the PTS / timestamp
information, and I don't know how to detect the last frame in the stream.
Do you know how I can handle a reduced duration for the last frame without
having an implementation of "read_seek()"?
(I can play with the position and EOF information, but can lead to strange
behaviors if the encoder adds extra frames at the end).
On Sat, Mar 30, 2024 at 4:46 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>
> On Fri, Mar 29, 2024 at 6:30 PM Antoine Soulier via ffmpeg-devel <
> ffmpeg-devel@ffmpeg.org> 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>
>> ---
>> Changelog | 1 +
>> doc/muxers.texi | 6 ++
>> libavformat/Makefile | 2 +
>> libavformat/allformats.c | 2 +
>> libavformat/lc3dec.c | 164 +++++++++++++++++++++++++++++++++++++++
>> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
>> 6 files changed, 275 insertions(+)
>> create mode 100644 libavformat/lc3dec.c
>> create mode 100644 libavformat/lc3enc.c
>>
>> diff --git a/Changelog b/Changelog
>> index 83a4cf7888..08c200a41d 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 demuxer and muxer
>> - LC3/LC3plus decoding/encoding using external library liblc3
>>
>>
>> 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.
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index 44aa485029..4961c42852 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
>> --- a/libavformat/allformats.c
>> +++ b/libavformat/allformats.c
>> @@ -252,6 +252,8 @@ 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 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..74d6794d00
>> --- /dev/null
>> +++ b/libavformat/lc3dec.c
>> @@ -0,0 +1,164 @@
>> +/*
>> + * 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 "libavcodec/packet.h"
>> +#include "libavutil/intreadwrite.h"
>> +
>> +#include "avformat.h"
>> +#include "avio.h"
>> +#include "demux.h"
>> +#include "internal.h"
>> +
>> +typedef struct LC3DemuxContext {
>> + int dt, delay;
>> + int64_t timestamp;
>> + 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_10us;
>> + uint32_t length;
>> + int 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_10us = avio_rl16(s->pb);
>> + 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, 64, 1, srate_hz);
>> + st->start_time = 0;
>> + st->duration = length;
>> +
>> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
>> + st->codecpar->codec_id = 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_10us);
>> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
>> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
>> +
>> + lc3->dt = av_rescale(frame_10us, srate_hz, 100*1000);
>> + lc3->delay = av_rescale(frame_10us == 750 ? 400 : 250, srate_hz,
>> 100*1000);
>> +
>> + lc3->timestamp = -lc3->delay;
>> + lc3->length = st->duration;
>> +
>> + return 0;
>> +}
>> +
>> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
>> +{
>> + LC3DemuxContext *lc3 = s->priv_data;
>> + AVIOContext *pb = s->pb;
>> + int ret;
>> +
>>
>
> int64_t pos = avio_tell(pb);
>
>
>> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
>> + if (ret < 0)
>> + return ret;
>> +
>> + pkt->pos -= 2;
>>
>
> pkt->pos = pos;
>
> + pkt->pts = FFMAX(lc3->timestamp, 0);
>>
>
> Remove that line above.
>
>
>> + pkt->duration = lc3->dt;
>> +
>> + lc3->timestamp += lc3->dt;
>> + if (lc3->timestamp > lc3->length) {
>> + pkt->duration -= lc3->timestamp - lc3->length;
>> + lc3->timestamp = lc3->length;
>> + }
>> +
>>
>
> Remove above block.
>
>
>> + return 0;
>> +}
>> +
>> +static int lc3_read_seek(AVFormatContext *s, int stream_index, int64_t
>> timestamp, int flags)
>> +{
>> + AVStream *st = s->streams[stream_index];
>> + FFStream *const sti = ffstream(st);
>> + LC3DemuxContext *lc3 = s->priv_data;
>> + const AVIndexEntry *ie;
>> + int index;
>> +
>> + timestamp = FFMAX(timestamp - lc3->delay, 0);
>> + if (timestamp >= lc3->length)
>> + return AVERROR_EOF;
>> +
>> + index = av_index_search_timestamp(st, timestamp, flags);
>> + ie = index < 0 ? NULL : &sti->index_entries[index];
>> + if (!ie || avio_seek(s->pb, ie->pos, SEEK_SET) < 0)
>> + return -1;
>> +
>> + lc3->timestamp = ie->timestamp;
>> + if (lc3->timestamp == 0)
>> + lc3->timestamp -= lc3->delay;
>> +
>> + return 0;
>> +}
>>
>
> This logic is unnecessary.
> Format does not store timestamp internally in bitstream.
> Just set packet duration to number of samples stored in that packet.
> No need for pts overdesign, and remove this read_seek_function.
> And just leave GENERIC_INDEX flags bellow.
>
>
>> +
>> +const FFInputFormat ff_lc3_demuxer = {
>> + .p.name = "lc3",
>> + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity
>> Communication Codec)"),
>> + .p.extensions = "lc3",
>> + .p.flags = AVFMT_GENERIC_INDEX,
>> + .priv_data_size = sizeof(LC3DemuxContext),
>> + .read_header = lc3_read_header,
>> + .read_packet = lc3_read_packet,
>> + .read_seek = lc3_read_seek,
>> +};
>> diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
>> new file mode 100644
>> index 0000000000..e768212f63
>> --- /dev/null
>> +++ b/libavformat/lc3enc.c
>> @@ -0,0 +1,100 @@
>> +/*
>> + * 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 "libavutil/intreadwrite.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;
>> + int frame_us, ep_mode, hr_mode;
>> + uint32_t nb_samples = av_rescale_q(
>> + st->duration, st->time_base, (AVRational){ 1, srate_hz });
>> +
>> + if (st->codecpar->extradata_size < 6)
>> + return AVERROR_INVALIDDATA;
>> +
>> + frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
>> + ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
>> + hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
>> +
>> + 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,
>> +};
>> --
>> 2.44.0.478.gd926399ef9-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".
>>
>
_______________________________________________
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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-03-30 21:51 ` Antoine Soulier via ffmpeg-devel
@ 2024-03-30 23:15 ` Paul B Mahol
0 siblings, 0 replies; 17+ messages in thread
From: Paul B Mahol @ 2024-03-30 23:15 UTC (permalink / raw)
To: Antoine Soulier; +Cc: FFmpeg development discussions and patches
On Sat, Mar 30, 2024 at 10:51 PM Antoine Soulier <asoulier@google.com>
wrote:
> I am not sure about the block:
>
>> + lc3->timestamp += lc3->dt;
>> + if (lc3->timestamp > lc3->length) {
>> + pkt->duration -= lc3->timestamp - lc3->length;
>> + lc3->timestamp = lc3->length;
>> + }
>
>
> The purpose is to reduce the duration of the last packet.
> When converting a file, the last frame can contain "zeros" (or irrelevant)
> samples that are not present in the source file (alignment to frame
> boundary). These samples should be discarded.
> > If I remove the "read_seek()" implementation, I lose the PTS / timestamp
> information, and I don't know how to detect the last frame in the stream.
> Do you know how I can handle a reduced duration for the last frame without
> having an implementation of "read_seek()"?
>
> (I can play with the position and EOF information, but can lead to strange
> behaviors if the encoder adds extra frames at the end).
>
Looks like format limitation. If duration is not stored in each packet than
demuxer can not do it and no spagetti code can fix it in demuxer. Still
output frame duration is set by decoder too when decoding, so make sure
that decoder outputs valid output and timestamps/durations.
So its not really possible to have also correct and robust duration of last
packet returned by demuxer. Even more the file may be manipulated in such
way that its no longer correct to derive last packet duration from full
duration stored in header when the EOF reached.
>
>
> On Sat, Mar 30, 2024 at 4:46 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>>
>>
>> On Fri, Mar 29, 2024 at 6:30 PM Antoine Soulier via ffmpeg-devel <
>> ffmpeg-devel@ffmpeg.org> 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>
>>> ---
>>> Changelog | 1 +
>>> doc/muxers.texi | 6 ++
>>> libavformat/Makefile | 2 +
>>> libavformat/allformats.c | 2 +
>>> libavformat/lc3dec.c | 164 +++++++++++++++++++++++++++++++++++++++
>>> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
>>> 6 files changed, 275 insertions(+)
>>> create mode 100644 libavformat/lc3dec.c
>>> create mode 100644 libavformat/lc3enc.c
>>>
>>> diff --git a/Changelog b/Changelog
>>> index 83a4cf7888..08c200a41d 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 demuxer and muxer
>>> - LC3/LC3plus decoding/encoding using external library liblc3
>>>
>>>
>>> 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.
>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>> index 44aa485029..4961c42852 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
>>> --- a/libavformat/allformats.c
>>> +++ b/libavformat/allformats.c
>>> @@ -252,6 +252,8 @@ 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 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..74d6794d00
>>> --- /dev/null
>>> +++ b/libavformat/lc3dec.c
>>> @@ -0,0 +1,164 @@
>>> +/*
>>> + * 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 "libavcodec/packet.h"
>>> +#include "libavutil/intreadwrite.h"
>>> +
>>> +#include "avformat.h"
>>> +#include "avio.h"
>>> +#include "demux.h"
>>> +#include "internal.h"
>>> +
>>> +typedef struct LC3DemuxContext {
>>> + int dt, delay;
>>> + int64_t timestamp;
>>> + 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_10us;
>>> + uint32_t length;
>>> + int 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_10us = avio_rl16(s->pb);
>>> + 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, 64, 1, srate_hz);
>>> + st->start_time = 0;
>>> + st->duration = length;
>>> +
>>> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
>>> + st->codecpar->codec_id = 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_10us);
>>> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
>>> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
>>> +
>>> + lc3->dt = av_rescale(frame_10us, srate_hz, 100*1000);
>>> + lc3->delay = av_rescale(frame_10us == 750 ? 400 : 250, srate_hz,
>>> 100*1000);
>>> +
>>> + lc3->timestamp = -lc3->delay;
>>> + lc3->length = st->duration;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
>>> +{
>>> + LC3DemuxContext *lc3 = s->priv_data;
>>> + AVIOContext *pb = s->pb;
>>> + int ret;
>>> +
>>>
>>
>> int64_t pos = avio_tell(pb);
>>
>>
>>> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + pkt->pos -= 2;
>>>
>>
>> pkt->pos = pos;
>>
>> + pkt->pts = FFMAX(lc3->timestamp, 0);
>>>
>>
>> Remove that line above.
>>
>>
>>> + pkt->duration = lc3->dt;
>>> +
>>> + lc3->timestamp += lc3->dt;
>>> + if (lc3->timestamp > lc3->length) {
>>> + pkt->duration -= lc3->timestamp - lc3->length;
>>> + lc3->timestamp = lc3->length;
>>> + }
>>> +
>>>
>>
>> Remove above block.
>>
>>
>>> + return 0;
>>> +}
>>> +
>>> +static int lc3_read_seek(AVFormatContext *s, int stream_index, int64_t
>>> timestamp, int flags)
>>> +{
>>> + AVStream *st = s->streams[stream_index];
>>> + FFStream *const sti = ffstream(st);
>>> + LC3DemuxContext *lc3 = s->priv_data;
>>> + const AVIndexEntry *ie;
>>> + int index;
>>> +
>>> + timestamp = FFMAX(timestamp - lc3->delay, 0);
>>> + if (timestamp >= lc3->length)
>>> + return AVERROR_EOF;
>>> +
>>> + index = av_index_search_timestamp(st, timestamp, flags);
>>> + ie = index < 0 ? NULL : &sti->index_entries[index];
>>> + if (!ie || avio_seek(s->pb, ie->pos, SEEK_SET) < 0)
>>> + return -1;
>>> +
>>> + lc3->timestamp = ie->timestamp;
>>> + if (lc3->timestamp == 0)
>>> + lc3->timestamp -= lc3->delay;
>>> +
>>> + return 0;
>>> +}
>>>
>>
>> This logic is unnecessary.
>> Format does not store timestamp internally in bitstream.
>> Just set packet duration to number of samples stored in that packet.
>> No need for pts overdesign, and remove this read_seek_function.
>> And just leave GENERIC_INDEX flags bellow.
>>
>>
>>> +
>>> +const FFInputFormat ff_lc3_demuxer = {
>>> + .p.name = "lc3",
>>> + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity
>>> Communication Codec)"),
>>> + .p.extensions = "lc3",
>>> + .p.flags = AVFMT_GENERIC_INDEX,
>>> + .priv_data_size = sizeof(LC3DemuxContext),
>>> + .read_header = lc3_read_header,
>>> + .read_packet = lc3_read_packet,
>>> + .read_seek = lc3_read_seek,
>>> +};
>>> diff --git a/libavformat/lc3enc.c b/libavformat/lc3enc.c
>>> new file mode 100644
>>> index 0000000000..e768212f63
>>> --- /dev/null
>>> +++ b/libavformat/lc3enc.c
>>> @@ -0,0 +1,100 @@
>>> +/*
>>> + * 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 "libavutil/intreadwrite.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;
>>> + int frame_us, ep_mode, hr_mode;
>>> + uint32_t nb_samples = av_rescale_q(
>>> + st->duration, st->time_base, (AVRational){ 1, srate_hz });
>>> +
>>> + if (st->codecpar->extradata_size < 6)
>>> + return AVERROR_INVALIDDATA;
>>> +
>>> + frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
>>> + ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
>>> + hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
>>> +
>>> + 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,
>>> +};
>>> --
>>> 2.44.0.478.gd926399ef9-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".
>>>
>>
_______________________________________________
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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec
@ 2024-04-01 21:32 Antoine Soulier via ffmpeg-devel
2024-04-01 21:32 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-04-01 21:32 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>
---
Changelog | 4 +
configure | 6 ++
doc/encoders.texi | 57 +++++++++++
doc/general_contents.texi | 11 +-
libavcodec/Makefile | 2 +
libavcodec/allcodecs.c | 2 +
libavcodec/codec_desc.c | 7 ++
libavcodec/codec_id.h | 1 +
libavcodec/liblc3dec.c | 145 ++++++++++++++++++++++++++
libavcodec/liblc3enc.c | 210 ++++++++++++++++++++++++++++++++++++++
10 files changed, 444 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/liblc3dec.c
create mode 100644 libavcodec/liblc3enc.c
diff --git a/Changelog b/Changelog
index e83a00e35c..83a4cf7888 100644
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,10 @@
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
+
+
version 7.0:
- DXV DXT1 encoder
- LEAD MCMP decoder
diff --git a/configure b/configure
index 2d46ef0b9c..e5d9ba9f53 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
@@ -3499,6 +3501,9 @@ libilbc_encoder_deps="libilbc"
libjxl_decoder_deps="libjxl libjxl_threads"
libjxl_encoder_deps="libjxl libjxl_threads"
libkvazaar_encoder_deps="libkvazaar"
+liblc3_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"
@@ -6869,6 +6874,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
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7c223ed74c..66847191e1 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
+@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 @var{bitrate}
+Set the bit rate in bits/s. This will determine the fixed size of the encoded
+frames, for a selected frame duration.
+
+@item ar @var{frequency}
+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 @var{boolean}
+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 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/libavcodec/Makefile b/libavcodec/Makefile
index eef936944d..6323c98add 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1122,6 +1122,8 @@ 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_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..f4705651fb 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -776,6 +776,8 @@ 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_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..7dba61dc8b 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3425,6 +3425,13 @@ 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,
+ },
/* subtitle codecs */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index c8dc21da74..0ab1e34a61 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -543,6 +543,7 @@ enum AVCodecID {
AV_CODEC_ID_AC4,
AV_CODEC_ID_OSQ,
AV_CODEC_ID_QOA,
+ AV_CODEC_ID_LC3,
/* 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..75ab90f377
--- /dev/null
+++ b/libavcodec/liblc3dec.c
@@ -0,0 +1,145 @@
+/*
+ * 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, hr_mode;
+ void *decoder_mem;
+ lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
+ int64_t length;
+} LibLC3DecContext;
+
+static av_cold int liblc3_decode_init(AVCodecContext *avctx)
+{
+ LibLC3DecContext *liblc3 = avctx->priv_data;
+ int channels = avctx->ch_layout.nb_channels;
+ int ep_mode;
+ unsigned decoder_size;
+
+ if (avctx->extradata_size < 10)
+ return AVERROR_INVALIDDATA;
+
+ liblc3->frame_us = AV_RL16(avctx->extradata + 0) * 10;
+ liblc3->srate_hz = avctx->sample_rate;
+ ep_mode = AV_RL16(avctx->extradata + 2);
+ liblc3->hr_mode = AV_RL16(avctx->extradata + 4);
+ liblc3->length = AV_RL32(avctx->extradata + 6);
+ if (ep_mode != 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Error protection mode is not supported.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO,
+ "Decoding %.1f ms frames.\n", liblc3->frame_us / 1000.f);
+ 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;
+
+ liblc3->decoder_mem = av_malloc_array(channels, decoder_size);
+ if (!liblc3->decoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->decoder[ch] = lc3_hr_setup_decoder(
+ liblc3->hr_mode, liblc3->frame_us, liblc3->srate_hz, 0,
+ (char *)liblc3->decoder_mem + ch * decoder_size);
+ }
+
+ 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;
+
+ av_freep(&liblc3->decoder_mem);
+
+ 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 = av_rescale(
+ liblc3->frame_us, liblc3->srate_hz, 1000*1000);
+ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+ return ret;
+
+ block_bytes = avpkt->size;
+ for (int ch = 0; ch < channels; ch++) {
+ int nbytes = block_bytes / channels + (ch < block_bytes % channels);
+
+ ret = lc3_decode(liblc3->decoder[ch], in, nbytes,
+ LC3_PCM_FORMAT_FLOAT, frame->data[ch], 1);
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+
+ in += nbytes;
+ }
+
+ if (liblc3->length > 0) {
+ int64_t end_pts = liblc3->length + avctx->delay;
+ frame->nb_samples = FFMIN(frame->nb_samples,
+ FFMAX(end_pts - frame->pts, 0));
+ }
+
+ *got_frame_ptr = 1;
+
+ return avpkt->size;
+}
+
+const FFCodec ff_liblc3_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),
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+ .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..9731229e50
--- /dev/null
+++ b/libavcodec/liblc3enc.c
@@ -0,0 +1,210 @@
+/*
+ * 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;
+ int hr_mode;
+} LibLC3EncOpts;
+
+typedef struct LibLC3EncContext {
+ const AVClass *av_class;
+ LibLC3EncOpts opts;
+ int block_bytes;
+ void *encoder_mem;
+ lc3_encoder_t encoder[ENCODER_MAX_CHANNELS];
+ int delay_samples;
+ int remaining_samples;
+} 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 / 1000.f);
+ return AVERROR(EINVAL);
+ }
+
+ hr_mode |= srate_hz > 48000;
+ hr_mode &= srate_hz >= 48000;
+
+ if (frame_us == 7500 && hr_mode) {
+ av_log(avctx, AV_LOG_ERROR,
+ "High-resolution mode is not supported with 7.5 ms frames.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Encoding %.1f ms frames.\n", frame_us / 1000.f);
+ if (hr_mode)
+ av_log(avctx, AV_LOG_INFO, "High-resolution mode is 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(hr_mode, frame_us, srate_hz);
+ if (!encoder_size)
+ return AVERROR(EINVAL);
+
+ liblc3->encoder_mem = av_malloc_array(channels, encoder_size);
+ if (!liblc3->encoder_mem)
+ return AVERROR(ENOMEM);
+
+ for (int ch = 0; ch < channels; ch++) {
+ liblc3->encoder[ch] = lc3_hr_setup_encoder(
+ hr_mode, frame_us, srate_hz, 0,
+ (char *)liblc3->encoder_mem + ch * encoder_size);
+ }
+
+ avctx->extradata = av_mallocz(6 + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!avctx->extradata)
+ return AVERROR(ENOMEM);
+
+ AV_WL16(avctx->extradata + 0, frame_us / 10);
+ AV_WL16(avctx->extradata + 2, 0);
+ AV_WL16(avctx->extradata + 4, hr_mode);
+ avctx->extradata_size = 6;
+
+ avctx->frame_size = av_rescale(frame_us, srate_hz, 1000*1000);
+ liblc3->delay_samples = lc3_hr_delay_samples(hr_mode, frame_us, srate_hz);
+ liblc3->remaining_samples = 0;
+
+ return 0;
+}
+
+static av_cold int liblc3_encode_close(AVCodecContext *avctx)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+
+ av_freep(&liblc3->encoder_mem);
+
+ return 0;
+}
+
+static int liblc3_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet_ptr)
+{
+ LibLC3EncContext *liblc3 = avctx->priv_data;
+ int block_bytes = liblc3->block_bytes;
+ int channels = avctx->ch_layout.nb_channels;
+ void *zero_frame = NULL;
+ uint8_t *data_ptr;
+ int ret;
+
+ if ((ret = ff_get_encode_buffer(avctx, pkt, block_bytes, 0)) < 0)
+ return ret;
+
+ if (frame) {
+ int padding = frame->nb_samples - frame->duration;
+ liblc3->remaining_samples = FFMAX(liblc3->delay_samples - padding, 0);
+ } else {
+ if (!liblc3->remaining_samples)
+ return 0;
+
+ liblc3->remaining_samples = 0;
+ zero_frame = av_mallocz(avctx->frame_size * sizeof(float));
+ if (!zero_frame)
+ return AVERROR(ENOMEM);
+ }
+
+ data_ptr = pkt->data;
+ for (int ch = 0; ch < channels; ch++) {
+ const float *pcm = zero_frame ? zero_frame : frame->data[ch];
+ int nbytes = block_bytes / channels + (ch < block_bytes % channels);
+
+ lc3_encode(liblc3->encoder[ch],
+ LC3_PCM_FORMAT_FLOAT, pcm, 1, nbytes, data_ptr);
+
+ data_ptr += nbytes;
+ }
+
+ if (zero_frame)
+ av_free(zero_frame);
+
+ *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 | AV_CODEC_CAP_DELAY,
+ .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.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-01 21:32 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
@ 2024-04-01 21:32 ` Antoine Soulier via ffmpeg-devel
2024-04-04 15:40 ` Stefano Sabatini
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-04-01 21:32 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>
---
Changelog | 1 +
doc/muxers.texi | 6 ++
libavformat/Makefile | 2 +
libavformat/allformats.c | 2 +
libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
6 files changed, 238 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/Changelog b/Changelog
index 83a4cf7888..08c200a41d 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 demuxer and muxer
- LC3/LC3plus decoding/encoding using external library liblc3
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.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 44aa485029..4961c42852 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,8 @@ 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 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..7232e9847e
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,127 @@
+/*
+ * 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 "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;
+} LC3DemuxContext;
+
+static int lc3_read_header(AVFormatContext *s)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVStream *st = NULL;
+ uint16_t tag, hdr_size;
+ uint16_t frame_10us;
+ uint32_t length;
+ int 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_10us = avio_rl16(s->pb);
+ 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, 64, 1, srate_hz);
+ st->duration = length;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = 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, 10)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
+ return ret;
+ }
+
+ AV_WL16(st->codecpar->extradata + 0, frame_10us);
+ AV_WL16(st->codecpar->extradata + 2, ep_mode);
+ AV_WL16(st->codecpar->extradata + 4, hr_mode);
+ AV_WL32(st->codecpar->extradata + 6, length);
+
+ lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVIOContext *pb = s->pb;
+ int64_t pos = avio_tell(pb);
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
+ if (ret < 0)
+ return ret;
+
+ pkt->pos = pos;
+ pkt->duration = lc3->frame_samples;
+
+ return 0;
+}
+
+const FFInputFormat ff_lc3_demuxer = {
+ .p.name = "lc3",
+ .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_GENERIC_INDEX,
+ .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..e768212f63
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "libavutil/intreadwrite.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;
+ int frame_us, ep_mode, hr_mode;
+ uint32_t nb_samples = av_rescale_q(
+ st->duration, st->time_base, (AVRational){ 1, srate_hz });
+
+ if (st->codecpar->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
+ ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
+ hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
+
+ 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,
+};
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-01 21:32 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-04-04 15:40 ` Stefano Sabatini
2024-04-04 16:51 ` Andreas Rheinhardt
2024-04-04 16:23 ` James Almer
2024-04-04 16:30 ` Andreas Rheinhardt
2 siblings, 1 reply; 17+ messages in thread
From: Stefano Sabatini @ 2024-04-04 15:40 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Monday 2024-04-01 21:32:05 +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>
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
> 6 files changed, 238 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
Paul, Andreas, do you have more comments or is it fine to push as is?
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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-04 15:40 ` Stefano Sabatini
@ 2024-04-04 16:51 ` Andreas Rheinhardt
0 siblings, 0 replies; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-04 16:51 UTC (permalink / raw)
To: ffmpeg-devel
Stefano Sabatini:
> On date Monday 2024-04-01 21:32:05 +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>
>> ---
>> Changelog | 1 +
>> doc/muxers.texi | 6 ++
>> libavformat/Makefile | 2 +
>> libavformat/allformats.c | 2 +
>> libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
>> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
>> 6 files changed, 238 insertions(+)
>> create mode 100644 libavformat/lc3dec.c
>> create mode 100644 libavformat/lc3enc.c
>
> Paul, Andreas, do you have more comments or is it fine to push as is?
>
Besides my comment about the actual patch: You applied the lavc commit
with incorrect author information: "Antoine Soulier via ffmpeg-devel
<ffmpeg-devel@ffmpeg.org>"
- 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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-01 21:32 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-04-04 15:40 ` Stefano Sabatini
@ 2024-04-04 16:23 ` James Almer
2024-04-04 16:30 ` Andreas Rheinhardt
2 siblings, 0 replies; 17+ messages in thread
From: James Almer @ 2024-04-04 16:23 UTC (permalink / raw)
To: ffmpeg-devel
On 4/1/2024 6:32 PM, Antoine Soulier via ffmpeg-devel 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>
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
> 6 files changed, 238 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/Changelog b/Changelog
> index 83a4cf7888..08c200a41d 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 demuxer and muxer
> - LC3/LC3plus decoding/encoding using external library liblc3
>
>
> 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.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 44aa485029..4961c42852 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,8 @@ 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 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..7232e9847e
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,127 @@
> +/*
> + * 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 "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;
> +} LC3DemuxContext;
> +
> +static int lc3_read_header(AVFormatContext *s)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVStream *st = NULL;
> + uint16_t tag, hdr_size;
> + uint16_t frame_10us;
> + uint32_t length;
> + int 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_10us = avio_rl16(s->pb);
> + 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, 64, 1, srate_hz);
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = 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, 10)) < 0) {
> + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
> + return ret;
> + }
> +
> + AV_WL16(st->codecpar->extradata + 0, frame_10us);
> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> + AV_WL32(st->codecpar->extradata + 6, length);
> +
> + lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
> +
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int64_t pos = avio_tell(pb);
> + int ret;
> +
> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
> + if (ret < 0)
> + return ret;
> +
> + pkt->pos = pos;
> + pkt->duration = lc3->frame_samples;
> +
> + return 0;
> +}
> +
> +const FFInputFormat ff_lc3_demuxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_GENERIC_INDEX,
> + .priv_data_size = sizeof(LC3DemuxContext),
> + .read_header = lc3_read_header,
> + .read_packet = lc3_read_packet,
This needs a probe function.
_______________________________________________
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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-01 21:32 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-04-04 15:40 ` Stefano Sabatini
2024-04-04 16:23 ` James Almer
@ 2024-04-04 16:30 ` Andreas Rheinhardt
2024-04-04 16:54 ` Antoine Soulier via ffmpeg-devel
2 siblings, 1 reply; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-04 16:30 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>
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
> 6 files changed, 238 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/Changelog b/Changelog
> index 83a4cf7888..08c200a41d 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 demuxer and muxer
> - LC3/LC3plus decoding/encoding using external library liblc3
>
>
> 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.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 44aa485029..4961c42852 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,8 @@ 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 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..7232e9847e
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,127 @@
> +/*
> + * 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 "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;
> +} LC3DemuxContext;
> +
> +static int lc3_read_header(AVFormatContext *s)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVStream *st = NULL;
> + uint16_t tag, hdr_size;
> + uint16_t frame_10us;
> + uint32_t length;
> + int 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_10us = avio_rl16(s->pb);
> + 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, 64, 1, srate_hz);
> + st->duration = length;
> +
> + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> + st->codecpar->codec_id = 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, 10)) < 0) {
> + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
Still pointless error message.
> + return ret;
> + }
> +
> + AV_WL16(st->codecpar->extradata + 0, frame_10us);
> + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> + AV_WL32(st->codecpar->extradata + 6, length);
> +
> + lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
> +
> + return 0;
> +}
> +
> +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + LC3DemuxContext *lc3 = s->priv_data;
> + AVIOContext *pb = s->pb;
> + int64_t pos = avio_tell(pb);
> + int ret;
> +
> + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
> + if (ret < 0)
> + return ret;
> +
> + pkt->pos = pos;
> + pkt->duration = lc3->frame_samples;
This should set AV_PKT_DATA_SKIP_SAMPLES side data to the packet that
crosses the number of samples specified in the header.
> +
> + return 0;
> +}
> +
> +const FFInputFormat ff_lc3_demuxer = {
> + .p.name = "lc3",
> + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication Codec)"),
> + .p.extensions = "lc3",
> + .p.flags = AVFMT_GENERIC_INDEX,
> + .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..e768212f63
> --- /dev/null
> +++ b/libavformat/lc3enc.c
> @@ -0,0 +1,100 @@
> +/*
> + * 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 "libavutil/intreadwrite.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;
> + int frame_us, ep_mode, hr_mode;
> + uint32_t nb_samples = av_rescale_q(
> + st->duration, st->time_base, (AVRational){ 1, srate_hz });
> +
> + if (st->codecpar->extradata_size < 6)
> + return AVERROR_INVALIDDATA;
> +
> + frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
> + ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
> + hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
> +
> + 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,
> +};
_______________________________________________
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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-04 16:30 ` Andreas Rheinhardt
@ 2024-04-04 16:54 ` Antoine Soulier via ffmpeg-devel
2024-04-04 17:09 ` Andreas Rheinhardt
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-04-04 16:54 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
In a previous patch, I modified the duration of the packet. But since, we
now support seeking, I don't know how to obtain the pts of the packet (or
sample position since start), without an implementation of a `read_seek()`
function.
Can you help me on how I can derive the pts of the packet?
Thanks.
On Thu, Apr 4, 2024, 9:30 AM Andreas Rheinhardt <
andreas.rheinhardt@outlook.com> wrote:
> 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>
> > ---
> > Changelog | 1 +
> > doc/muxers.texi | 6 ++
> > libavformat/Makefile | 2 +
> > libavformat/allformats.c | 2 +
> > libavformat/lc3dec.c | 127 +++++++++++++++++++++++++++++++++++++++
> > libavformat/lc3enc.c | 100 ++++++++++++++++++++++++++++++
> > 6 files changed, 238 insertions(+)
> > create mode 100644 libavformat/lc3dec.c
> > create mode 100644 libavformat/lc3enc.c
> >
> > diff --git a/Changelog b/Changelog
> > index 83a4cf7888..08c200a41d 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 demuxer and muxer
> > - LC3/LC3plus decoding/encoding using external library liblc3
> >
> >
> > 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.
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index 44aa485029..4961c42852 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -332,6 +332,8 @@ 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_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 9df42bb87a..0b36a7c3eb 100644
> > --- a/libavformat/allformats.c
> > +++ b/libavformat/allformats.c
> > @@ -252,6 +252,8 @@ 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 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..7232e9847e
> > --- /dev/null
> > +++ b/libavformat/lc3dec.c
> > @@ -0,0 +1,127 @@
> > +/*
> > + * 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 "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;
> > +} LC3DemuxContext;
> > +
> > +static int lc3_read_header(AVFormatContext *s)
> > +{
> > + LC3DemuxContext *lc3 = s->priv_data;
> > + AVStream *st = NULL;
> > + uint16_t tag, hdr_size;
> > + uint16_t frame_10us;
> > + uint32_t length;
> > + int 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_10us = avio_rl16(s->pb);
> > + 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, 64, 1, srate_hz);
> > + st->duration = length;
> > +
> > + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> > + st->codecpar->codec_id = 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, 10)) < 0) {
> > + av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");
>
> Still pointless error message.
>
> > + return ret;
> > + }
> > +
> > + AV_WL16(st->codecpar->extradata + 0, frame_10us);
> > + AV_WL16(st->codecpar->extradata + 2, ep_mode);
> > + AV_WL16(st->codecpar->extradata + 4, hr_mode);
> > + AV_WL32(st->codecpar->extradata + 6, length);
> > +
> > + lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
> > +
> > + return 0;
> > +}
> > +
> > +static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
> > +{
> > + LC3DemuxContext *lc3 = s->priv_data;
> > + AVIOContext *pb = s->pb;
> > + int64_t pos = avio_tell(pb);
> > + int ret;
> > +
> > + ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
> > + if (ret < 0)
> > + return ret;
> > +
> > + pkt->pos = pos;
> > + pkt->duration = lc3->frame_samples;
>
> This should set AV_PKT_DATA_SKIP_SAMPLES side data to the packet that
> crosses the number of samples specified in the header.
>
> > +
> > + return 0;
> > +}
> > +
> > +const FFInputFormat ff_lc3_demuxer = {
> > + .p.name = "lc3",
> > + .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity
> Communication Codec)"),
> > + .p.extensions = "lc3",
> > + .p.flags = AVFMT_GENERIC_INDEX,
> > + .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..e768212f63
> > --- /dev/null
> > +++ b/libavformat/lc3enc.c
> > @@ -0,0 +1,100 @@
> > +/*
> > + * 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 "libavutil/intreadwrite.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;
> > + int frame_us, ep_mode, hr_mode;
> > + uint32_t nb_samples = av_rescale_q(
> > + st->duration, st->time_base, (AVRational){ 1, srate_hz });
> > +
> > + if (st->codecpar->extradata_size < 6)
> > + return AVERROR_INVALIDDATA;
> > +
> > + frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
> > + ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
> > + hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
> > +
> > + 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,
> > +};
>
> _______________________________________________
> 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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-04 16:54 ` Antoine Soulier via ffmpeg-devel
@ 2024-04-04 17:09 ` Andreas Rheinhardt
0 siblings, 0 replies; 17+ messages in thread
From: Andreas Rheinhardt @ 2024-04-04 17:09 UTC (permalink / raw)
To: ffmpeg-devel
Antoine Soulier via ffmpeg-devel:
> In a previous patch, I modified the duration of the packet. But since, we
> now support seeking, I don't know how to obtain the pts of the packet (or
> sample position since start), without an implementation of a `read_seek()`
> function.
> Can you help me on how I can derive the pts of the packet?
> Thanks.
>
Have you already taken a look at FFStream.cur_dts?
- 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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3dec: Retrieve duration of the last packet from the demux
@ 2024-04-04 22:57 Antoine Soulier via ffmpeg-devel
2024-04-04 22:57 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-04-04 22:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
From: Antoine SOULIER <asoulier@google.com>
Use the packet duration field to invalid last samples of the last frame.
---
libavcodec/liblc3dec.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/libavcodec/liblc3dec.c b/libavcodec/liblc3dec.c
index 90da28679b..d250ace38a 100644
--- a/libavcodec/liblc3dec.c
+++ b/libavcodec/liblc3dec.c
@@ -34,7 +34,6 @@ typedef struct LibLC3DecContext {
int frame_us, srate_hz, hr_mode;
void *decoder_mem;
lc3_decoder_t decoder[DECODER_MAX_CHANNELS];
- int64_t length;
} LibLC3DecContext;
static av_cold int liblc3_decode_init(AVCodecContext *avctx)
@@ -44,12 +43,12 @@ static av_cold int liblc3_decode_init(AVCodecContext *avctx)
int ep_mode;
unsigned decoder_size;
- if (avctx->extradata_size < 10)
+ if (avctx->extradata_size < 6)
return AVERROR_INVALIDDATA;
if (channels < 0 || channels > DECODER_MAX_CHANNELS) {
av_log(avctx, AV_LOG_ERROR,
"Invalid number of channels %d. Max %d channels are accepted\n",
- channels, DECODER_MAX_CHANNES);
+ channels, DECODER_MAX_CHANNELS);
return AVERROR(EINVAL);
}
@@ -57,7 +56,6 @@ static av_cold int liblc3_decode_init(AVCodecContext *avctx)
liblc3->srate_hz = avctx->sample_rate;
ep_mode = AV_RL16(avctx->extradata + 2);
liblc3->hr_mode = AV_RL16(avctx->extradata + 4);
- liblc3->length = AV_RL32(avctx->extradata + 6);
if (ep_mode != 0) {
av_log(avctx, AV_LOG_ERROR,
"Error protection mode is not supported.\n");
@@ -126,11 +124,7 @@ static int liblc3_decode(AVCodecContext *avctx, AVFrame *frame,
in += nbytes;
}
- if (liblc3->length > 0) {
- int64_t end_pts = liblc3->length + avctx->delay;
- frame->nb_samples = FFMIN(frame->nb_samples,
- FFMAX(end_pts - frame->pts, 0));
- }
+ frame->nb_samples = FFMIN(frame->nb_samples, avpkt->duration);
*got_frame_ptr = 1;
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-04 22:57 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3dec: Retrieve duration of the last packet from the demux Antoine Soulier via ffmpeg-devel
@ 2024-04-04 22:57 ` Antoine Soulier via ffmpeg-devel
2024-04-06 7:54 ` Stefano Sabatini
0 siblings, 1 reply; 17+ messages in thread
From: Antoine Soulier via ffmpeg-devel @ 2024-04-04 22:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Antoine Soulier
From: Antoine SOULIER <asoulier@google.com>
A file format is described in Bluetooth SIG LC3 and ETSI TS 103 634, for
test purpose. This is the format implemented here.
---
Changelog | 1 +
doc/muxers.texi | 6 ++
libavformat/Makefile | 2 +
libavformat/allformats.c | 2 +
libavformat/lc3dec.c | 160 +++++++++++++++++++++++++++++++++++++++
libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
6 files changed, 271 insertions(+)
create mode 100644 libavformat/lc3dec.c
create mode 100644 libavformat/lc3enc.c
diff --git a/Changelog b/Changelog
index 18e83b99a1..92670f6a05 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 demuxer and muxer
- Raw Captions with Time (RCWT) closed caption demuxer
- LC3/LC3plus decoding/encoding using external library liblc3
diff --git a/doc/muxers.texi b/doc/muxers.texi
index d8a1f83309..ed4144f6d1 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -2700,6 +2700,12 @@ computer-generated compositions.
This muxer accepts a single audio stream containing PCM data.
+@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.
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 9981799cc9..027d0cdae5 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -332,6 +332,8 @@ 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_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 ae925dcf60..305fa46532 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -252,6 +252,8 @@ 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 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..1fcde8ca4e
--- /dev/null
+++ b/libavformat/lc3dec.c
@@ -0,0 +1,160 @@
+/*
+ * 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 "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 end_dts;
+} LC3DemuxContext;
+
+static int lc3_read_probe(const AVProbeData *p)
+{
+ uint16_t frame_10us, srate_100hz;
+
+ if (p->buf_size < 12)
+ return 0;
+
+ if (AV_RB16(p->buf + 0) != 0x1ccc ||
+ AV_RL16(p->buf + 2) < 9 * sizeof(uint16_t))
+ return 0;
+
+ srate_100hz = AV_RL16(p->buf + 4);
+ if (srate_100hz != 8000/100 && srate_100hz != 16000/100 &&
+ srate_100hz != 24000/100 && srate_100hz != 32000/100 &&
+ srate_100hz != 48000/100 && srate_100hz != 96000/100)
+ return 0;
+
+ frame_10us = AV_RL16(p->buf + 10);
+ if (frame_10us != 2500/10 && frame_10us != 5000/10 &&
+ frame_10us != 7500/10 && frame_10us != 10000/10)
+ return 0;
+
+ return AVPROBE_SCORE_MAX;
+}
+
+static int lc3_read_header(AVFormatContext *s)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVStream *st = NULL;
+ uint16_t tag, hdr_size;
+ uint16_t frame_10us;
+ uint32_t length;
+ int ep_mode, hr_mode;
+ int srate_hz, channels, bit_rate;
+ int num_extra_params, ret;
+ int delay;
+
+ 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_10us = avio_rl16(s->pb);
+ 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, 64, 1, srate_hz);
+ avpriv_update_cur_dts(s, st, 0);
+ st->duration = length;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = 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)
+ return ret;
+
+ AV_WL16(st->codecpar->extradata + 0, frame_10us);
+ AV_WL16(st->codecpar->extradata + 2, ep_mode);
+ AV_WL16(st->codecpar->extradata + 4, hr_mode);
+
+ lc3->frame_samples = av_rescale(frame_10us, srate_hz, 100*1000);
+
+ delay = av_rescale(frame_10us == 750 ? 400 : 250, srate_hz, 100*1000);
+ lc3->end_dts = length + delay;
+
+ return 0;
+}
+
+static int lc3_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ LC3DemuxContext *lc3 = s->priv_data;
+ AVStream *st = s->streams[0];
+ AVIOContext *pb = s->pb;
+ int64_t pos = avio_tell(pb);
+ int64_t remaining_samples;
+ int ret;
+
+ ret = av_get_packet(s->pb, pkt, avio_rl16(pb));
+ if (ret < 0)
+ return ret;
+
+ pkt->pos = pos;
+
+ remaining_samples = FFMAX(lc3->end_dts - ffstream(st)->cur_dts, 0);
+ pkt->duration = FFMIN(lc3->frame_samples, remaining_samples);
+
+ return 0;
+}
+
+const FFInputFormat ff_lc3_demuxer = {
+ .p.name = "lc3",
+ .p.long_name = NULL_IF_CONFIG_SMALL("LC3 (Low Complexity Communication Codec)"),
+ .p.extensions = "lc3",
+ .p.flags = AVFMT_GENERIC_INDEX,
+ .priv_data_size = sizeof(LC3DemuxContext),
+ .read_probe = lc3_read_probe,
+ .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..e768212f63
--- /dev/null
+++ b/libavformat/lc3enc.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "libavutil/intreadwrite.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;
+ int frame_us, ep_mode, hr_mode;
+ uint32_t nb_samples = av_rescale_q(
+ st->duration, st->time_base, (AVRational){ 1, srate_hz });
+
+ if (st->codecpar->extradata_size < 6)
+ return AVERROR_INVALIDDATA;
+
+ frame_us = AV_RL16(st->codecpar->extradata + 0) * 10;
+ ep_mode = AV_RL16(st->codecpar->extradata + 2) != 0;
+ hr_mode = AV_RL16(st->codecpar->extradata + 4) != 0;
+
+ 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,
+};
--
2.44.0.478.gd926399ef9-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] 17+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport
2024-04-04 22:57 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
@ 2024-04-06 7:54 ` Stefano Sabatini
0 siblings, 0 replies; 17+ messages in thread
From: Stefano Sabatini @ 2024-04-06 7:54 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Antoine Soulier
On date Thursday 2024-04-04 22:57:34 +0000, ffmpeg-devel Mailing List wrote:
> From: Antoine SOULIER <asoulier@google.com>
>
> A file format is described in Bluetooth SIG LC3 and ETSI TS 103 634, for
> test purpose. This is the format implemented here.
> ---
> Changelog | 1 +
> doc/muxers.texi | 6 ++
> libavformat/Makefile | 2 +
> libavformat/allformats.c | 2 +
> libavformat/lc3dec.c | 160 +++++++++++++++++++++++++++++++++++++++
> libavformat/lc3enc.c | 100 ++++++++++++++++++++++++
> 6 files changed, 271 insertions(+)
> create mode 100644 libavformat/lc3dec.c
> create mode 100644 libavformat/lc3enc.c
>
> diff --git a/Changelog b/Changelog
> index 18e83b99a1..92670f6a05 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 demuxer and muxer
> - Raw Captions with Time (RCWT) closed caption demuxer
> - LC3/LC3plus decoding/encoding using external library liblc3
nit: add new entry to the bottom
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index d8a1f83309..ed4144f6d1 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -2700,6 +2700,12 @@ computer-generated compositions.
>
> This muxer accepts a single audio stream containing PCM data.
>
> +@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.
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 9981799cc9..027d0cdae5 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -332,6 +332,8 @@ 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_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 ae925dcf60..305fa46532 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -252,6 +252,8 @@ 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 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..1fcde8ca4e
> --- /dev/null
> +++ b/libavformat/lc3dec.c
> @@ -0,0 +1,160 @@
> +/*
> + * 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 "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 end_dts;
> +} LC3DemuxContext;
> +
> +static int lc3_read_probe(const AVProbeData *p)
> +{
> + uint16_t frame_10us, srate_100hz;
> +
> + if (p->buf_size < 12)
> + return 0;
> +
> + if (AV_RB16(p->buf + 0) != 0x1ccc ||
> + AV_RL16(p->buf + 2) < 9 * sizeof(uint16_t))
> + return 0;
> +
> + srate_100hz = AV_RL16(p->buf + 4);
> + if (srate_100hz != 8000/100 && srate_100hz != 16000/100 &&
> + srate_100hz != 24000/100 && srate_100hz != 32000/100 &&
> + srate_100hz != 48000/100 && srate_100hz != 96000/100)
> + return 0;
nit++: sligthly simpler if you multiply srate to avoid the dividend in
the checks
Also I'm a bit surprised by this logic, since here we are assuming
only a subset of srate values, but on the other end there is no
validation in the demuxer. Probably it's good to keep this logic for
the probing, but in this case we should also add validation here and
in the muxer?
> +
> + frame_10us = AV_RL16(p->buf + 10);
> + if (frame_10us != 2500/10 && frame_10us != 5000/10 &&
> + frame_10us != 7500/10 && frame_10us != 10000/10)
> + return 0;
ditto mutatis mutandis
[...]
_______________________________________________
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] 17+ messages in thread
end of thread, other threads:[~2024-04-06 7:54 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-28 21:35 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-28 21:35 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-03-28 21:49 ` Andreas Rheinhardt
2024-03-28 22:33 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-28 22:33 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-03-29 17:30 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-03-29 17:30 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-03-30 11:45 ` Paul B Mahol
2024-03-30 21:51 ` Antoine Soulier via ffmpeg-devel
2024-03-30 23:15 ` Paul B Mahol
2024-04-01 21:32 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3: Add encoding/decoding support of LC3 audio codec Antoine Soulier via ffmpeg-devel
2024-04-01 21:32 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-04-04 15:40 ` Stefano Sabatini
2024-04-04 16:51 ` Andreas Rheinhardt
2024-04-04 16:23 ` James Almer
2024-04-04 16:30 ` Andreas Rheinhardt
2024-04-04 16:54 ` Antoine Soulier via ffmpeg-devel
2024-04-04 17:09 ` Andreas Rheinhardt
2024-04-04 22:57 [FFmpeg-devel] [PATCH 1/2] avcodec/liblc3dec: Retrieve duration of the last packet from the demux Antoine Soulier via ffmpeg-devel
2024-04-04 22:57 ` [FFmpeg-devel] [PATCH 2/2] avformat/lc3: Add file format for LC3/LC3plus transport Antoine Soulier via ffmpeg-devel
2024-04-06 7:54 ` 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