From patchwork Wed May 27 05:22:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: A G X-Patchwork-Id: 19892 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 29DD944A8FC for ; Wed, 27 May 2020 08:22:52 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EF0586880CC; Wed, 27 May 2020 08:22:51 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10olkn2049.outbound.protection.outlook.com [40.92.42.49]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7BFAF680072 for ; Wed, 27 May 2020 08:22:45 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=GsFRASitYQ9C7c20zW3sqZD8sB1CWxxNo9BQHqAebcpNoAQLFyRRhzIzcbmq/cFkK3HO/MAews98slq38ewp1wBX1z3ujMNRXs6YcXaOI9bO/fHmhzG8iEN7ixQfh/E/yA6wsoDUcYgUYCId0NsnpNwD6zqtRI2yZONqsSpGI14hSDEXxgbLZ4CQWqCljorQupnvY3T4XMy4M/aENZVWG3VPs4TyIWc/NetNjrSI0ZJJofIctK3DZa3o/ymEOJSudUuP2oESK/PCjThWYAWcszZHnSiXZBuXNUKFwQDWA6dCEG3y4n+BudSi52jGFbF/Q0z9qsA/e3JjKHcy4iZGXw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=AGfsaO4ytKjs5BO+UGhKBMDgferb1D2AA9XE2iE4xyI=; b=arXJmY5Vsd6yvYoRRHlMeDTMPXSBgZXGjiP88mLiyPwjohUxGcnY5cTbQejk/jJtNzIRdtx91m4Pikkm2+GisfxgYo0540wixShEMP9Mb/FVARpzRPHh0lkejL5NK+xUFOjyfiRLc1c/wE9pPltsoPwNOglDCB2jYO7eCxA8+2jMNABoJubJcpjfFpqrMxG/+TcfjqepDxLT14Rn8hrRY9bneWZAPtEH7QlJStz2il01m1k0qjQjs2ashSZ4Mgoh9/F+g/nx6wqC4vzW/UtLCmBXXW8jpT4oIPo8TS4EKdaXgXYhWUEaxfmcnJMs8e4L47zOoy6egttbM1r7MZDhqg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=AGfsaO4ytKjs5BO+UGhKBMDgferb1D2AA9XE2iE4xyI=; b=oELL8mPKEQNkXjlHUN3EcNDMF/jesIm0fKJiTxk761FO9OlXSO/JXzIC+q9d1ZSD/8s2yp4IWhIcPuJshcgkIwqrN6rZJOS8klMT3Jzi0+LtwvH1WH1Xo/Px/ShqfR37pPaD0Y9o/yCCwT+NsBPmEr4ZG/N76LsaoyfGYVcwO7REA88I7Hn8d9vgSNbe+dtLW1sq7+50iYrlpRU1b5tJVc+ysWUxF64nbYr0GwVgK6xBLHXD7TVyvX0wgNOra28Yfn1cR/umQ5sa8rmB9wxGRBFPQ8d6WCN+us4ZGLXbL6i0ec/xrq8N1X8BmUjesdTMI7FCO5PGljV2pepRJVTKdg== Received: from BN7NAM10FT057.eop-nam10.prod.protection.outlook.com (2a01:111:e400:7e8f::4f) by BN7NAM10HT075.eop-nam10.prod.protection.outlook.com (2a01:111:e400:7e8f::158) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3021.23; Wed, 27 May 2020 05:22:42 +0000 Received: from BYAPR15MB2647.namprd15.prod.outlook.com (2a01:111:e400:7e8f::41) by BN7NAM10FT057.mail.protection.outlook.com (2a01:111:e400:7e8f::403) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3021.23 via Frontend Transport; Wed, 27 May 2020 05:22:42 +0000 Received: from BYAPR15MB2647.namprd15.prod.outlook.com ([fe80::f083:ed57:da58:e2a1]) by BYAPR15MB2647.namprd15.prod.outlook.com ([fe80::f083:ed57:da58:e2a1%6]) with mapi id 15.20.3021.029; Wed, 27 May 2020 05:22:42 +0000 From: A G To: FFmpeg development discussions and patches Thread-Topic: [PATCH] avformat/OggPCM: Add partial support for muxing and playing Thread-Index: AQHWM8cts/rkufqGxEOD1k4BDatcGQ== Date: Wed, 27 May 2020 05:22:42 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-incomingtopheadermarker: OriginalChecksum:93206A90FAD81AB29DCFCB8F0A403B32874FE077F4F1CB4BF2C4611A2F24524E; UpperCasedChecksum:90B5470D657E558B05D5649F0E68523C8FF0426633A00086DA8BA74464CD5929; SizeAsReceived:6821; Count:42 x-tmn: [m74+eUlSUnKRYr8hWIFKdUDvsIptk8w3wybpdp7Sf80=] x-ms-publictraffictype: Email x-incomingheadercount: 42 x-eopattributedmessage: 0 x-ms-office365-filtering-correlation-id: 2305e1b3-6f59-4647-c809-08d801fdfaf8 x-ms-traffictypediagnostic: BN7NAM10HT075: x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: ww+zd/5wvRBfhiMVzC4j0vxaAXxWk8++n7Bq5rSWHWbdbHAfgwXVB9qVpOx9IdD5KZCAeskaMyjJ3QGPrt5NxE/u0NehUvJqpzmqGaEGAVAijWCvQp2OM1JpkdYfuNVZx919OhLGUijT4r2RhkIppBAVky038Ze1hHV77OJ7VmJaJOH5ZOFs75uCulYb9pDLmPkEwWFxF9Ea2QmxukP7gTok0HsyDYONtZGcmgorVco04lzCsKuWmVR4ZaHVcaDT x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:0; SRV:; IPV:NLI; SFV:NSPM; H:BYAPR15MB2647.namprd15.prod.outlook.com; PTR:; CAT:NONE; SFTY:; SFS:; DIR:OUT; SFP:1901; x-ms-exchange-antispam-messagedata: G1/091FD5A4M4pgfYiA5lCfQHA7G7VlqPdbzh30wCRCaI9aBQ1a5o2u6QW6qMXWeFfHxLwm49v4ScyiALnMz0ErnKqId70bJu8oKklyOknQGzvU2oLbf3lvLuZQ7BlkcK/VzO0lEQbCjpILBBBDAEaunfEqSrKGXgrySOENT/2A= x-ms-exchange-transport-forked: True MIME-Version: 1.0 X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-Network-Message-Id: 2305e1b3-6f59-4647-c809-08d801fdfaf8 X-MS-Exchange-CrossTenant-rms-persistedconsumerorg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-CrossTenant-originalarrivaltime: 27 May 2020 05:22:42.1087 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Internet X-MS-Exchange-CrossTenant-id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN7NAM10HT075 X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] [PATCH] avformat/OggPCM: Add partial support for muxing and playing X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From 16e0b79bcdc029b7ab34dfbf80b50868743a8e5e Mon Sep 17 00:00:00 2001 From: oreo639 <31916379+Oreo639@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 -- 2.26.2 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); + 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; + } + + 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, +};