yading@11: /* yading@11: Copyright (C) 2008 Reimar Döffinger yading@11: yading@11: Permission is hereby granted, free of charge, to any person yading@11: obtaining a copy of this software and associated documentation yading@11: files (the "Software"), to deal in the Software without yading@11: restriction, including without limitation the rights to use, copy, yading@11: modify, merge, publish, distribute, sublicense, and/or sell copies yading@11: of the Software, and to permit persons to whom the Software is yading@11: furnished to do so, subject to the following conditions: yading@11: yading@11: The above copyright notice and this permission notice shall be yading@11: included in all copies or substantial portions of the Software. yading@11: yading@11: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, yading@11: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF yading@11: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND yading@11: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT yading@11: HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, yading@11: WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, yading@11: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER yading@11: DEALINGS IN THE SOFTWARE. yading@11: **/ yading@11: yading@11: #include yading@11: #include "libavutil/bswap.h" yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/channel_layout.h" yading@11: #include "libavcodec/get_bits.h" yading@11: #include "libavcodec/bytestream.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #include "oggdec.h" yading@11: yading@11: struct speex_params { yading@11: int packet_size; yading@11: int final_packet_duration; yading@11: int seq; yading@11: }; yading@11: yading@11: static int speex_header(AVFormatContext *s, int idx) { yading@11: struct ogg *ogg = s->priv_data; yading@11: struct ogg_stream *os = ogg->streams + idx; yading@11: struct speex_params *spxp = os->private; yading@11: AVStream *st = s->streams[idx]; yading@11: uint8_t *p = os->buf + os->pstart; yading@11: yading@11: if (!spxp) { yading@11: spxp = av_mallocz(sizeof(*spxp)); yading@11: os->private = spxp; yading@11: } yading@11: yading@11: if (spxp->seq > 1) yading@11: return 0; yading@11: yading@11: if (spxp->seq == 0) { yading@11: int frames_per_packet; yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = AV_CODEC_ID_SPEEX; yading@11: yading@11: if (os->psize < 68) { yading@11: av_log(s, AV_LOG_ERROR, "speex packet too small\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: st->codec->sample_rate = AV_RL32(p + 36); yading@11: st->codec->channels = AV_RL32(p + 48); yading@11: if (st->codec->channels < 1 || st->codec->channels > 2) { yading@11: av_log(s, AV_LOG_ERROR, "invalid channel count. Speex must be mono or stereo.\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: st->codec->channel_layout = st->codec->channels == 1 ? AV_CH_LAYOUT_MONO : yading@11: AV_CH_LAYOUT_STEREO; yading@11: yading@11: spxp->packet_size = AV_RL32(p + 56); yading@11: frames_per_packet = AV_RL32(p + 64); yading@11: if (frames_per_packet) yading@11: spxp->packet_size *= frames_per_packet; yading@11: yading@11: st->codec->extradata_size = os->psize; yading@11: st->codec->extradata = av_malloc(st->codec->extradata_size yading@11: + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: memcpy(st->codec->extradata, p, st->codec->extradata_size); yading@11: yading@11: avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); yading@11: } else yading@11: ff_vorbis_comment(s, &st->metadata, p, os->psize); yading@11: yading@11: spxp->seq++; yading@11: return 1; yading@11: } yading@11: yading@11: static int ogg_page_packets(struct ogg_stream *os) yading@11: { yading@11: int i; yading@11: int packets = 0; yading@11: for (i = 0; i < os->nsegs; i++) yading@11: if (os->segments[i] < 255) yading@11: packets++; yading@11: return packets; yading@11: } yading@11: yading@11: static int speex_packet(AVFormatContext *s, int idx) yading@11: { yading@11: struct ogg *ogg = s->priv_data; yading@11: struct ogg_stream *os = ogg->streams + idx; yading@11: struct speex_params *spxp = os->private; yading@11: int packet_size = spxp->packet_size; yading@11: yading@11: if (os->flags & OGG_FLAG_EOS && os->lastpts != AV_NOPTS_VALUE && yading@11: os->granule > 0) { yading@11: /* first packet of final page. we have to calculate the final packet yading@11: duration here because it is the only place we know the next-to-last yading@11: granule position. */ yading@11: spxp->final_packet_duration = os->granule - os->lastpts - yading@11: packet_size * (ogg_page_packets(os) - 1); yading@11: } yading@11: yading@11: if (!os->lastpts && os->granule > 0) yading@11: /* first packet */ yading@11: os->lastpts = os->lastdts = os->granule - packet_size * yading@11: ogg_page_packets(os); yading@11: if (os->flags & OGG_FLAG_EOS && os->segp == os->nsegs && yading@11: spxp->final_packet_duration) yading@11: /* final packet */ yading@11: os->pduration = spxp->final_packet_duration; yading@11: else yading@11: os->pduration = packet_size; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: const struct ogg_codec ff_speex_codec = { yading@11: .magic = "Speex ", yading@11: .magicsize = 8, yading@11: .header = speex_header, yading@11: .packet = speex_packet, yading@11: .nb_header = 2, yading@11: };