yading@11: /* yading@11: * Opus parser for Ogg yading@11: * Copyright (c) 2012 Nicolas George yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include yading@11: yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #include "oggdec.h" yading@11: yading@11: struct oggopus_private { yading@11: int need_comments; yading@11: unsigned pre_skip; yading@11: int64_t cur_dts; yading@11: }; yading@11: yading@11: #define OPUS_HEAD_SIZE 19 yading@11: yading@11: static int opus_header(AVFormatContext *avf, int idx) yading@11: { yading@11: struct ogg *ogg = avf->priv_data; yading@11: struct ogg_stream *os = &ogg->streams[idx]; yading@11: AVStream *st = avf->streams[idx]; yading@11: struct oggopus_private *priv = os->private; yading@11: uint8_t *packet = os->buf + os->pstart; yading@11: uint8_t *extradata; yading@11: yading@11: if (!priv) { yading@11: priv = os->private = av_mallocz(sizeof(*priv)); yading@11: if (!priv) yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: if (os->flags & OGG_FLAG_BOS) { yading@11: if (os->psize < OPUS_HEAD_SIZE || (AV_RL8(packet + 8) & 0xF0) != 0) yading@11: return AVERROR_INVALIDDATA; yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = AV_CODEC_ID_OPUS; yading@11: st->codec->channels = AV_RL8 (packet + 9); yading@11: priv->pre_skip = AV_RL16(packet + 10); yading@11: /*orig_sample_rate = AV_RL32(packet + 12);*/ yading@11: /*gain = AV_RL16(packet + 16);*/ yading@11: /*channel_map = AV_RL8 (packet + 18);*/ yading@11: yading@11: extradata = av_malloc(os->psize + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!extradata) yading@11: return AVERROR(ENOMEM); yading@11: memcpy(extradata, packet, os->psize); yading@11: st->codec->extradata = extradata; yading@11: st->codec->extradata_size = os->psize; yading@11: yading@11: st->codec->sample_rate = 48000; yading@11: avpriv_set_pts_info(st, 64, 1, 48000); yading@11: priv->need_comments = 1; yading@11: return 1; yading@11: } yading@11: yading@11: if (priv->need_comments) { yading@11: if (os->psize < 8 || memcmp(packet, "OpusTags", 8)) yading@11: return AVERROR_INVALIDDATA; yading@11: ff_vorbis_comment(avf, &st->metadata, packet + 8, os->psize - 8); yading@11: priv->need_comments--; yading@11: return 1; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static int opus_packet(AVFormatContext *avf, int idx) yading@11: { yading@11: struct ogg *ogg = avf->priv_data; yading@11: struct ogg_stream *os = &ogg->streams[idx]; yading@11: AVStream *st = avf->streams[idx]; yading@11: struct oggopus_private *priv = os->private; yading@11: uint8_t *packet = os->buf + os->pstart; yading@11: unsigned toc, toc_config, toc_count, frame_size, nb_frames = 1; yading@11: yading@11: if (!os->psize) yading@11: return AVERROR_INVALIDDATA; yading@11: toc = *packet; yading@11: toc_config = toc >> 3; yading@11: toc_count = toc & 3; yading@11: frame_size = toc_config < 12 ? FFMAX(480, 960 * (toc_config & 3)) : yading@11: toc_config < 16 ? 480 << (toc_config & 1) : yading@11: 120 << (toc_config & 3); yading@11: if (toc_count == 3) { yading@11: if (os->psize < 2) yading@11: return AVERROR_INVALIDDATA; yading@11: nb_frames = packet[1] & 0x3F; yading@11: } else if (toc_count) { yading@11: nb_frames = 2; yading@11: } yading@11: os->pduration = frame_size * nb_frames; yading@11: if (os->lastpts != AV_NOPTS_VALUE) { yading@11: if (st->start_time == AV_NOPTS_VALUE) yading@11: st->start_time = os->lastpts; yading@11: priv->cur_dts = os->lastdts = os->lastpts -= priv->pre_skip; yading@11: } yading@11: priv->cur_dts += os->pduration; yading@11: if ((os->flags & OGG_FLAG_EOS)) { yading@11: int64_t skip = priv->cur_dts - os->granule + priv->pre_skip; yading@11: skip = FFMIN(skip, os->pduration); yading@11: if (skip > 0) { yading@11: os->pduration = skip < os->pduration ? os->pduration - skip : 1; yading@11: av_log(avf, AV_LOG_WARNING, yading@11: "Last packet must be truncated to %d (unimplemented).\n", yading@11: os->pduration); yading@11: } yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: const struct ogg_codec ff_opus_codec = { yading@11: .name = "Opus", yading@11: .magic = "OpusHead", yading@11: .magicsize = 8, yading@11: .header = opus_header, yading@11: .packet = opus_packet, yading@11: };