mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avcodec: Add support for Opus in MPEG-TS
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
05da586fef
commit
74141f693d
@ -290,10 +290,6 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
|
|||||||
OpusContext *s)
|
OpusContext *s)
|
||||||
{
|
{
|
||||||
static const uint8_t default_channel_map[2] = { 0, 1 };
|
static const uint8_t default_channel_map[2] = { 0, 1 };
|
||||||
uint8_t default_extradata[19] = {
|
|
||||||
'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
|
|
||||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
int (*channel_reorder)(int, int) = channel_reorder_unknown;
|
int (*channel_reorder)(int, int) = channel_reorder_unknown;
|
||||||
|
|
||||||
@ -308,9 +304,8 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
|
|||||||
"Multichannel configuration without extradata.\n");
|
"Multichannel configuration without extradata.\n");
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
default_extradata[9] = (avctx->channels == 1) ? 1 : 2;
|
extradata = opus_default_extradata;
|
||||||
extradata = default_extradata;
|
extradata_size = sizeof(opus_default_extradata);
|
||||||
extradata_size = sizeof(default_extradata);
|
|
||||||
} else {
|
} else {
|
||||||
extradata = avctx->extradata;
|
extradata = avctx->extradata;
|
||||||
extradata_size = avctx->extradata_size;
|
extradata_size = avctx->extradata_size;
|
||||||
@ -330,7 +325,7 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
|
|||||||
|
|
||||||
avctx->delay = AV_RL16(extradata + 10);
|
avctx->delay = AV_RL16(extradata + 10);
|
||||||
|
|
||||||
channels = extradata[9];
|
channels = avctx->extradata ? extradata[9] : (avctx->channels == 1) ? 1 : 2;
|
||||||
if (!channels) {
|
if (!channels) {
|
||||||
av_log(avctx, AV_LOG_ERROR, "Zero channel count specified in the extadata\n");
|
av_log(avctx, AV_LOG_ERROR, "Zero channel count specified in the extadata\n");
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
@ -61,6 +61,15 @@
|
|||||||
#define ROUND_MUL16(a,b) ((MUL16(a, b) + 16384) >> 15)
|
#define ROUND_MUL16(a,b) ((MUL16(a, b) + 16384) >> 15)
|
||||||
#define opus_ilog(i) (av_log2(i) + !!(i))
|
#define opus_ilog(i) (av_log2(i) + !!(i))
|
||||||
|
|
||||||
|
#define OPUS_TS_HEADER 0x7FE0 // 0x3ff (11 bits)
|
||||||
|
#define OPUS_TS_MASK 0xFFE0 // top 11 bits
|
||||||
|
|
||||||
|
static const uint8_t opus_default_extradata[30] = {
|
||||||
|
'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
enum OpusMode {
|
enum OpusMode {
|
||||||
OPUS_MODE_SILK,
|
OPUS_MODE_SILK,
|
||||||
OPUS_MODE_HYBRID,
|
OPUS_MODE_HYBRID,
|
||||||
|
@ -27,49 +27,162 @@
|
|||||||
|
|
||||||
#include "avcodec.h"
|
#include "avcodec.h"
|
||||||
#include "opus.h"
|
#include "opus.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "bytestream.h"
|
||||||
|
|
||||||
typedef struct OpusParseContext {
|
typedef struct OpusParseContext {
|
||||||
OpusContext ctx;
|
OpusContext ctx;
|
||||||
OpusPacket pkt;
|
OpusPacket pkt;
|
||||||
int extradata_parsed;
|
int extradata_parsed;
|
||||||
|
ParseContext pc;
|
||||||
|
int ts_framing;
|
||||||
} OpusParseContext;
|
} OpusParseContext;
|
||||||
|
|
||||||
static int opus_parse(AVCodecParserContext *ctx, AVCodecContext *avctx,
|
static const uint8_t *parse_opus_ts_header(const uint8_t *start, int *payload_len, int buf_len)
|
||||||
const uint8_t **poutbuf, int *poutbuf_size,
|
{
|
||||||
const uint8_t *buf, int buf_size)
|
const uint8_t *buf = start + 1;
|
||||||
|
int start_trim_flag, end_trim_flag, control_extension_flag, control_extension_length;
|
||||||
|
uint8_t flags;
|
||||||
|
|
||||||
|
GetByteContext gb;
|
||||||
|
bytestream2_init(&gb, buf, buf_len);
|
||||||
|
|
||||||
|
flags = bytestream2_get_byte(&gb);
|
||||||
|
start_trim_flag = (flags >> 4) & 1;
|
||||||
|
end_trim_flag = (flags >> 3) & 1;
|
||||||
|
control_extension_flag = (flags >> 2) & 1;
|
||||||
|
|
||||||
|
*payload_len = 0;
|
||||||
|
while (bytestream2_peek_byte(&gb) == 0xff)
|
||||||
|
*payload_len += bytestream2_get_byte(&gb);
|
||||||
|
|
||||||
|
*payload_len += bytestream2_get_byte(&gb);
|
||||||
|
|
||||||
|
if (start_trim_flag)
|
||||||
|
bytestream2_skip(&gb, 2);
|
||||||
|
if (end_trim_flag)
|
||||||
|
bytestream2_skip(&gb, 2);
|
||||||
|
if (control_extension_flag) {
|
||||||
|
control_extension_length = bytestream2_get_byte(&gb);
|
||||||
|
bytestream2_skip(&gb, control_extension_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf + bytestream2_tell(&gb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the end of the current frame in the bitstream.
|
||||||
|
* @return the position of the first byte of the next frame, or -1
|
||||||
|
*/
|
||||||
|
static int opus_find_frame_end(AVCodecParserContext *ctx, AVCodecContext *avctx,
|
||||||
|
const uint8_t *buf, int buf_size, int *header_len)
|
||||||
{
|
{
|
||||||
OpusParseContext *s = ctx->priv_data;
|
OpusParseContext *s = ctx->priv_data;
|
||||||
int ret;
|
ParseContext *pc = &s->pc;
|
||||||
|
int ret, start_found, i = 0, payload_len = 0;
|
||||||
|
const uint8_t *payload;
|
||||||
|
uint32_t state;
|
||||||
|
uint16_t hdr;
|
||||||
|
*header_len = 0;
|
||||||
|
|
||||||
if (!buf_size)
|
if (!buf_size)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
start_found = pc->frame_start_found;
|
||||||
|
state = pc->state;
|
||||||
|
payload = buf;
|
||||||
|
|
||||||
|
/* Check if we're using Opus in MPEG-TS framing */
|
||||||
|
if (!s->ts_framing && buf_size > 2) {
|
||||||
|
hdr = AV_RB16(buf);
|
||||||
|
if ((hdr & OPUS_TS_MASK) == OPUS_TS_HEADER)
|
||||||
|
s->ts_framing = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->ts_framing && !start_found) {
|
||||||
|
for (i = 0; i < buf_size-2; i++) {
|
||||||
|
state = (state << 8) | payload[i];
|
||||||
|
if ((state & OPUS_TS_MASK) == OPUS_TS_HEADER) {
|
||||||
|
payload = parse_opus_ts_header(payload, &payload_len, buf_size - i);
|
||||||
|
*header_len = payload - buf;
|
||||||
|
start_found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s->ts_framing)
|
||||||
|
payload_len = buf_size;
|
||||||
|
|
||||||
if (avctx->extradata && !s->extradata_parsed) {
|
if (avctx->extradata && !s->extradata_parsed) {
|
||||||
ret = ff_opus_parse_extradata(avctx, &s->ctx);
|
ret = ff_opus_parse_extradata(avctx, &s->ctx);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
av_log(avctx, AV_LOG_ERROR, "Error parsing Ogg extradata.\n");
|
av_log(avctx, AV_LOG_ERROR, "Error parsing Ogg extradata.\n");
|
||||||
goto fail;
|
return AVERROR_INVALIDDATA;
|
||||||
}
|
}
|
||||||
av_freep(&s->ctx.channel_maps);
|
av_freep(&s->ctx.channel_maps);
|
||||||
s->extradata_parsed = 1;
|
s->extradata_parsed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ff_opus_parse_packet(&s->pkt, buf, buf_size, s->ctx.nb_streams > 1);
|
if (payload_len <= buf_size && (!s->ts_framing || start_found)) {
|
||||||
if (ret < 0) {
|
ret = ff_opus_parse_packet(&s->pkt, payload, payload_len, s->ctx.nb_streams > 1);
|
||||||
av_log(avctx, AV_LOG_ERROR, "Error parsing Opus packet header.\n");
|
if (ret < 0) {
|
||||||
goto fail;
|
av_log(avctx, AV_LOG_ERROR, "Error parsing Opus packet header.\n");
|
||||||
|
pc->frame_start_found = 0;
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->duration = s->pkt.frame_count * s->pkt.frame_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->duration = s->pkt.frame_count * s->pkt.frame_duration;
|
if (s->ts_framing) {
|
||||||
|
if (start_found) {
|
||||||
|
if (payload_len + *header_len <= buf_size) {
|
||||||
|
pc->frame_start_found = 0;
|
||||||
|
pc->state = -1;
|
||||||
|
return payload_len + *header_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pc->frame_start_found = start_found;
|
||||||
|
pc->state = state;
|
||||||
|
return END_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
fail:
|
|
||||||
*poutbuf = buf;
|
|
||||||
*poutbuf_size = buf_size;
|
|
||||||
return buf_size;
|
return buf_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int opus_parse(AVCodecParserContext *ctx, AVCodecContext *avctx,
|
||||||
|
const uint8_t **poutbuf, int *poutbuf_size,
|
||||||
|
const uint8_t *buf, int buf_size)
|
||||||
|
{
|
||||||
|
OpusParseContext *s = ctx->priv_data;
|
||||||
|
ParseContext *pc = &s->pc;
|
||||||
|
int next, header_len;
|
||||||
|
|
||||||
|
next = opus_find_frame_end(ctx, avctx, buf, buf_size, &header_len);
|
||||||
|
|
||||||
|
if (s->ts_framing && next != AVERROR_INVALIDDATA &&
|
||||||
|
ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
|
||||||
|
*poutbuf = NULL;
|
||||||
|
*poutbuf_size = 0;
|
||||||
|
return buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == AVERROR_INVALIDDATA){
|
||||||
|
*poutbuf = NULL;
|
||||||
|
*poutbuf_size = 0;
|
||||||
|
return buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
*poutbuf = buf + header_len;
|
||||||
|
*poutbuf_size = buf_size - header_len;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
AVCodecParser ff_opus_parser = {
|
AVCodecParser ff_opus_parser = {
|
||||||
.codec_ids = { AV_CODEC_ID_OPUS },
|
.codec_ids = { AV_CODEC_ID_OPUS },
|
||||||
.priv_data_size = sizeof(OpusParseContext),
|
.priv_data_size = sizeof(OpusParseContext),
|
||||||
.parser_parse = opus_parse,
|
.parser_parse = opus_parse,
|
||||||
|
.parser_close = ff_parse_close
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include "libavutil/version.h"
|
#include "libavutil/version.h"
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_MAJOR 56
|
#define LIBAVCODEC_VERSION_MAJOR 56
|
||||||
#define LIBAVCODEC_VERSION_MINOR 9
|
#define LIBAVCODEC_VERSION_MINOR 10
|
||||||
#define LIBAVCODEC_VERSION_MICRO 100
|
#define LIBAVCODEC_VERSION_MICRO 100
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||||
|
Loading…
Reference in New Issue
Block a user