From patchwork Mon Sep 12 09:40:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 37852 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:139a:b0:8f:1db5:eae2 with SMTP id w26csp2355067pzh; Mon, 12 Sep 2022 02:40:13 -0700 (PDT) X-Google-Smtp-Source: AA6agR7O/JmmREYy7fC8AZ867jvFQ7oVXNe0L/c7cPorogC3MdufAYsJcRPInUmePhD7pYjxKpIH X-Received: by 2002:a17:906:58c9:b0:778:8c85:19ee with SMTP id e9-20020a17090658c900b007788c8519eemr12637643ejs.693.1662975613325; Mon, 12 Sep 2022 02:40:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1662975613; cv=none; d=google.com; s=arc-20160816; b=rGNdIh6ducANvT227Npw9giRVeojP6AbXn8woeJW7B/28I9Sud1vyBo6G9cIaYXtky SeTjYKG9/fjfFOaSumnFjOnRYg75VGfGZqNdwH40bRBnXx977+mTFJSE5fDoVR5fwOVw znSWfIJo2G4KipfvVoi9/XgIEqJH0k8svmb0iOimhVcMfieV9teVl0VS6oY+xuMuIWkf WT/DBhefnjjl3ZR+BdKuRq+8lMLEIxQYdrK9nx5HzUpq4Yj4HXGoxJOXQEm87/w1jGV+ Gx2OUxTTukScEk25goVV3VM5mynxkZY0PyKxGPz8CFtdqrKWnyu8Y6IdvQy735N1Fmfb vkHQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:to :message-id:date:from:mime-version:dkim-signature:delivered-to; bh=1anSFLsOWDL8KHSYYPs9U23QD6WvjFVeeGa7lJ4DiQg=; b=M0q5gbtMEL/SypMK3HBQsXQk+9O8BWiOBViXjbTOTVcksiMcw4aT8laVh7grgY9BMz QYY4IzS3V3C2ceNLty0yk/ZnmPrEj6a+a+QAR4kT6SRb3WANoXU2vm/nE1AN3gfYsQqO pnQ3SM8uZ4LsdRF/vj04DI/MUa0wcyoWR4Bf5R9x/DAIx2MIwg3S0ighxBYCacmrY1Jb 0+ps3fG7szycpkuEG5Hd5Rqv6a3NHjiSWCCfrvGduwUXunZUExv5oSAXQsOKa3hrCR1L IypuyDl6yfDsbkyyGNZsjYgB97uMs5uRE0+GhqBrhvKNW1vSAQge8ZvhfdJvv7J2Yt4p kzRA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=UIq5jXQx; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id cr12-20020a170906d54c00b0073065ea79bfsi4922571ejc.528.2022.09.12.02.40.12; Mon, 12 Sep 2022 02:40:13 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=UIq5jXQx; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 0A3BD68BB25; Mon, 12 Sep 2022 12:40:09 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-yb1-f180.google.com (mail-yb1-f180.google.com [209.85.219.180]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6C41B68B779 for ; Mon, 12 Sep 2022 12:40:02 +0300 (EEST) Received: by mail-yb1-f180.google.com with SMTP id 198so6778580ybc.1 for ; Mon, 12 Sep 2022 02:40:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date; bh=7Qu0X7RUI8ahwYuo0hWqXKENLiA1AcGUcwII9SjGpDw=; b=UIq5jXQxg4bfz/CZNRPUeDCtcLkF6yxZq3/tqzehBx7eB0GZi3cxyNuE8MjiT7SY58 fte3GLd0vcaTrasC6uEEV+y4KH+sr9o1rgWfYkxzwftH6wUdq3mJhpKAH7WlJ11f0KBJ H8NDRNhKA2o6Cq6PVHQlNFuhLzKd+LeTmODT/XNLi+D2cHqX1eZ99quaQ271rFUMMkUo Og+hmFh7ipEPxYKal8tg3tV1tm673c4+rQU9z8+qTBVpxWcEDrwTknvq+JEA9a0e94d5 /ol6RgtPUAsOhdv9VzEYXpUQE38Nyxn+1iyinbotRiCW5HbxdlvsEn47gZ2BUeRirCPa mn1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date; bh=7Qu0X7RUI8ahwYuo0hWqXKENLiA1AcGUcwII9SjGpDw=; b=eqJxcSt3GtDlaRCghlU9RVxxy+wWqn3WYeEx2tTLu4zjGlf+MjaWuMcggZ9mZ0g46u RZzyYHcEbOxCWeqFT7tu2mnS8pX1qgxZpjymSwiKbjlkcjxRyDjHAu7iqDxokOVmH2O6 dGkEq31IDKEoLX8NVIAvwPuCivE0RGoeo20+APFE+4TVH+XginFjXQKzdF1FUQdzB50s sr4IWl6A89M/kB1Xfd1IDYf3Aqac62hD879L+fy+x7pU6ru4z7VzPXHq3RTA6brxnEPr 3W3ApamW8rS4KqhhLbaRMu9OsmmpAjaok6YdZQRGN0ZVpU2b0GGUmEXZKCvVJm+QdjOF deug== X-Gm-Message-State: ACgBeo1KXNNGC1DwkCuTEBuBu3y4YYmJcpA8yMgubp39fdj0E/3jqz4y v26kAaN1LDAtrN5F2iq5AxM7htypWl3oUz27LM4vf/+j X-Received: by 2002:a25:d78b:0:b0:6af:662c:f087 with SMTP id o133-20020a25d78b000000b006af662cf087mr1000189ybg.396.1662975601071; Mon, 12 Sep 2022 02:40:01 -0700 (PDT) MIME-Version: 1.0 Received: by 2002:a81:7402:0:0:0:0:0 with HTTP; Mon, 12 Sep 2022 02:40:00 -0700 (PDT) From: Paul B Mahol Date: Mon, 12 Sep 2022 11:40:00 +0200 Message-ID: To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avformat: add Limitless Audio Format demuxer X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: MvEHE0J7O5T3 Patch attached. From d867b825507b5f38a051dd0ccf4612b7570a2088 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Sun, 11 Sep 2022 20:10:27 +0200 Subject: [PATCH] avformat: add LAF demuxer Signed-off-by: Paul B Mahol --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/lafdec.c | 253 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 libavformat/lafdec.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 5cdcda3239..19a4ba2a8f 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -319,6 +319,7 @@ OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o OBJS-$(CONFIG_KUX_DEMUXER) += flvdec.o 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_LMLM4_DEMUXER) += lmlm4.o OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index cebd5e0c67..a545b5ff45 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -236,6 +236,7 @@ extern const AVInputFormat ff_jv_demuxer; extern const AVInputFormat ff_kux_demuxer; extern const AVInputFormat ff_kvag_demuxer; extern const AVOutputFormat ff_kvag_muxer; +extern const AVInputFormat ff_laf_demuxer; extern const AVOutputFormat ff_latm_muxer; extern const AVInputFormat ff_lmlm4_demuxer; extern const AVInputFormat ff_loas_demuxer; diff --git a/libavformat/lafdec.c b/libavformat/lafdec.c new file mode 100644 index 0000000000..35bce2b327 --- /dev/null +++ b/libavformat/lafdec.c @@ -0,0 +1,253 @@ +/* + * Limitless Audio Format demuxer + * Copyright (c) 2022 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +typedef struct StreamParams { + float horizontal; + float vertical; + int lfe; + AVChannelLayout layout; +} StreamParams; + +typedef struct LAFContext { + uint8_t *data; + unsigned nb_stored; + unsigned stored_index; + unsigned index; + unsigned bpp; + + StreamParams p[1024]; +} LAFContext; + +typedef struct LAFStream { + unsigned stored; +} LAFStream; + +static int laf_probe(const AVProbeData *p) +{ + if (memcmp(p->buf, "LIMITLESS", 9)) + return 0; + if (memcmp(p->buf + 9, "HEAD", 4)) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int laf_read_header(AVFormatContext *ctx) +{ + LAFContext *s = ctx->priv_data; + AVIOContext *pb = ctx->pb; + unsigned st_count, mode; + unsigned sample_rate; + int64_t duration; + int codec_id; + int quality; + int bpp; + + avio_skip(pb, 9); + if (avio_rb32(pb) != MKBETAG('H','E','A','D')) + return AVERROR_INVALIDDATA; + + quality = avio_r8(pb); + if (quality > 3) + return AVERROR_INVALIDDATA; + mode = avio_r8(pb); + if (mode > 1) + return AVERROR_INVALIDDATA; + st_count = avio_rl32(pb); + if (st_count == 0 || st_count > 1024) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < st_count; i++) { + StreamParams *stp = &s->p[i]; + + stp->vertical = av_int2float(avio_rl32(pb)); + stp->horizontal = av_int2float(avio_rl32(pb)); + stp->lfe = avio_r8(pb); + if (stp->lfe) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_LOW_FREQUENCY)); + } else if (stp->vertical == 0.f && + stp->horizontal == 0.f) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_FRONT_CENTER)); + } else if (stp->vertical == 0.f && + stp->horizontal == -30.f) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_FRONT_LEFT)); + } else if (stp->vertical == 0.f && + stp->horizontal == 30.f) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_FRONT_RIGHT)); + } else if (stp->vertical == 0.f && + stp->horizontal == -110.f) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_SIDE_LEFT)); + } else if (stp->vertical == 0.f && + stp->horizontal == 110.f) { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MASK(1, (AV_CH_SIDE_RIGHT)); + } else { + stp->layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + } + } + + sample_rate = avio_rl32(pb); + duration = avio_rl64(pb) / st_count; + switch (quality) { + case 0: + codec_id = AV_CODEC_ID_PCM_U8; + bpp = 1; + break; + case 1: + codec_id = AV_CODEC_ID_PCM_S16LE; + bpp = 2; + break; + case 2: + codec_id = AV_CODEC_ID_PCM_F32LE; + bpp = 4; + break; + case 3: + codec_id = AV_CODEC_ID_PCM_S24LE; + bpp = 3; + break; + } + + s->index = 0; + s->stored_index = 0; + s->bpp = bpp; + s->data = av_mallocz(st_count * sample_rate * bpp); + if (!s->data) + return AVERROR(ENOMEM); + + for (int st = 0; st < st_count; st++) { + StreamParams *stp = &s->p[st]; + LAFStream *lafst; + AVCodecParameters *par; + AVStream *st = avformat_new_stream(ctx, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_id = codec_id; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->ch_layout.nb_channels = 1; + par->ch_layout = stp->layout; + par->sample_rate = sample_rate; + st->duration = duration; + st->priv_data = lafst = av_mallocz(sizeof(LAFStream)); + if (!st->priv_data) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + } + + return 0; +} + +static int laf_read_packet(AVFormatContext *ctx, AVPacket *pkt) +{ + AVIOContext *pb = ctx->pb; + LAFContext *s = ctx->priv_data; + AVStream *st = ctx->streams[0]; + LAFStream *lafst = st->priv_data; + const int bpp = s->bpp; + int header_len = (ctx->nb_streams / 8) + !!(ctx->nb_streams & 7); + int64_t pos; + int ret; + +again: + if (avio_feof(pb)) + return AVERROR_EOF; + + pos = avio_tell(pb); + + if (s->index >= ctx->nb_streams) { + int cur_st = 0, st_count = 0, st_index = 0; + + for (int i = 0; i < header_len; i++) { + uint8_t val = avio_r8(pb); + + for (int j = 0; j < 8 && cur_st < ctx->nb_streams; j++, cur_st++) { + AVStream *st = ctx->streams[st_index]; + LAFStream *lafst = st->priv_data; + + lafst->stored = 0; + if (val & 1) { + lafst->stored = 1; + st_count++; + } + val >>= 1; + st_index++; + } + } + + s->index = s->stored_index = 0; + s->nb_stored = st_count; + if (!st_count) + return AVERROR_INVALIDDATA; + ret = avio_read(pb, s->data, st_count * st->codecpar->sample_rate * bpp); + if (ret < 0) + return ret; + } + + st = ctx->streams[s->index]; + lafst = st->priv_data; + while (!lafst->stored) { + s->index++; + if (s->index >= ctx->nb_streams) + goto again; + lafst = ctx->streams[s->index]->priv_data; + } + st = ctx->streams[s->index]; + + ret = av_new_packet(pkt, st->codecpar->sample_rate * bpp); + if (ret < 0) + return ret; + + for (int n = 0; n < st->codecpar->sample_rate; n++) + memcpy(pkt->data + n * bpp, s->data + n * s->nb_stored * bpp + s->stored_index * bpp, bpp); + + pkt->stream_index = s->index; + pkt->pos = pos; + s->index++; + s->stored_index++; + + return ret; +} + +static int laf_read_seek(AVFormatContext *ctx, int stream_index, + int64_t timestamp, int flags) +{ + LAFContext *s = ctx->priv_data; + + s->stored_index = s->index = 0; + + return -1; +} + +const AVInputFormat ff_laf_demuxer = { + .name = "laf", + .long_name = NULL_IF_CONFIG_SMALL("LAF (Limitless Audio Format)"), + .priv_data_size = sizeof(LAFContext), + .read_probe = laf_probe, + .read_header = laf_read_header, + .read_packet = laf_read_packet, + .read_seek = laf_read_seek, + .extensions = "laf", + .flags = AVFMT_GENERIC_INDEX, +}; -- 2.37.2