external_ffmpeg/libavformat/ldacdec.c

263 lines
7.8 KiB
C

/*
* LDAC Audio Demuxer
*
* 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
* LDAC Audio Demuxer
*/
#include "avformat.h"
#include "demux.h"
#include "internal.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/avstring.h"
#include "ldacBT.h"
#define SAMPLE_RATE_44100_INDEX 0
#define SAMPLE_RATE_48000_INDEX 1
#define SAMPLE_RATE_88200_INDEX 2
#define SAMPLE_RATE_96000_INDEX 3
#define SAMPLE_RATE_176400_INDEX 4
#define SAMPLE_RATE_192000_INDEX 5
#define MONO_CHANNEL_CONFIG_INDEX 0
#define DUAL_CHANNEL_CONFIG_INDEX 1
#define STEREO_CHANNEL_CONFIG_INDEX 2
#define LDAC_HEADER_SIZE 8
#define LDAC_SYNC_WORD_SIZE 1
#define FS_INFO_MASK 0x3F
#define CM_MASK 0x07
#define SAMPLE_RATE_MASK 0x07
#define CHANNEL_CONFIG_MASK 0x03
#define MQ_SYNC_WORD_INDEX 55
#define SQ_SYNC_WORD_INDEX 110
#define HQ_SYNC_WORD_INDEX 165
#define LDAC_BUFFER_SIZE 400
typedef struct LDACContext {
const AVClass *class; ///< class for private options
char *sfmt_str;
} LDACContext;
static int ldac_probe(const AVProbeData *p)
{
if (p->buf[0] == LDACBT_VENDOR_ID0 &&
p->buf[1] == LDACBT_VENDOR_ID1 &&
p->buf[2] == LDACBT_VENDOR_ID2 &&
p->buf[3] == LDACBT_VENDOR_ID3 &&
p->buf[4] == LDACBT_CODEC_ID0 &&
p->buf[5] == LDACBT_CODEC_ID1) {
return AVPROBE_SCORE_MAX;
} else if (p->buf[0] == 0xAA) {
return AVPROBE_SCORE_MAX;
} else
return 0;
}
static int find_frame_len(unsigned char *buffer, int size, int channels)
{
if(size <= HQ_SYNC_WORD_INDEX * channels)
return 0;
if (buffer[MQ_SYNC_WORD_INDEX * channels] == 0xAA)
return MQ_SYNC_WORD_INDEX * channels;
else if (buffer[SQ_SYNC_WORD_INDEX * channels] == 0xAA)
return SQ_SYNC_WORD_INDEX * channels;
else if (buffer[HQ_SYNC_WORD_INDEX * channels] == 0xAA)
return HQ_SYNC_WORD_INDEX * channels;
return 0;
}
static int parse_header(AVFormatContext *s, int *sf, int *channels, int *frame_size)
{
int ret = AVERROR(EINVAL);
unsigned char *buffer;
int buffer_offset;
int buffer_size;
buffer = av_mallocz(LDAC_BUFFER_SIZE);
if (!buffer)
return AVERROR(ENOMEM);
if (ret = avio_read(s->pb, buffer, LDAC_BUFFER_SIZE) < 0)
goto fail;
if (buffer[0] == 0xAA) {
avio_seek(s->pb, 0, SEEK_SET);
/* get sampling frequency */
switch ((buffer[1] >> 5) & SAMPLE_RATE_MASK) {
case SAMPLE_RATE_44100_INDEX: *sf = 44100; break;
case SAMPLE_RATE_48000_INDEX: *sf = 48000; break;
case SAMPLE_RATE_88200_INDEX: *sf = 88200; break;
case SAMPLE_RATE_96000_INDEX: *sf = 96000; break;
case SAMPLE_RATE_176400_INDEX: *sf = 176400; break;
case SAMPLE_RATE_192000_INDEX: *sf = 192000; break;
default:
av_log(s, AV_LOG_ERROR, "Unsupported Sampling Frequency Info 0x%02x!\n",
(buffer[1] >> 5) & SAMPLE_RATE_MASK);
goto fail;
}
/* get channel mode & channel */
switch ((buffer[1] >> 3) & CHANNEL_CONFIG_MASK) {
case MONO_CHANNEL_CONFIG_INDEX:
*channels = 1;
break;
case DUAL_CHANNEL_CONFIG_INDEX:
case STEREO_CHANNEL_CONFIG_INDEX:
*channels = 2;
break;
default:
av_log(s, AV_LOG_ERROR, "Unsupported Channel Info 0x%02x!\n",
(buffer[1] >> 3) & CHANNEL_CONFIG_MASK);
goto fail;
}
buffer_offset = 0;
buffer_size = LDAC_BUFFER_SIZE - LDAC_SYNC_WORD_SIZE;
} else {
avio_seek(s->pb, 8, SEEK_SET);
if (buffer[6] & LDACBT_SAMPLING_FREQ_192000)
*sf = 192000;
else if (buffer[6] & LDACBT_SAMPLING_FREQ_176400)
*sf = 176400;
else if (buffer[6] & LDACBT_SAMPLING_FREQ_096000)
*sf = 96000;
else if (buffer[6] & LDACBT_SAMPLING_FREQ_088200)
*sf = 88200;
else if (buffer[6] & LDACBT_SAMPLING_FREQ_048000)
*sf = 48000;
else if (buffer[6] & LDACBT_SAMPLING_FREQ_044100)
*sf = 44100;
else {
av_log(s, AV_LOG_ERROR, "Unsupported FS INFO 0x%02x!\n", buffer[6] & FS_INFO_MASK);
goto fail;
}
/* get channel mode & channel */
switch (buffer[7] & CM_MASK) {
case LDACBT_CHANNEL_MODE_MONO:
*channels = 1;
break;
case LDACBT_CHANNEL_MODE_DUAL_CHANNEL:
case LDACBT_CHANNEL_MODE_STEREO:
*channels = 2;
break;
default:
av_log(s, AV_LOG_ERROR, "Unsupported Channel Mode 0x%02x!\n", buffer[7] & CM_MASK);
goto fail;
}
buffer_offset = LDAC_HEADER_SIZE;
buffer_size = LDAC_BUFFER_SIZE - LDAC_HEADER_SIZE;
}
*frame_size = find_frame_len(buffer + buffer_offset, buffer_size, *channels);
if (*frame_size == 0)
goto fail;
av_freep(&buffer);
return 0;
fail:
av_freep(&buffer);
return ret;
}
static int ldac_read_header(AVFormatContext *s)
{
LDACContext *ldac = s->priv_data;
AVStream *st;
int sample_rate;
int channels;
int frame_size;
int ret;
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
if (ret = parse_header(s, &sample_rate, &channels, &frame_size) < 0)
return ret;
st->codecpar->format = AV_SAMPLE_FMT_S16;
if (ldac->sfmt_str != NULL) {
if (!av_strcasecmp(ldac->sfmt_str, "s32"))
st->codecpar->format = AV_SAMPLE_FMT_S32;
else if (!av_strcasecmp(ldac->sfmt_str, "flt"))
st->codecpar->format = AV_SAMPLE_FMT_FLT;
}
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_LDAC;
st->codecpar->sample_rate = sample_rate;
st->codecpar->frame_size = frame_size;
av_channel_layout_default(&st->codecpar->ch_layout, channels);
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
return 0;
}
static int ldac_read_packet(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st = s->streams[0];
if (avio_feof(s->pb))
return AVERROR_EOF;
return av_get_packet(s->pb, pkt, st->codecpar->frame_size);
}
static const AVOption ldac_options[] = {
{"sfmt", "ldac pcm sample format", offsetof(LDACContext, sfmt_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM},
{NULL},
};
static const AVClass ldac_class = {
.class_name = "ldac",
.item_name = av_default_item_name,
.option = ldac_options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFInputFormat ff_ldac_demuxer = {
.p.name = "ldac",
.p.long_name = NULL_IF_CONFIG_SMALL("LDAC Audio Demuxer"),
.p.priv_class = &ldac_class,
.priv_data_size = sizeof(LDACContext),
.read_probe = ldac_probe,
.read_header = ldac_read_header,
.read_packet = ldac_read_packet,
.p.flags = AVFMT_GENERIC_INDEX,
.p.extensions = "ldac",
.p.mime_type = "audio/ldac",
.raw_codec_id = AV_CODEC_ID_LDAC,
};