1/*
2 * Copyright (c) 2013-2014 Mozilla Corporation
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/**
22 * @file
23 * Opus parser
24 *
25 * Determines the duration for each packet.
26 */
27
28#include "avcodec.h"
29#include "bytestream.h"
30#include "opus.h"
31#include "parser.h"
32
33typedef struct OpusParseContext {
34    ParseContext pc;
35    OpusContext ctx;
36    OpusPacket pkt;
37    int extradata_parsed;
38    int ts_framing;
39} OpusParseContext;
40
41static const uint8_t *parse_opus_ts_header(const uint8_t *start, int *payload_len, int buf_len)
42{
43    const uint8_t *buf = start + 1;
44    int start_trim_flag, end_trim_flag, control_extension_flag, control_extension_length;
45    uint8_t flags;
46    uint64_t payload_len_tmp;
47
48    GetByteContext gb;
49    bytestream2_init(&gb, buf, buf_len);
50
51    flags = bytestream2_get_byte(&gb);
52    start_trim_flag        = (flags >> 4) & 1;
53    end_trim_flag          = (flags >> 3) & 1;
54    control_extension_flag = (flags >> 2) & 1;
55
56    payload_len_tmp = *payload_len = 0;
57    while (bytestream2_peek_byte(&gb) == 0xff)
58        payload_len_tmp += bytestream2_get_byte(&gb);
59
60    payload_len_tmp += bytestream2_get_byte(&gb);
61
62    if (start_trim_flag)
63        bytestream2_skip(&gb, 2);
64    if (end_trim_flag)
65        bytestream2_skip(&gb, 2);
66    if (control_extension_flag) {
67        control_extension_length = bytestream2_get_byte(&gb);
68        bytestream2_skip(&gb, control_extension_length);
69    }
70
71    if (bytestream2_tell(&gb) + payload_len_tmp > buf_len)
72        return NULL;
73
74    *payload_len = payload_len_tmp;
75
76    return buf + bytestream2_tell(&gb);
77}
78
79/**
80 * Find the end of the current frame in the bitstream.
81 * @return the position of the first byte of the next frame, or -1
82 */
83static int opus_find_frame_end(AVCodecParserContext *ctx, AVCodecContext *avctx,
84                               const uint8_t *buf, int buf_size, int *header_len)
85{
86    OpusParseContext *s = ctx->priv_data;
87    ParseContext *pc    = &s->pc;
88    int ret, start_found, i = 0, payload_len = 0;
89    const uint8_t *payload;
90    uint32_t state;
91    uint16_t hdr;
92    *header_len = 0;
93
94    if (!buf_size)
95        return 0;
96
97    start_found = pc->frame_start_found;
98    state = pc->state;
99    payload = buf;
100
101    /* Check if we're using Opus in MPEG-TS framing */
102    if (!s->ts_framing && buf_size > 2) {
103        hdr = AV_RB16(buf);
104        if ((hdr & OPUS_TS_MASK) == OPUS_TS_HEADER)
105            s->ts_framing = 1;
106    }
107
108    if (s->ts_framing && !start_found) {
109        for (i = 0; i < buf_size-2; i++) {
110            state = (state << 8) | payload[i];
111            if ((state & OPUS_TS_MASK) == OPUS_TS_HEADER) {
112                payload = parse_opus_ts_header(payload, &payload_len, buf_size - i);
113                if (!payload) {
114                    av_log(avctx, AV_LOG_ERROR, "Error parsing Ogg TS header.\n");
115                    return AVERROR_INVALIDDATA;
116                }
117                *header_len = payload - buf;
118                start_found = 1;
119                break;
120            }
121        }
122    }
123
124    if (!s->ts_framing)
125        payload_len = buf_size;
126
127    if (avctx->extradata && !s->extradata_parsed) {
128        ret = ff_opus_parse_extradata(avctx, &s->ctx);
129        if (ret < 0) {
130            av_log(avctx, AV_LOG_ERROR, "Error parsing Ogg extradata.\n");
131            return AVERROR_INVALIDDATA;
132        }
133        av_freep(&s->ctx.channel_maps);
134        s->extradata_parsed = 1;
135    }
136
137    if (payload_len <= buf_size && (!s->ts_framing || start_found)) {
138        ret = ff_opus_parse_packet(&s->pkt, payload, payload_len, s->ctx.nb_streams > 1);
139        if (ret < 0) {
140            av_log(avctx, AV_LOG_ERROR, "Error parsing Opus packet header.\n");
141            pc->frame_start_found = 0;
142            return AVERROR_INVALIDDATA;
143        }
144
145        ctx->duration = s->pkt.frame_count * s->pkt.frame_duration;
146    }
147
148    if (s->ts_framing) {
149        if (start_found) {
150            if (payload_len + *header_len <= buf_size) {
151                pc->frame_start_found = 0;
152                pc->state             = -1;
153                return payload_len + *header_len;
154            }
155        }
156
157        pc->frame_start_found = start_found;
158        pc->state = state;
159        return END_NOT_FOUND;
160    }
161
162    return buf_size;
163}
164
165static int opus_parse(AVCodecParserContext *ctx, AVCodecContext *avctx,
166                       const uint8_t **poutbuf, int *poutbuf_size,
167                       const uint8_t *buf, int buf_size)
168{
169    OpusParseContext *s = ctx->priv_data;
170    ParseContext *pc    = &s->pc;
171    int next, header_len;
172
173    next = opus_find_frame_end(ctx, avctx, buf, buf_size, &header_len);
174
175    if (s->ts_framing && next != AVERROR_INVALIDDATA &&
176        ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
177        *poutbuf      = NULL;
178        *poutbuf_size = 0;
179        return buf_size;
180    }
181
182    if (next == AVERROR_INVALIDDATA){
183        *poutbuf      = NULL;
184        *poutbuf_size = 0;
185        return buf_size;
186    }
187
188    *poutbuf      = buf + header_len;
189    *poutbuf_size = buf_size - header_len;
190    return next;
191}
192
193const AVCodecParser ff_opus_parser = {
194    .codec_ids      = { AV_CODEC_ID_OPUS },
195    .priv_data_size = sizeof(OpusParseContext),
196    .parser_parse   = opus_parse,
197    .parser_close   = ff_parse_close
198};
199