From a13210ab398c83ecd5eba96c3fe0d93827ea80e1 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Tue, 31 Jan 2023 19:27:09 +0100 Subject: [PATCH] avformat: add RKA demuxer --- Changelog | 2 +- doc/general_contents.texi | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/rka.c | 173 ++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 2 +- 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 libavformat/rka.c diff --git a/Changelog b/Changelog index b70835a7fe..cfe2b8a770 100644 --- a/Changelog +++ b/Changelog @@ -41,7 +41,7 @@ version : - WavArc decoder and demuxer - CrystalHD decoders deprecated - SDNS demuxer -- RKA decoder +- RKA decoder and demuxer version 5.1: diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 2f9ef1544d..2eeebd847d 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -579,6 +579,7 @@ library: @item Ogg @tab X @tab X @item Playstation Portable PMP @tab @tab X @item Portable Voice Format @tab @tab X +@item RK Audio (RKA) @tab @tab X @item TechnoTrend PVA @tab @tab X @tab Used by TechnoTrend DVB PCI boards. @item QCP @tab @tab X diff --git a/libavformat/Makefile b/libavformat/Makefile index 471e014b22..47bbbbfb2a 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -482,6 +482,7 @@ OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o +OBJS-$(CONFIG_RKA_DEMUXER) += rka.o apetag.o img2.o OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 8393dd34c7..cb5b69e9cd 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -381,6 +381,7 @@ extern const AVInputFormat ff_rawvideo_demuxer; extern const FFOutputFormat ff_rawvideo_muxer; extern const AVInputFormat ff_realtext_demuxer; extern const AVInputFormat ff_redspark_demuxer; +extern const AVInputFormat ff_rka_demuxer; extern const AVInputFormat ff_rl2_demuxer; extern const AVInputFormat ff_rm_demuxer; extern const FFOutputFormat ff_rm_muxer; diff --git a/libavformat/rka.c b/libavformat/rka.c new file mode 100644 index 0000000000..c1e64ebcb3 --- /dev/null +++ b/libavformat/rka.c @@ -0,0 +1,173 @@ +/* + * RKA demuxer + * Copyright (c) 2023 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/dict.h" +#include "libavutil/intreadwrite.h" + +#include "apetag.h" +#include "avformat.h" +#include "avio_internal.h" +#include "demux.h" +#include "internal.h" + +typedef struct RKAContext { + int total_frames, currentframe; + int frame_size; + int last_frame_size; +} RKAContext; + +static int rka_probe(const AVProbeData *p) +{ + if (AV_RL32(&p->buf[0]) == MKTAG('R', 'K', 'A', '7') && + AV_RL32(&p->buf[4]) > 0 && + AV_RL32(&p->buf[8]) > 0 && + p->buf[12] > 0 && + p->buf[12] <= 2 && + (p->buf[13] == 8 || p->buf[13] == 16) && + p->buf[15] & 2 != 0) + return AVPROBE_SCORE_EXTENSION + 30; + return 0; +} + +static int rka_read_header(AVFormatContext *s) +{ + int64_t nb_samples, size_offset; + RKAContext *c = s->priv_data; + int channels, bps, samplerate; + AVCodecParameters *par; + int64_t framepos; + AVStream *st; + int ret; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + ret = ff_get_extradata(s, par, s->pb, 16); + if (ret < 0) + return ret; + + nb_samples = AV_RL32(par->extradata + 4); + samplerate = AV_RL32(par->extradata + 8); + channels = par->extradata[12]; + if (channels == 0) + return AVERROR_INVALIDDATA; + bps = par->extradata[13]; + if (bps == 0) + return AVERROR_INVALIDDATA; + size_offset = avio_rl32(s->pb); + framepos = avio_tell(s->pb); + c->frame_size = 131072; + + avpriv_set_pts_info(st, 64, 1, samplerate); + st->start_time = 0; + + avio_seek(s->pb, size_offset, SEEK_SET); + c->total_frames = (nb_samples + c->frame_size - 1) / c->frame_size; + c->last_frame_size = nb_samples % c->frame_size; + + for (int i = 0; i < c->total_frames; i++) { + int r, end = 0; + int64_t size; + + if (avio_feof(s->pb)) + break; + + size = avio_rl24(s->pb); + if (size == 0) { + end = 1; + size = size_offset - framepos; + if (size <= 0) + break; + } + + if ((r = av_add_index_entry(st, framepos, (i * 131072LL) / (channels * (bps >> 3)), + size, 0, AVINDEX_KEYFRAME)) < 0) + return r; + framepos += size; + + if (end) + break; + } + + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_RKA; + par->ch_layout.nb_channels = channels; + par->sample_rate = samplerate; + par->bits_per_raw_sample = bps; + st->duration = nb_samples / (channels * (bps >> 3)); + + if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) + ff_ape_parse_tag(s); + + avio_seek(s->pb, 20, SEEK_SET); + + return 0; +} + +static int rka_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + RKAContext *c = s->priv_data; + AVStream *st = s->streams[0]; + FFStream *const sti = ffstream(st); + int size, ret; + + if (avio_feof(s->pb)) + return AVERROR_EOF; + + if (c->currentframe >= sti->nb_index_entries) + return AVERROR_EOF; + + size = sti->index_entries[c->currentframe].size; + + ret = av_get_packet(s->pb, pkt, size); + pkt->dts = sti->index_entries[c->currentframe++].timestamp; + pkt->duration = c->currentframe == c->total_frames ? c->last_frame_size : + 131072; + return ret; +} + +static int rka_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + RKAContext *c = s->priv_data; + AVStream *st = s->streams[stream_index]; + int index = av_index_search_timestamp(st, timestamp, flags); + if (index < 0) + return -1; + if (avio_seek(s->pb, ffstream(st)->index_entries[index].pos, SEEK_SET) < 0) + return -1; + + c->currentframe = index; + + return 0; +} + +const AVInputFormat ff_rka_demuxer = { + .name = "rka", + .long_name = NULL_IF_CONFIG_SMALL("RKA (RK Audio)"), + .priv_data_size = sizeof(RKAContext), + .read_probe = rka_probe, + .read_header = rka_read_header, + .read_packet = rka_read_packet, + .read_seek = rka_read_seek, + .extensions = "rka", +}; diff --git a/libavformat/version.h b/libavformat/version.h index a7c80dc564..904e7f06aa 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #include "version_major.h" -#define LIBAVFORMAT_VERSION_MINOR 1 +#define LIBAVFORMAT_VERSION_MINOR 2 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \