librempeg/libavformat/dat.c
2024-09-12 22:46:43 +02:00

160 lines
4.5 KiB
C

/*
* DAT (Digital Audio Tape) demuxer
* Copyright (c) 2024 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 "avformat.h"
#include "demux.h"
#include "internal.h"
#define DAT_PACKET_SIZE 5822
#define DAT_OFFSET 5760
static const uint32_t encoded_rate[] = { 48000, 44100, 32000, 0 };
static const uint16_t encoded_size[] = { 5760, 5292, 3840, 0 };
static const uint8_t encoded_chans[] = { 2, 4, 0, 0 };
static const enum AVCodecID encoded_codec[] = {
AV_CODEC_ID_PCM_S16LE,
AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, AV_CODEC_ID_NONE,
};
static int valid_frame(uint8_t *frame)
{
uint8_t *scode = frame+DAT_OFFSET;
uint8_t *subid = scode+7*8;
uint8_t *mainid = subid+4;
int chan_index = (mainid[0] >> 0) & 0x3;
int rate_index = (mainid[0] >> 2) & 0x3;
int enc_index = (mainid[1] >> 6) & 0x3;
int dataid = (subid[0] >> 0) & 0xf;
if (dataid != 0 || encoded_codec[enc_index] == AV_CODEC_ID_NONE ||
encoded_chans[chan_index] == 0 ||
encoded_rate[rate_index] == 0)
return 0;
return 1;
}
static int read_probe(const AVProbeData *p)
{
const int cnt = p->buf_size / DAT_PACKET_SIZE;
int score = 0;
for (int i = 0; i < cnt; i++) {
const int ret = valid_frame(&p->buf[i * DAT_PACKET_SIZE]);
score += ret;
if (ret == 0)
break;
}
return FFMIN(score, AVPROBE_SCORE_MAX);
}
static int read_header(AVFormatContext *s)
{
s->ctx_flags |= AVFMTCTX_NOHEADER;
return 0;
}
static int parse_frame(uint8_t *frame, AVCodecParameters *par)
{
uint8_t *scode = frame+DAT_OFFSET;
uint8_t *subid = scode+7*8;
uint8_t *mainid = subid+4;
int chan_index = (mainid[0] >> 0) & 0x3;
int rate_index = (mainid[0] >> 2) & 0x3;
int enc_index = (mainid[1] >> 6) & 0x3;
int dataid = (subid[0] >> 0) & 0xf;
par->codec_type = AVMEDIA_TYPE_AUDIO;
par->codec_id = encoded_codec[enc_index];
av_channel_layout_default(&par->ch_layout, encoded_chans[chan_index]);
par->sample_rate = encoded_rate[rate_index];
par->bit_rate = (8LL * DAT_PACKET_SIZE * par->sample_rate) / FFMAX(1, av_get_audio_frame_duration2(par, encoded_size[rate_index]));
if (dataid != 0 || par->codec_id == AV_CODEC_ID_NONE ||
par->ch_layout.nb_channels <= 0 ||
par->sample_rate <= 0)
return AVERROR_INVALIDDATA;
return encoded_size[rate_index];
}
static int read_packet(AVFormatContext *s, AVPacket *pkt)
{
uint8_t data[DAT_PACKET_SIZE];
AVIOContext *pb = s->pb;
int ret, pcm_size;
int64_t pos;
if (avio_feof(pb))
return AVERROR_EOF;
pos = avio_tell(pb);
if (avio_read(pb, data, sizeof(data)) != sizeof(data))
return AVERROR_EOF;
if (s->nb_streams == 0) {
AVStream *st;
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
pcm_size = parse_frame(data, st->codecpar);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
} else {
int old_sample_rate = s->streams[0]->codecpar->sample_rate;
pcm_size = parse_frame(data, s->streams[0]->codecpar);
if (old_sample_rate != s->streams[0]->codecpar->sample_rate) {
ret = ff_add_param_change(pkt, 0, 0, s->streams[0]->codecpar->sample_rate, 0, 0);
if (ret < 0)
return ret;
}
}
if (pcm_size < 0)
return 0;
if ((ret = av_new_packet(pkt, pcm_size)) < 0)
return ret;
memcpy(pkt->data, data, pcm_size);
pkt->stream_index = 0;
pkt->pos = pos;
return 0;
}
const FFInputFormat ff_dat_demuxer = {
.p.name = "dat",
.p.long_name = NULL_IF_CONFIG_SMALL("DAT (Digital Audio Tape)"),
.p.flags = AVFMT_GENERIC_INDEX,
.p.extensions = "dat",
.read_probe = read_probe,
.read_header = read_header,
.read_packet = read_packet,
};