diff mbox series

[FFmpeg-devel,v2] avformat/oggenc: Add partial support for OggPCM muxing

Message ID BYAPR15MB26474298A5117FF35E2F9E43F78E0@BYAPR15MB2647.namprd15.prod.outlook.com
State New
Headers show
Series [FFmpeg-devel,v2] avformat/oggenc: Add partial support for OggPCM muxing | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

A G May 28, 2020, 10:34 a.m. UTC
This adds partial support for OggPCM muxing.

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/oggenc.c | 86 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 80 insertions(+), 6 deletions(-)

--
2.26.2

Comments

A G May 28, 2020, 10:35 a.m. UTC | #1
Apologies for the previous patch (v1), the commit message was incorrect 
and I failed to add the requested change.
A G May 28, 2020, 10:52 a.m. UTC | #2
> +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;
> +}
> +

The ogg_get_pcm_format_id cannot return 0 for an unsupported format since that is already assigned to pcm_s8.
Lynne May 28, 2020, 10:55 a.m. UTC | #3
May 28, 2020, 11:34 by oreo639@outlook.com:

> This adds partial support for OggPCM muxing.
>
> +static inline uint32_t ogg_get_pcm_format_id(uint32_t codec_id)
>

use enum AVCodecID instead of uint32_t.
Also use that in ogg_pcm_codec for the codec IDs.



> +{
> +    int i;
> +
> +    for (i = 0; i < FF_ARRAY_ELEMS(ogg_pcm_codecs); i++)
>

Save 2 lines, use for (int i...



> +        if (ogg_pcm_codecs[i].codec_id == codec_id)
> +            return ogg_pcm_codecs[i].format_id;
> +
> +    return 0;
>

return AV_CODEC_ID_NONE;



> +}
> +
>  static void ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags)
>  {
>  OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data;
> @@ -363,6 +393,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)
>

Indentation seems wrong here.



> +{
> +    uint8_t *p;
> +
> +    // first packet: OggPCM header
> +    p = av_mallocz(OGGPCM_HEADER_SIZE);
> +    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)
>

That's not correct.
par->bits_per_raw_sample is what you want it set to.



> +    bytestream_put_byte(&p, par->channels); // Channels
> +    bytestream_put_be16(&p, 0); // Max frames per packet TODO:// Actually calculate max frames per packet
>

That's not really possible. PCM data packets can contain any number of samples.
Best you can do is place an upper bound for it, since an Ogg packet cannot be larger than
65xxx bytes.



> +    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 +550,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 +618,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);
>

Indent seems wrong here too.
diff mbox series

Patch

diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index f5032759a6..153f7f6760 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -99,6 +99,36 @@  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 +393,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);
+    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 +550,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 +618,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";
@@ -729,6 +802,7 @@  static void ogg_free(AVFormatContext *s)
         if (st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
             st->codecpar->codec_id == AV_CODEC_ID_SPEEX ||
             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_freep(&oggstream->header[0]);
         }