From patchwork Sun Jul 31 16:03:45 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Almer X-Patchwork-Id: 40 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.67 with SMTP id o64csp2309478vsd; Sun, 31 Jul 2016 09:17:45 -0700 (PDT) X-Received: by 10.28.55.21 with SMTP id e21mr53841620wma.80.1469981865552; Sun, 31 Jul 2016 09:17:45 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d191si12242246wme.111.2016.07.31.09.17.45; Sun, 31 Jul 2016 09:17:45 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D4E2E689DF1; Sun, 31 Jul 2016 19:17:34 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-oi0-f68.google.com (mail-oi0-f68.google.com [209.85.218.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B3A95689DF1 for ; Sun, 31 Jul 2016 19:17:24 +0300 (EEST) Received: by mail-oi0-f68.google.com with SMTP id l9so11968117oih.0 for ; Sun, 31 Jul 2016 09:17:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id; bh=aMrbOaCcwN+re5KJO/3BMcMHfDlV7o8YJNSHcTbFJao=; b=ASbb45RcgB/4YnfR03ql9tQrgNbaSu0bivvZMoMu0BZf7ZH52vPg+gKqVTSodH2hzY MkkLT6ogXPIdIxpYt3JcU/HdpbvYdjJbRVSmOFhKU3etX14yvlnij9MEjeE8Aid8PHpI aPcNbmazIzR80atMx2QxBq/KmihiflZjVZrD/U8STkmclcPKM2/e/XpkePyk862g4ENT p2QEjg4vsDTUtAtbiBlQxNHTjCVRDSo4V34e3QlxPFMDs3HmX1OE/7qYyrT0UFd3ALqS vIkjDZ1fAXuZxbqfnrAfdq08O1Jdgq6rNVdbflZ6kbrDgxGz2M6us53JuysYiFv0PkOY jICw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id; bh=aMrbOaCcwN+re5KJO/3BMcMHfDlV7o8YJNSHcTbFJao=; b=lesIEylQxblttnoJ2qClULxKmFkl9GLIuS5h2kjUzHXK6sBwfw6qZLX7FAYG4+YzVl 3uOYBvZFA5HaaNVl4O3ALY8AiDvTXzT4zgyWVN27MmOuZnoYMJECgG6+Qtfz+gQlXr0J Oa3ORqoe6UZ1QcGXDCreP5FUPAPUwKmo/37QLf3rZVLvm3MkF5E07vH9vzIV+B5Plu+P fovsZmCI3ebEinvYh4U1RkVpgYwsKC0Acqw22GvYM5fExdHb2ZuhJagO5ircJ06MnXlS iQ5sSyFB5hDUxSM9mW8UdWjcGrRQKW53QstBfBcNOALO/csqeU0WrKD85M+RzB0rs/TI 39iw== X-Gm-Message-State: AEkoouvHwEpyqBgztydht9xSw4ZnHcMjyB4142vxCAArOZH+RBADUv7RbF5b3UDoF92fKA== X-Received: by 10.157.43.6 with SMTP id o6mr32881467otb.41.1469981131872; Sun, 31 Jul 2016 09:05:31 -0700 (PDT) Received: from localhost.localdomain ([181.22.17.94]) by smtp.gmail.com with ESMTPSA id s20sm11150645ota.25.2016.07.31.09.05.29 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 31 Jul 2016 09:05:31 -0700 (PDT) From: James Almer To: ffmpeg-devel@ffmpeg.org Date: Sun, 31 Jul 2016 13:03:45 -0300 Message-Id: <20160731160345.3696-1-jamrial@gmail.com> X-Mailer: git-send-email 2.9.1 Subject: [FFmpeg-devel] [PATCH] avformat: add a TTA Muxer 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: James Almer Tested-by: Michael on x86 32 / 64, mingw 32/64, mips , arm Reviewed-by: Michael --- Changelog | 1 + doc/general.texi | 2 +- libavformat/Makefile | 1 + libavformat/allformats.c | 2 +- libavformat/avio_internal.h | 2 + libavformat/aviobuf.c | 6 ++ libavformat/tta.c | 10 +-- libavformat/ttaenc.c | 147 ++++++++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 2 +- tests/fate/acodec.mak | 4 +- tests/fate/avformat.mak | 2 + tests/lavf-regression.sh | 8 +++ tests/ref/acodec/tta | 4 +- tests/ref/lavf/mka | 3 + tests/ref/lavf/tta | 3 + 15 files changed, 182 insertions(+), 15 deletions(-) create mode 100644 libavformat/ttaenc.c create mode 100644 tests/ref/lavf/mka create mode 100644 tests/ref/lavf/tta diff --git a/Changelog b/Changelog index 0f9b4cf..a0bf4d6 100644 --- a/Changelog +++ b/Changelog @@ -12,6 +12,7 @@ version : - 16-bit support in selectivecolor filter - OpenH264 decoder wrapper - MediaCodec hwaccel +- True Audio (TTA) muxer version 3.1: diff --git a/doc/general.texi b/doc/general.texi index 6b5975c..74fc539 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -509,7 +509,7 @@ library: @tab Used on the Nintendo GameCube. @item Tiertex Limited SEQ @tab @tab X @tab Tiertex .seq files used in the DOS CD-ROM version of the game Flashback. -@item True Audio @tab @tab X +@item True Audio @tab X @tab X @item VAG @tab @tab X @tab Audio format used in many Sony PS2 games. @item VC-1 test bitstream @tab X @tab X diff --git a/libavformat/Makefile b/libavformat/Makefile index c3f38b4..d6291ca 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -471,6 +471,7 @@ OBJS-$(CONFIG_TMV_DEMUXER) += tmv.o OBJS-$(CONFIG_TRUEHD_DEMUXER) += rawdec.o mlpdec.o OBJS-$(CONFIG_TRUEHD_MUXER) += rawenc.o OBJS-$(CONFIG_TTA_DEMUXER) += tta.o apetag.o img2.o +OBJS-$(CONFIG_TTA_MUXER) += ttaenc.o apetag.o img2.o OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o OBJS-$(CONFIG_TXD_DEMUXER) += txd.o OBJS-$(CONFIG_UNCODEDFRAMECRC_MUXER) += uncodedframecrcenc.o framehash.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 10c9bcc..a69195e 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -311,7 +311,7 @@ void av_register_all(void) REGISTER_MUXER (MKVTIMESTAMP_V2, mkvtimestamp_v2); REGISTER_DEMUXER (TMV, tmv); REGISTER_MUXDEMUX(TRUEHD, truehd); - REGISTER_DEMUXER (TTA, tta); + REGISTER_MUXDEMUX(TTA, tta); REGISTER_DEMUXER (TXD, txd); REGISTER_DEMUXER (TTY, tty); REGISTER_MUXER (UNCODEDFRAMECRC, uncodedframecrc); diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index 3867be6..fdb323c 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -111,6 +111,8 @@ void ffio_init_checksum(AVIOContext *s, unsigned long ffio_get_checksum(AVIOContext *s); unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf, unsigned int len); +unsigned long ff_crcEDB88320_update(unsigned long checksum, const uint8_t *buf, + unsigned int len); unsigned long ff_crcA001_update(unsigned long checksum, const uint8_t *buf, unsigned int len); diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 31e7202..f3acb32 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -560,6 +560,12 @@ unsigned long ff_crc04C11DB7_update(unsigned long checksum, const uint8_t *buf, return av_crc(av_crc_get_table(AV_CRC_32_IEEE), checksum, buf, len); } +unsigned long ff_crcEDB88320_update(unsigned long checksum, const uint8_t *buf, + unsigned int len) +{ + return av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), checksum, buf, len); +} + unsigned long ff_crcA001_update(unsigned long checksum, const uint8_t *buf, unsigned int len) { diff --git a/libavformat/tta.c b/libavformat/tta.c index 02656ac..1447eff 100644 --- a/libavformat/tta.c +++ b/libavformat/tta.c @@ -35,12 +35,6 @@ typedef struct TTAContext { int last_frame_size; } TTAContext; -static unsigned long tta_check_crc(unsigned long checksum, const uint8_t *buf, - unsigned int len) -{ - return av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), checksum, buf, len); -} - static int tta_probe(AVProbeData *p) { if (AV_RL32(&p->buf[0]) == MKTAG('T', 'T', 'A', '1') && @@ -65,7 +59,7 @@ static int tta_read_header(AVFormatContext *s) start_offset = avio_tell(s->pb); if (start_offset < 0) return start_offset; - ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX); + ffio_init_checksum(s->pb, ff_crcEDB88320_update, UINT32_MAX); if (avio_rl32(s->pb) != AV_RL32("TTA1")) return AVERROR_INVALIDDATA; @@ -121,7 +115,7 @@ static int tta_read_header(AVFormatContext *s) avio_seek(s->pb, start_offset, SEEK_SET); avio_read(s->pb, st->codecpar->extradata, st->codecpar->extradata_size); - ffio_init_checksum(s->pb, tta_check_crc, UINT32_MAX); + ffio_init_checksum(s->pb, ff_crcEDB88320_update, UINT32_MAX); for (i = 0; i < c->totalframes; i++) { uint32_t size = avio_rl32(s->pb); int r; diff --git a/libavformat/ttaenc.c b/libavformat/ttaenc.c new file mode 100644 index 0000000..9ad301a --- /dev/null +++ b/libavformat/ttaenc.c @@ -0,0 +1,147 @@ +/* + * True Audio (TTA) muxer + * Copyright (c) 2016 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/crc.h" +#include "libavutil/intreadwrite.h" + +#include "apetag.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" + +typedef struct TTAMuxContext { + AVIOContext *seek_table; + AVIOContext *data; + uint32_t nb_samples; + int frame_size; + int last_frame; +} TTAMuxContext; + +static int tta_write_header(AVFormatContext *s) +{ + TTAMuxContext *tta = s->priv_data; + AVCodecParameters *par = s->streams[0]->codecpar; + int ret; + + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "Only one stream is supported\n"); + return AVERROR(EINVAL); + } + if (par->codec_id != AV_CODEC_ID_TTA) { + av_log(s, AV_LOG_ERROR, "Unsupported codec\n"); + return AVERROR(EINVAL); + } + if (par->extradata && par->extradata_size < 22) { + av_log(s, AV_LOG_ERROR, "Invalid TTA extradata\n"); + return AVERROR_INVALIDDATA; + } + + if ((ret = avio_open_dyn_buf(&tta->seek_table)) < 0) + return ret; + if ((ret = avio_open_dyn_buf(&tta->data)) < 0) { + ffio_free_dyn_buf(&tta->seek_table); + return ret; + } + + /* Ignore most extradata information if present. It can be innacurate + if for example remuxing from Matroska */ + ffio_init_checksum(s->pb, ff_crcEDB88320_update, UINT32_MAX); + ffio_init_checksum(tta->seek_table, ff_crcEDB88320_update, UINT32_MAX); + avio_write(s->pb, "TTA1", 4); + avio_wl16(s->pb, par->extradata ? AV_RL16(par->extradata + 4) : 1); + avio_wl16(s->pb, par->channels); + avio_wl16(s->pb, par->bits_per_raw_sample); + avio_wl32(s->pb, par->sample_rate); + tta->frame_size = par->sample_rate * 256 / 245; + avpriv_set_pts_info(s->streams[0], 64, 1, par->sample_rate); + + return 0; +} + +static int tta_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + TTAMuxContext *tta = s->priv_data; + + avio_write(tta->data, pkt->data, pkt->size); + avio_wl32(tta->seek_table, pkt->size); + tta->nb_samples += pkt->duration; + + if (tta->frame_size != pkt->duration) { + if (tta->last_frame) { + /* Two frames with a different duration than the default frame + size means the TTA stream comes from a faulty container, and + there's no way the last frame duration will be correct. */ + av_log(s, AV_LOG_ERROR, "Invalid frame durations\n"); + + return AVERROR_INVALIDDATA; + } + /* First frame with a different duration than the default frame size. + Assume it's the last frame in the stream and continue. */ + tta->last_frame++; + } + + return 0; +} + +static int tta_write_trailer(AVFormatContext *s) +{ + TTAMuxContext *tta = s->priv_data; + uint8_t *ptr; + unsigned int crc; + int size; + + avio_wl32(s->pb, tta->nb_samples); + crc = ffio_get_checksum(s->pb) ^ UINT32_MAX; + avio_wl32(s->pb, crc); + + /* Write Seek table */ + crc = ffio_get_checksum(tta->seek_table) ^ UINT32_MAX; + avio_wl32(tta->seek_table, crc); + + avio_flush(tta->seek_table); + size = avio_close_dyn_buf(tta->seek_table, &ptr); + avio_write(s->pb, ptr, size); + av_free(ptr); + + /* Write audio data */ + avio_flush(tta->data); + size = avio_close_dyn_buf(tta->data, &ptr); + avio_write(s->pb, ptr, size); + avio_flush(s->pb); + av_free(ptr); + + ff_ape_write_tag(s); + + return 0; +} + +AVOutputFormat ff_tta_muxer = { + .name = "tta", + .long_name = NULL_IF_CONFIG_SMALL("TTA (True Audio)"), + .mime_type = "audio/x-tta", + .extensions = "tta", + .priv_data_size = sizeof(TTAMuxContext), + .audio_codec = AV_CODEC_ID_TTA, + .video_codec = AV_CODEC_ID_NONE, + .write_header = tta_write_header, + .write_packet = tta_write_packet, + .write_trailer = tta_write_trailer, +}; diff --git a/libavformat/version.h b/libavformat/version.h index 5630808..8899bfd 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you belive might be affected here #define LIBAVFORMAT_VERSION_MAJOR 57 -#define LIBAVFORMAT_VERSION_MINOR 44 +#define LIBAVFORMAT_VERSION_MINOR 45 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ diff --git a/tests/fate/acodec.mak b/tests/fate/acodec.mak index e0f2320..c7d4d26 100644 --- a/tests/fate/acodec.mak +++ b/tests/fate/acodec.mak @@ -154,8 +154,8 @@ FATE_ACODEC-$(call ENCDEC, WAVPACK, WV) += fate-acodec-wavpack fate-acodec-wavpack: FMT = wv fate-acodec-wavpack: CODEC = wavpack -compression_level 1 -FATE_ACODEC-$(call ENCDEC, TTA, MATROSKA) += fate-acodec-tta -fate-acodec-tta: FMT = matroska +FATE_ACODEC-$(call ENCDEC, TTA, TTA) += fate-acodec-tta +fate-acodec-tta: FMT = tta FATE_ACODEC += $(FATE_ACODEC-yes) diff --git a/tests/fate/avformat.mak b/tests/fate/avformat.mak index c7f3124..3760e41 100644 --- a/tests/fate/avformat.mak +++ b/tests/fate/avformat.mak @@ -15,6 +15,7 @@ FATE_LAVF-$(call ENCDEC, GIF, IMAGE2) += gif FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, GXF) += gxf FATE_LAVF-$(call ENCDEC, PCM_S16LE, IRCAM) += ircam FATE_LAVF-$(call ENCDEC, MJPEG, IMAGE2) += jpg +FATE_LAVF-$(call ENCMUX, TTA, MATROSKA_AUDIO) += mka FATE_LAVF-$(call ENCDEC2, MPEG4, MP2, MATROSKA) += mkv FATE_LAVF-$(call ENCDEC, ADPCM_YAMAHA, MMF) += mmf FATE_LAVF-$(call ENCDEC2, MPEG4, PCM_ALAW, MOV) += mov ismv @@ -44,6 +45,7 @@ FATE_LAVF-$(call ENCDEC, FLV, SWF) += swf FATE_LAVF-$(call ENCDEC, TARGA, IMAGE2) += tga FATE_LAVF-$(call ENCDEC, TIFF, IMAGE2) += tiff FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, MP2, MPEGTS) += ts +FATE_LAVF-$(call ENCDEC, TTA, TTA) += tta FATE_LAVF-$(call ENCDEC, PCM_U8, VOC) += voc FATE_LAVF-$(call ENCDEC, PCM_S16LE, VOC) += voc_s16 FATE_LAVF-$(call ENCDEC, PCM_S16LE, WAV) += wav diff --git a/tests/lavf-regression.sh b/tests/lavf-regression.sh index 0e20513..8d96178 100755 --- a/tests/lavf-regression.sh +++ b/tests/lavf-regression.sh @@ -145,6 +145,10 @@ if [ -n "$do_nut" ] ; then do_lavf nut "" "-acodec mp2 -ab 64k -ar 44100 -threads 1" fi +if [ -n "$do_mka" ] ; then +do_audio_only mka "" "-c:a tta" +fi + if [ -n "$do_mkv" ] ; then do_lavf mkv "" "-acodec mp2 -ab 64k -vcodec mpeg4 \ -attach ${raw_src%/*}/00.pgm -metadata:s:t mimetype=image/x-portable-greymap -threads 1" @@ -350,6 +354,10 @@ if [ -n "$do_sox" ] ; then do_audio_only sox fi +if [ -n "$do_tta" ] ; then +do_audio_only tta +fi + if [ -n "$do_caf" ] ; then do_audio_only caf fi diff --git a/tests/ref/acodec/tta b/tests/ref/acodec/tta index 0f60345..8e183f9 100644 --- a/tests/ref/acodec/tta +++ b/tests/ref/acodec/tta @@ -1,4 +1,4 @@ -6c260836d7a32e4bd714453a3546c0d5 *tests/data/fate/acodec-tta.matroska -331148 tests/data/fate/acodec-tta.matroska +847d065f082ac94825728b5f1af853eb *tests/data/fate/acodec-tta.tta +330583 tests/data/fate/acodec-tta.tta 95e54b261530a1bcf6de6fe3b21dc5f6 *tests/data/fate/acodec-tta.out.wav stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 1058400/ 1058400 diff --git a/tests/ref/lavf/mka b/tests/ref/lavf/mka new file mode 100644 index 0000000..962b254 --- /dev/null +++ b/tests/ref/lavf/mka @@ -0,0 +1,3 @@ +b2e3746787b885d0191a1a26f3faa58f *./tests/data/lavf/lavf.mka +43654 ./tests/data/lavf/lavf.mka +./tests/data/lavf/lavf.mka CRC=0x3a1da17e diff --git a/tests/ref/lavf/tta b/tests/ref/lavf/tta new file mode 100644 index 0000000..745e8d2 --- /dev/null +++ b/tests/ref/lavf/tta @@ -0,0 +1,3 @@ +f2721d06704ac43d89fdd25835b43598 *./tests/data/lavf/lavf.tta +43200 ./tests/data/lavf/lavf.tta +./tests/data/lavf/lavf.tta CRC=0x3a1da17e