234 lines
5.5 KiB
C
234 lines
5.5 KiB
C
/*
|
|
* DRA parser
|
|
* Copyright (c) 2026 Amlogic, Inc.
|
|
*
|
|
* 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 "parser.h"
|
|
#include "get_bits.h"
|
|
#include "libavcodec/internal.h"
|
|
#include "libavutil/log.h"
|
|
|
|
typedef struct DRAParseContext {
|
|
int sample_rate;
|
|
int channels;
|
|
|
|
/* Lightweight state only (no heap buffer management) */
|
|
int last_sync_pos;
|
|
} DRAParseContext;
|
|
|
|
/* DRA sample rate table */
|
|
static const int dra_sample_rates[13] = {
|
|
8000, 11025, 12000, 16000, 22050, 24000, 32000,
|
|
44100, 48000, 88200, 96000, 176400, 192000
|
|
};
|
|
|
|
/* Find sync word 0x7FFF */
|
|
static int dra_find_sync(const uint8_t *buf, int buf_size)
|
|
{
|
|
int i;
|
|
for (i = 0; i < buf_size - 1; i++) {
|
|
if (buf[i] == 0x7F && buf[i + 1] == 0xFF)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Parse DRA frame header safely */
|
|
static int dra_parse_header(const uint8_t *buf,
|
|
int buf_size,
|
|
int *frame_size,
|
|
int *sample_rate,
|
|
int *channels)
|
|
{
|
|
GetBitContext gb;
|
|
int frm_header_type;
|
|
int num_word;
|
|
int sr_idx;
|
|
int num_normal_ch;
|
|
int num_lfe_ch;
|
|
|
|
if (buf_size < 8)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
init_get_bits8(&gb, buf, buf_size);
|
|
|
|
/* Sync word */
|
|
if (get_bits(&gb, 16) != 0x7FFF)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
frm_header_type = get_bits1(&gb);
|
|
|
|
/* Frame size in 32-bit words */
|
|
if (frm_header_type == 0)
|
|
num_word = get_bits(&gb, 10);
|
|
else
|
|
num_word = get_bits(&gb, 13);
|
|
|
|
*frame_size = num_word * 4;
|
|
|
|
/* Basic sanity check */
|
|
if (*frame_size < 32 || *frame_size > 8192)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
/* Blocks per frame (ignored but must consume bits) */
|
|
get_bits(&gb, 2);
|
|
|
|
/* Sample rate index */
|
|
sr_idx = get_bits(&gb, 4);
|
|
if (sr_idx > 12)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
*sample_rate = dra_sample_rates[sr_idx];
|
|
|
|
/* Channel parsing */
|
|
if (frm_header_type == 0) {
|
|
num_normal_ch = get_bits(&gb, 3) + 1;
|
|
num_lfe_ch = get_bits1(&gb);
|
|
} else {
|
|
num_normal_ch = get_bits(&gb, 6) + 1;
|
|
num_lfe_ch = get_bits(&gb, 2);
|
|
}
|
|
|
|
*channels = num_normal_ch + num_lfe_ch;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Find frame boundary */
|
|
static int dra_find_frame_end(AVCodecParserContext *s,
|
|
const uint8_t *buf,
|
|
int buf_size)
|
|
{
|
|
int frame_size;
|
|
int sr, ch;
|
|
|
|
if (buf_size < 8)
|
|
return END_NOT_FOUND;
|
|
|
|
/* Try to parse header */
|
|
if (dra_parse_header(buf, buf_size,
|
|
&frame_size, &sr, &ch) < 0)
|
|
return END_NOT_FOUND;
|
|
|
|
/* Not enough data */
|
|
if (buf_size < frame_size)
|
|
return END_NOT_FOUND;
|
|
|
|
return frame_size;
|
|
}
|
|
|
|
/*
|
|
* Split function (used for resync)
|
|
* Must be conservative and safe.
|
|
*/
|
|
static int dra_split(AVCodecContext *avctx,
|
|
const uint8_t *buf,
|
|
int buf_size)
|
|
{
|
|
int pos = dra_find_sync(buf, buf_size);
|
|
|
|
if (pos < 0)
|
|
return buf_size; /* drop all if no sync found */
|
|
|
|
return pos;
|
|
}
|
|
|
|
/*
|
|
* Main parser function
|
|
*/
|
|
static int dra_parse(AVCodecParserContext *s,
|
|
AVCodecContext *avctx,
|
|
const uint8_t **outbuf,
|
|
int *outbuf_size,
|
|
const uint8_t *buf,
|
|
int buf_size)
|
|
{
|
|
DRAParseContext *ctx = s->priv_data;
|
|
|
|
int offset = 0;
|
|
|
|
/* Step 1: find sync */
|
|
offset = dra_find_sync(buf, buf_size);
|
|
if (offset < 0) {
|
|
*outbuf = NULL;
|
|
*outbuf_size = 0;
|
|
return buf_size;
|
|
}
|
|
|
|
buf += offset;
|
|
buf_size -= offset;
|
|
|
|
/* Step 2: find frame size */
|
|
int frame_size = dra_find_frame_end(s, buf, buf_size);
|
|
if (frame_size == END_NOT_FOUND) {
|
|
*outbuf = NULL;
|
|
*outbuf_size = 0;
|
|
return offset ? offset : buf_size;
|
|
}
|
|
|
|
/* Step 3: parse header (for codec config only) */
|
|
int sr = 0, ch = 0;
|
|
if (dra_parse_header(buf, buf_size,
|
|
&frame_size, &sr, &ch) == 0) {
|
|
|
|
avctx->sample_rate = sr;
|
|
avctx->channels = ch;
|
|
|
|
ctx->sample_rate = sr;
|
|
ctx->channels = ch;
|
|
}
|
|
|
|
/* Fixed DRA frame duration (typical) */
|
|
s->duration = 1024;
|
|
|
|
/* Output frame */
|
|
*outbuf = buf;
|
|
*outbuf_size = frame_size;
|
|
|
|
/* Consumed bytes */
|
|
return offset + frame_size;
|
|
}
|
|
|
|
/*
|
|
* Safe close
|
|
*/
|
|
static void dra_close(AVCodecParserContext *s)
|
|
{
|
|
DRAParseContext *ctx = s->priv_data;
|
|
|
|
av_log(NULL, AV_LOG_INFO,
|
|
"[DRA] parser close ctx=%p\n",
|
|
ctx);
|
|
|
|
if (ctx) {
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parser definition
|
|
*/
|
|
AVCodecParser ff_dra_parser = {
|
|
.codec_ids = { AV_CODEC_ID_DRA },
|
|
.priv_data_size = sizeof(DRAParseContext),
|
|
.parser_parse = dra_parse,
|
|
.parser_close = dra_close,
|
|
.split = dra_split,
|
|
};
|