A G: > From 16e0b79bcdc029b7ab34dfbf80b50868743a8e5e Mon Sep 17 00:00:00 2001 > From: oreo639 <31916379+oreo...@users.noreply.github.com> > Date: Sun, 24 May 2020 22:28:51 -0700 > Subject: [PATCH] avformat/OggPCM: Add partial support > > This adds partial support for OggPCM muxing and playing. > Heavily based on the work here: > https://ffmpeg.org/pipermail/ffmpeg-devel/2013-July/145556.html > and here: > http://www.on2.com/media/gpl/mplayer/ > --- > libavformat/Makefile | 1 + > libavformat/oggdec.c | 1 + > libavformat/oggdec.h | 1 + > libavformat/oggenc.c | 84 ++++++++++++++++++++++++-- > libavformat/oggparsepcm.c | 123 ++++++++++++++++++++++++++++++++++++++ > 5 files changed, 204 insertions(+), 6 deletions(-) > create mode 100644 libavformat/oggparsepcm.c > > diff --git a/libavformat/Makefile b/libavformat/Makefile > index 2b634ecbfc..41df74fc4f 100644 > --- a/libavformat/Makefile > +++ b/libavformat/Makefile > @@ -364,6 +364,7 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o > \ > oggparseflac.o \ > oggparseogm.o \ > oggparseopus.o \ > + oggparsepcm.o \ > oggparseskeleton.o \ > oggparsespeex.o \ > oggparsetheora.o \ > diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c > index 1a3acbb55e..6a95485680 100644 > --- a/libavformat/oggdec.c > +++ b/libavformat/oggdec.c > @@ -49,6 +49,7 @@ static const struct ogg_codec * const ogg_codecs[] = { > &ff_flac_codec, > &ff_celt_codec, > &ff_opus_codec, > + &ff_pcm_codec, > &ff_vp8_codec, > &ff_old_dirac_codec, > &ff_old_flac_codec, > diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h > index 629a1d6262..ae001b4ded 100644 > --- a/libavformat/oggdec.h > +++ b/libavformat/oggdec.h > @@ -124,6 +124,7 @@ extern const struct ogg_codec ff_ogm_video_codec; > extern const struct ogg_codec ff_old_dirac_codec; > extern const struct ogg_codec ff_old_flac_codec; > extern const struct ogg_codec ff_opus_codec; > +extern const struct ogg_codec ff_pcm_codec; > extern const struct ogg_codec ff_skeleton_codec; > extern const struct ogg_codec ff_speex_codec; > extern const struct ogg_codec ff_theora_codec; > diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c > index f5032759a6..ad7cc91724 100644 > --- a/libavformat/oggenc.c > +++ b/libavformat/oggenc.c > @@ -99,6 +99,35 @@ static const AVClass flavor ## _muxer_class = {\ > .version = LIBAVUTIL_VERSION_INT,\ > }; > > +static const struct ogg_pcm_codec { > + uint32_t codec_id; > + uint32_t format_id; > +} ogg_pcm_codecs[] = { > + { AV_CODEC_ID_PCM_S8, 0x00 }, > + { AV_CODEC_ID_PCM_U8, 0x01 }, > + { AV_CODEC_ID_PCM_S16LE, 0x02 }, > + { AV_CODEC_ID_PCM_S16BE, 0x03 }, > + { AV_CODEC_ID_PCM_S24LE, 0x04 }, > + { AV_CODEC_ID_PCM_S24BE, 0x05 }, > + { AV_CODEC_ID_PCM_S32LE, 0x06 }, > + { AV_CODEC_ID_PCM_S32BE, 0x07 }, > + { AV_CODEC_ID_PCM_F32LE, 0x20 }, > + { AV_CODEC_ID_PCM_F32BE, 0x21 }, > + { AV_CODEC_ID_PCM_F64LE, 0x22 }, > + { AV_CODEC_ID_PCM_F64BE, 0x23 }, > +}; > + > +static inline uint32_t ogg_get_pcm_format_id(uint32_t codec_id) > +{ > + int i; > + > + for (i = 0; i < FF_ARRAY_ELEMS(ogg_pcm_codecs); i++) > + if (ogg_pcm_codecs[i].codec_id == codec_id) > + return ogg_pcm_codecs[i].format_id; > + > + return 0; > +} > + > static void ogg_write_page(AVFormatContext *s, OGGPage *page, int > extra_flags) > { > OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data; > @@ -363,6 +392,39 @@ static int ogg_build_speex_headers(AVCodecParameters > *par, > return 0; > } > > +#define OGGPCM_HEADER_SIZE 224 > + > +static int ogg_build_pcm_headers(AVCodecParameters *par, > + OGGStreamContext *oggstream, int bitexact, > + AVDictionary **m) > +{ > + uint8_t *p; > + > + // first packet: OggPCM header > + p = av_mallocz(OGGPCM_HEADER_SIZE);
I didn't test it, but it seems to me that this leaks eventually. You need to modify ogg_free() to ensure that it doesn't. > + if (!p) > + return AVERROR(ENOMEM); > + oggstream->header[0] = p; > + oggstream->header_len[0] = OGGPCM_HEADER_SIZE; > + bytestream_put_buffer(&p, "PCM ", 8); // Identifier > + bytestream_put_be16(&p, 0x00); // VMAJ > + bytestream_put_be16(&p, 0x00); // VMIN > + bytestream_put_be32(&p, ogg_get_pcm_format_id(par->codec_id)); // PCM fmt > + bytestream_put_be32(&p, par->sample_rate); // Sample rate > + bytestream_put_byte(&p, 0); // Significant bits (0 == Same as fmt) > + bytestream_put_byte(&p, par->channels); // Channels > + bytestream_put_be16(&p, 0); // Max frames per packet TODO:// Actually > calculate max frames per packet > + bytestream_put_be32(&p, 0); // Number of extra headers > + > + // second packet: VorbisComment > + p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, > 0, NULL, 0); > + if (!p) > + return AVERROR(ENOMEM); > + oggstream->header[1] = p; > + > + return 0; > +} > + > #define OPUS_HEADER_SIZE 19 > > static int ogg_build_opus_headers(AVCodecParameters *par, > @@ -487,18 +549,20 @@ static int ogg_init(AVFormatContext *s) > avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); > } > > - if (st->codecpar->codec_id != AV_CODEC_ID_VORBIS && > - st->codecpar->codec_id != AV_CODEC_ID_THEORA && > - st->codecpar->codec_id != AV_CODEC_ID_SPEEX && > - st->codecpar->codec_id != AV_CODEC_ID_FLAC && > - st->codecpar->codec_id != AV_CODEC_ID_OPUS && > + if (st->codecpar->codec_id != AV_CODEC_ID_VORBIS && > + st->codecpar->codec_id != AV_CODEC_ID_THEORA && > + st->codecpar->codec_id != AV_CODEC_ID_SPEEX && > + st->codecpar->codec_id != AV_CODEC_ID_FLAC && > + st->codecpar->codec_id != AV_CODEC_ID_OPUS && > + !ogg_get_pcm_format_id(st->codecpar->codec_id) && > st->codecpar->codec_id != AV_CODEC_ID_VP8) { > av_log(s, AV_LOG_ERROR, "Unsupported codec id in stream %d\n", > i); > return AVERROR(EINVAL); > } > > if ((!st->codecpar->extradata || !st->codecpar->extradata_size) && > - st->codecpar->codec_id != AV_CODEC_ID_VP8) { > + st->codecpar->codec_id != AV_CODEC_ID_VP8 && > + !ogg_get_pcm_format_id(st->codecpar->codec_id)) { > av_log(s, AV_LOG_ERROR, "No extradata present\n"); > return AVERROR_INVALIDDATA; > } > @@ -553,6 +617,14 @@ static int ogg_init(AVFormatContext *s) > av_log(s, AV_LOG_ERROR, "Error writing VP8 headers\n"); > return err; > } > + } else if (ogg_get_pcm_format_id(st->codecpar->codec_id)) { > + int err = ogg_build_pcm_headers(st->codecpar, oggstream, > + s->flags & AVFMT_FLAG_BITEXACT, > + &st->metadata); > + if (err) { > + av_log(s, AV_LOG_ERROR, "Error writing OggPCM headers\n"); > + return err; > + } > } else { > uint8_t *p; > const char *cstr = st->codecpar->codec_id == AV_CODEC_ID_VORBIS > ? "vorbis" : "theora"; > diff --git a/libavformat/oggparsepcm.c b/libavformat/oggparsepcm.c > new file mode 100644 > index 0000000000..4bb777d8db > --- /dev/null > +++ b/libavformat/oggparsepcm.c > @@ -0,0 +1,123 @@ > +/* > + * PCM parser for Ogg > + * Copyright (c) 2013 James Almer > + * > + * 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 "libavutil/intreadwrite.h" > +#include "avformat.h" > +#include "internal.h" > +#include "oggdec.h" > + > +struct oggpcm_private { > + int vorbis_comment; > + uint32_t extra_headers; > +}; > + > +static const struct ogg_pcm_codec { > + uint32_t codec_id; > + uint32_t format_id; > +} ogg_pcm_codecs[] = { > + { AV_CODEC_ID_PCM_S8, 0x00 }, > + { AV_CODEC_ID_PCM_U8, 0x01 }, > + { AV_CODEC_ID_PCM_S16LE, 0x02 }, > + { AV_CODEC_ID_PCM_S16BE, 0x03 }, > + { AV_CODEC_ID_PCM_S24LE, 0x04 }, > + { AV_CODEC_ID_PCM_S24BE, 0x05 }, > + { AV_CODEC_ID_PCM_S32LE, 0x06 }, > + { AV_CODEC_ID_PCM_S32BE, 0x07 }, > + { AV_CODEC_ID_PCM_F32LE, 0x20 }, > + { AV_CODEC_ID_PCM_F32BE, 0x21 }, > + { AV_CODEC_ID_PCM_F64LE, 0x22 }, > + { AV_CODEC_ID_PCM_F64BE, 0x23 }, > +}; > + > +static const struct ogg_pcm_codec *ogg_get_pcm_codec_id(uint32_t format_id) > +{ > + int i; > + > + for (i = 0; i < FF_ARRAY_ELEMS(ogg_pcm_codecs); i++) > + if (ogg_pcm_codecs[i].format_id == format_id) > + return &ogg_pcm_codecs[i]; > + > + return NULL; > +} > + > +static int pcm_header(AVFormatContext *as, int idx) > +{ > + struct ogg *ogg = as->priv_data; > + struct ogg_stream *os = ogg->streams + idx; > + struct oggpcm_private *priv = os->private; > + const struct ogg_pcm_codec *pcm; > + AVStream *st = as->streams[idx]; > + uint8_t *p = os->buf + os->pstart; > + uint16_t major, minor; > + uint32_t format_id; > + > + if (!priv) { > + priv = os->private = av_mallocz(sizeof(*priv)); > + if (!priv) > + return AVERROR(ENOMEM); > + } > + > + if (os->flags & OGG_FLAG_BOS) { > + if (os->psize < 28) { > + av_log(as, AV_LOG_ERROR, "Invalid OggPCM header packet"); > + return -1; Don't return -1, return AVERROR_INVALIDDATA here. And also use proper error codes below. > + } > + > + major = AV_RB16(p + 8); > + minor = AV_RB16(p + 10); > + if (major) { > + av_log(as, AV_LOG_ERROR, "Unsupported OggPCM version %u.%u\n", > major, minor); > + return -1; > + } > + > + format_id = AV_RB32(p + 12); > + pcm = ogg_get_pcm_codec_id(format_id); > + if (!pcm) { > + av_log(as, AV_LOG_ERROR, "Unsupported PCM format ID 0x%X\n", > format_id); > + return -1; > + } > + > + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; > + st->codecpar->codec_id = pcm->codec_id; > + st->codecpar->sample_rate = AV_RB32(p + 16); > + st->codecpar->channels = AV_RB8 (p + 21); > + priv->extra_headers = AV_RB32(p + 24); > + priv->vorbis_comment = 1; > + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); > + } else if (priv->vorbis_comment) { > + ff_vorbis_stream_comment(as, st, p, os->psize); > + priv->vorbis_comment = 0; > + } else if (priv->extra_headers) { > + // TODO: Support for channel mapping and conversion headers. > + priv->extra_headers--; > + } else > + return 0; > + > + return 1; > +} > + > +const struct ogg_codec ff_pcm_codec = { > + .name = "OggPCM", > + .magic = "PCM ", > + .magicsize = 8, > + .header = pcm_header, > + .nb_header = 2, > +}; > -- _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".