From patchwork Mon Sep 19 13:25:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: erkki.seppala.ext@nokia.com X-Patchwork-Id: 624 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp999992vsd; Mon, 19 Sep 2016 06:26:19 -0700 (PDT) X-Received: by 10.28.47.87 with SMTP id v84mr1054477wmv.50.1474291579065; Mon, 19 Sep 2016 06:26:19 -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 u64si19435601wmg.130.2016.09.19.06.26.18; Mon, 19 Sep 2016 06:26:19 -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=@nokia.onmicrosoft.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=nokia.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 67679689DEF; Mon, 19 Sep 2016 16:25:53 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from EUR02-AM5-obe.outbound.protection.outlook.com (mail-eopbgr00127.outbound.protection.outlook.com [40.107.0.127]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E89AD689D9A for ; Mon, 19 Sep 2016 16:25:45 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nokia.onmicrosoft.com; s=selector1-nokia-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=ux+53APseAzNnjms8MCmPRpakwTkosG0g/mzNlXNwLQ=; b=Qw93XUztW/k2CdBtqjxa555VRJYkCpN3Lo9GaPVFyTfV/juytxDOnaYcvdTF9dxPptHuyVqnmyweC43lt8KiKGHhrT7SQeY/pmYTiMfkmeK6EApL0fTYgNLM+8/zY/OqrC457L/BcYGqWcaMicwX4bGjpmhGKUqK3VPY+X5yo2Y= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=erkki.seppala.ext@nokia.com; Received: from erkkise-laptop.vincit.intranet (131.228.2.24) by AM5PR0701MB2531.eurprd07.prod.outlook.com (10.169.153.151) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384) id 15.1.629.8; Mon, 19 Sep 2016 13:25:58 +0000 Received: by erkkise-laptop.vincit.intranet (Postfix, from userid 1000) id 9E0EB4F0F41; Mon, 19 Sep 2016 16:25:48 +0300 (EEST) From: =?UTF-8?q?Erkki=20Sepp=C3=A4l=C3=A4?= To: Date: Mon, 19 Sep 2016 16:25:26 +0300 Message-ID: <1474291548-17810-4-git-send-email-erkki.seppala.ext@nokia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1474291548-17810-1-git-send-email-erkki.seppala.ext@nokia.com> References: <1471943019-14136-1-git-send-email-erkki.seppala.ext@nokia.com> <1474291548-17810-1-git-send-email-erkki.seppala.ext@nokia.com> MIME-Version: 1.0 X-Originating-IP: [131.228.2.24] X-ClientProxiedBy: DM5PR18CA0041.namprd18.prod.outlook.com (10.173.208.27) To AM5PR0701MB2531.eurprd07.prod.outlook.com (10.169.153.151) X-MS-Office365-Filtering-Correlation-Id: 2d981119-9ca2-44ce-1b56-08d3e0907e7c X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2531; 2:/sVf3aR6JsU13TDzJG9clr4M74qCSCIqytlXs9yKTbl3wSqFCxxpPN9lbIDP74JxmizU+00CIhaNKaUJ6EV2HwFk4XuHDfT0sJWx/uE4+CDWuNeudzRDE53nvoV0jiUwmPY+MhAM9/Hb9+svxhoe2kdwz325FYwN3kzUtsrDpETF3GrFDtH7X2eubvO7Xmdq; 3:i+b0wy9PDHbHVvkLvYroRMwJAc8phMMXxCrBcshOkwvK+u4yfUxlkQp6HVsrOQuV3E6ZATLodmHH6A47EN5U3g72hpPPKa8jsYACx6+SL6MqsJilgd+/qngVA6AgSkk0 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:AM5PR0701MB2531; X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2531; 25:NBqqnfDUJsG0/SA3krd+tk2SzIiGaRPj4jQcIZLq8JhCvO5rHpmzhZzix1IVl4RI5tFfKCRphsgpzj00nUDqJLwPq2teu6VF5ybuHBA+2pqOl3bqc+45c8a5ZVc8x+ErA6L9CF4niOr7g/t61jylfL7t8GqNMZ8GO/WBxtC7+8Gm7j2Dn8M4q92mIuLjRfQqfdvFYKF61fz8pU3RhciFrVBv6Ff0SlulKcXcr8IAFhSviRXlMMTSl2r87UyFzichD5f5SJh5H4oQPs/F3x8MES9WM5QXCP+Y3U8mzJnXyitJoG5359cldCQYIMeFjA+alCvXF9rrCfQOgqjci8ggN2TAr5Kq5sn0J2Lda9CNwSwrHdNysReusBWTFnIamefWUg8/JX/6IW9gvMPagPr7I3Lnh+Mwvh4ZjR9XgOwwgBfdqF3UlL42k5Vp895mJ4wIzsQUR80ByIi+jurEe6kLzaPS5H6zSgUM/F3/yg126tNj9mmaTZGwuj2f6EUAgU+dhqAlyCCq7xL1+Qjg8fCBwdCfjKsgXTCXqQykq3CccXaEh4ZFkbqJs0Mp73C3C/BME+FqzR9yi5MU0ZXHHesBbE2JeN0N04zeczD7jSkpRJ7b+kvAUbdU507vEsEF0Ka9kNqShlkzmYPdzlCV3jgsy7/7mit1jRZ/EmseLGkW46G4u9ItLhP8JHhiUCixpNA2; 31:SlaOfVICeEkbRj1pVU6t907yLITWt/wZ4budZ+UX7uxaTIsQ99XqeMIjPcGP776btZp/E4wMJqJKxaVBGXV+8LhczfSvg9+XwaLVxqhUO7ztk8xxAPKFVIHqILu7ZdiE242QO7afkmFpgIIJ7gWsl5hxPU1PsiHsvvQCVLZkn7J2gy0hQeeI6Cx0BS/9b8VbEB/MiOZLQnYFywArxgKjiWhNRy2OdsOcAJo+k5ozZZ8= X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2531; 20:anbngC+PNNICkVmWP73xd3QkaG57X7FY0RUiG1MqP0f/bNUL0Lp8tDTYvyLYLlLfZG9Yvhh0Jno77PDniq53i6H4YDDcX832LPTRxsiu7Ub/ddK/HMWdfYHy3V6PaXik5J3JlO+XeGwDmi/XUJ+/vRXt3wJAJo5vVzSPzow0pBRJE7q7HUZbMo+ol8qoyG54r7duaClx0bdphpaw15T1Y9/E3EZnIveW3gnsAWG88MRlP/Ov+oBErGo0F2Gr4nSrulYs4cCVaREwLWcECz/bAJEp66ADkbClldYXPEiUALYMPqy/HoGm60x0z1VFFRvLFlPdo7PCXsfHQR42PFqASUIFojstddQ/oqbKz5+U4EMKgZpaOB2tlEdIFvm8h/6oXcF3pCYD00f3OaDvsxFk+umzFUhjjJa5s8mQQns0563hJWhw7b1RJK+zUqeXsQYxub7QE69ImsIXmLYAhWACBe0VitVMOXe6XHKjynBhNqrx+CZAn4jXYpl6QyaApSrQ; 4:xnQZQaU/P1nMM120A2LHK6HOTWfAfK1kW/kQEJoxwxxcN/oo8qWIGXFMXnIesGZcFDJTCh6W841f7j1Ph6Z/ZSO44DEXVMmRvc0WSMII8kTP8hegNErJH8xB0Ooe5dO8YDYtIdt/hlHiCGppo7K8SZlAVAZYgUO+NVucmp5VUUxsR9g92D0B98Fs4LN7SOjX/qaTvMQBeTcUUMqvRU6h0nmLuGKKj74QkkWYHp9bpo6Zy/Dm+vsvU9zhUKOtugPRw9m2xcMC5uz1fiCo675GgFDor+EvKOLo4IwPxkKHOTOIIfqkC6YiUuCTjD8QpiAMLw3zU4qncD7MTjS+aT/9EnJYg9Y22WKQyTALjmFbUbsbQXDw31MBYFY7LsuLeLLD6IdMl/H3qucjLJ+OVHJIA6rKvdOEvBmwwOWrq3TnQj16YIQ5TLOcbPzv7o+LtZJX X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(82608151540597); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040176)(601004)(2401047)(8121501046)(5005006)(3002001)(10201501046)(6055026); SRVR:AM5PR0701MB2531; BCL:0; PCL:0; RULEID:; SRVR:AM5PR0701MB2531; X-Forefront-PRVS: 0070A8666B X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(7916002)(189002)(199003)(50466002)(45336002)(4001430100002)(90966002)(36756003)(47776003)(92566002)(229853001)(2351001)(66066001)(52956003)(189998001)(107886002)(122856001)(68736007)(5820100001)(50226002)(2870700001)(106356001)(50986999)(6116002)(5660300001)(3846002)(4326007)(2906002)(586003)(76176999)(105586002)(33646002)(7736002)(110136003)(19580395003)(305945005)(450100001)(81156014)(101416001)(7846002)(81166006)(19580405001)(42186005)(46386002)(97736004)(23676002)(8676002)(86362001)(2950100001)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:AM5PR0701MB2531; H:erkkise-laptop.vincit.intranet; FPR:; SPF:None; PTR:InfoNoRecords; MX:1; A:1; LANG:en; Received-SPF: None (protection.outlook.com: nokia.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtBTTVQUjA3MDFNQjI1MzE7MjM6MjZEWkRYT3BrYlJHYXBxS2I4ZjI1dzF1?= =?utf-8?B?eUNGYTNVWFhDcHpVdTRFV295ZHRFZEVOakFkbzhCN2ZPL3BWUEl5MENQN0lt?= =?utf-8?B?eW9mWFM0QTdUOG0vdEh2eUhmQ045NWUzYnN3UFRyUFk5TXZMZ0toVGR5S05M?= =?utf-8?B?OG5KZUJCdFBPQmNSMFlGMUNiZ1F4MU5ySXdndFBMQ0xOQktCWmxRL1RTMHVD?= =?utf-8?B?cmsxL3VUM3h4YUxDQjBqMUl6SFYwVkJ0SWJEeVMwUlhNb0p5RXRzd0hGRXhp?= =?utf-8?B?cjdlUGRjMWtUVnMwL1dBK2EvZ0pvb08wNSt4TWJTQW9nWGRNWU9xc3V3NUs3?= =?utf-8?B?ZUpXM05DUEIzY1lqWjFSR2k4NHBjRTBuNmNJemQvZzIyVGxlSk9LbG8zTXZG?= =?utf-8?B?ck42cmx3UXVFb2NyQzZoMHptU2hwNzdlUFU3NEEra2RzczBJMHd4SExpVzY3?= =?utf-8?B?byt4YjBZSCsxT2VaRm9JOXFzMUluUjczZjVzaEtoK0Q4VkJpYlpBL3pyRmEw?= =?utf-8?B?eU11ZVBnQ2RFTWtUVWswb3lXQ0hiSk1haHB6VG5VY2FTeFNXYmJYa2pxNGFt?= =?utf-8?B?cmVPRUp2OUdWQnMxSHJERjZ1OWhJK1lwZzNIMk5CbmNPNTE5ZmkzRnI1Z01p?= =?utf-8?B?L1ZEcTBOVHc3bTg2dFZrd2lpeEZiR2gvdnpveTV6bU1OeERscCtweWZLaVYr?= =?utf-8?B?RTZGUDg0ZTkvVHlPZElOZjBuZzRIem1kUjNWcFVWM0hqemJUR2prb0I2aXA1?= =?utf-8?B?VEdVUVFjalFhdDdxNloyU04vYmExMG5BVmR1OTRGZDRyN2xlbVZ3b2xpZlhq?= =?utf-8?B?S3hrUjE5OVdVWjRXcVhPenNWNVRkNUJsNVZNYzN1dThEMWpIV1AwcG5ZNFlZ?= =?utf-8?B?dHJydHprVmF1N0F0aTJRU0tQMVExY1oyN1hQK3FqdFU3Q0hXdWw4TDBvdkVF?= =?utf-8?B?QzM4VDNpVE9kekpmVXIvdHFLcUp4VTJ5RmozTWZoME1DZDR5VjlRbUhPWE5X?= =?utf-8?B?YmFTaXR0RSt3aVovRzkvbWo3ZzRrUVNFVXVPejZ6QnJmclE2bHZ3VTdPTWpV?= =?utf-8?B?N21ER3N0SGtNL3p2TWdhNms0UHVCOW0vOTF3akgyWXcxdWs2NHA0OWpMaDkr?= =?utf-8?B?QWtHMUgrQUxrc21nQ2ZMQmZuMFA3azVCZHA0RWoyd2R0dnM5NEFBZDcyeTEr?= =?utf-8?B?MFQ5aWpvUTIwd2hRMkR3eVFZek4xbXh3c3grVzAyWGpOZEIwVCtheWpIdVU4?= =?utf-8?B?T2lucGVEY1FjWDZQUzNwRlQyRStReHZzc3c1TzZUK21hbXlKRmZZMUNuRjJM?= =?utf-8?B?RS9acTEvTmxvQ2RNQmN5VkwxNG4rNExuaWhBMnpPWEJ0Y211UUw4MzZXeGYv?= =?utf-8?B?S0FkdVVCdzR0WkhZanZRSVY1a2o3bWQ0ZkdySHoyczRJZEtBWUhRcHZEUENy?= =?utf-8?B?djh1STAxZjZTV29iRy94K1o1Sm55cEk0SEhaeERBV2VJYWJaTFFPTm1ZR04z?= =?utf-8?B?SXBkMGZhN3ptQjEvTzJUMStDZjBEdDl0NHgwU1YyMHUwcmpob21PYmluc25I?= =?utf-8?B?bjhlU1g0NUdsTHc5cU03NCtibmNjUXdsc3BGNUtxZ3dRa2E2NFN6OFlNWk9L?= =?utf-8?B?eHRsVXFId3FzVWk0dHFpT0lsRk55T0pldnZEblIvb092U0p4bWZ5alN0Y0ZF?= =?utf-8?B?TjU5a29TMkZuVjBUSXRacjhlbmQ3QktzOGNIU2VWcHNRb1NMWlkwRjhIVEc1?= =?utf-8?B?NDhkSTdGZk1DcDlZTDZqdU9BPT0=?= X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2531; 6:DLDxQUAK1QFgWYwnK4pjKnE7sf//VqsmUdfyYMeyqxzCbKHduLT2bDTpbnblaTO3FDqARoEAPjXqPGwCRjxwucDbaMyz0ss8bgAK2C4BjjfxSxxhu/PTDBtoNQESfr4OV2+pierCQ7OgZrJbsRKqXLQMC3dXPK+oGUSh/bvwEBfeyrBgRefEf+U68z3q8p6uoBYXCoXU+9LspvHVQ//ck9QZAVlIcpxTZCDOOkPALytsAMIWZZ9+7YQd8t2h+RT+nu+CLXBbJVAEh+2+8dVexOzuFd1W7SE+8O6k6TJWGIZ4mPR6KRzFeNxSfsdWYrYOsyXVYbSdOsAkv+CaIL+i3A==; 5:dS1qyh2S3UqXwzWDyXe6XVuExi/yIPh9his0FETkmEvZx8fNDaTF0xba6icRndccWyJRYpEMYt1oKGtwgtoAmgkfnKEtCDKGNqny59YTHu2MC/nLzSMi883SeR7/E5ZfmB43qXyny6ayvRcTB6GpgQ==; 24:GotS31NTFQ+Whd9oR5O2rEfQ00a2+540AYB2eVn/ymPSTteQte1jkiZUWreb7HKpZWIYk1nYZpBsR+CfqS7d2a5Ii2tWl9uBFcmtlWS1VnM=; 7:Mg1jtCi+HXPk+lxfcjymgnGWxXVcOFl4Hq5IwA5wPcpjqR2aDac26Fh9ALvFX2rftZ4nrw92yZCrUe8RbcuuCkFNdKOzbopE/EX2vkhLw4W8YLJ/pNBOSA5Jd6xx1AnlEw/NjTALgZQowmKECfwvOYw0OTSjeeRG04MegbQLnj/Oi8XtPfk8rjkvjBxV1iovT39W1y+2otccOpMMwAijJf5Of2exXvbPx4meJmD/JY1nKXnpOxoXjmw8UbBvBPkrlCcuS1vAcsIb/dCxNPto3X3zk58KckigDHmZelkO0TZv8+ROScEw49XS560TU6LV SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: nokia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Sep 2016 13:25:58.8724 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0701MB2531 Subject: [FFmpeg-devel] [PATCH v3 03/25] avformat/movenc: support for multiple and client-provided track references 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 Cc: =?UTF-8?q?Erkki=20Sepp=C3=A4l=C3=A4?= , OZOPlayer Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Instead of one track reference, allow many, and instead of of track reference type (ie. 'cdsc'), allow many. In addition this patch allows client to explicitly add track references with side packet AV_PKT_DATA_TRACK_REFERENCES containing AVTrackReferences (which of there can many many). Internally MOVTrack's track references can be manipulated with helper functions ff_mov_tref* (and is used by a later patch for reading MOVTRefs). Multiple track references can be useful in particular with timed meta data tracks, indicating the track is related to multiple other tracks. This information ends up in ISO media file box 'tref' as specified by ISO/IEC 14496-12. Signed-off-by: Erkki Seppälä Signed-off-by: OZOPlayer --- libavcodec/avcodec.h | 19 ++++++- libavformat/Makefile | 4 +- libavformat/movenc.c | 153 ++++++++++++++++++++++++++++++++++++++++++-------- libavformat/movenc.h | 5 +- libavformat/movtref.c | 115 +++++++++++++++++++++++++++++++++++++ libavformat/movtref.h | 75 +++++++++++++++++++++++++ 6 files changed, 344 insertions(+), 27 deletions(-) create mode 100644 libavformat/movtref.c create mode 100644 libavformat/movtref.h diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 6ac6646..56bb9b0 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1348,6 +1348,15 @@ typedef struct AVCPBProperties { * Types and functions for working with AVPacket. * @{ */ + +typedef struct AVTrackReferences { + int next_tref_ofs; /**< offset in bytes to the next AVTrackReferences or 0 if this is the last one*/ + char tag[4]; /**< 4cc used for describing this */ + int nb_tracks; /**< number of tracks */ + int tracks[1]; /**< tracks this track refers to (contains nb_tracks entries) */ + /** followed by an optional gap for alignment purposes and another AVTrackReferences is applicaple */ +} AVTrackReferences; + enum AVPacketSideDataType { AV_PKT_DATA_PALETTE, @@ -1525,7 +1534,15 @@ enum AVPacketSideDataType { * should be associated with a video stream and containts data in the form * of the AVMasteringDisplayMetadata struct. */ - AV_PKT_DATA_MASTERING_DISPLAY_METADATA + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + + /** + * Define track references (in particular applicaple for ISO MP4 + * files). The data is a sequence of type AVTrackReferences + * (including the track list that follows it), for as long as + * indicated by the key's length. + */ + AV_PKT_DATA_TRACK_REFERENCES, }; #define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED diff --git a/libavformat/Makefile b/libavformat/Makefile index fda1e17..6a393ed 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -280,10 +280,10 @@ OBJS-$(CONFIG_MLV_DEMUXER) += mlvdec.o riffdec.o OBJS-$(CONFIG_MM_DEMUXER) += mm.o OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o -OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o replaygain.o +OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o replaygain.o movtref.o OBJS-$(CONFIG_MOV_MUXER) += movenc.o avc.o hevc.o vpcc.o \ movenchint.o mov_chan.o rtp.o \ - movenccenc.o rawutils.o + movenccenc.o rawutils.o movtref.o OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 571c2a7..aed30dc 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -2626,14 +2626,22 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, return size; } -static int mov_write_tref_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_tref_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { - avio_wb32(pb, 20); // size + int64_t pos = avio_tell(pb); + int i, j; + avio_wb32(pb, 0); // size ffio_wfourcc(pb, "tref"); - avio_wb32(pb, 12); // size (subatom) - avio_wl32(pb, track->tref_tag); - avio_wb32(pb, track->tref_id); - return 20; + for (j = 0; j < track->trefs.nb_trefs; ++j) { + int64_t pos_sub = avio_tell(pb); + MOVTRef* tref = &track->trefs.trefs[j]; + avio_wb32(pb, 0); // size (subatom) + avio_wl32(pb, tref->tag); + for (i = 0; i < tref->nb_track_ids; i++) + avio_wb32(pb, tref->track_ids[i]); + update_size(pb, pos_sub); + } + return update_size(pb, pos); } // goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it) @@ -2744,8 +2752,8 @@ static int mov_write_trak_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext "Not writing any edit list even though one would have been required\n"); } - if (track->tref_tag) - mov_write_tref_tag(pb, track); + if (track->trefs.nb_trefs) + mov_write_tref_tag(pb, mov, track); if ((ret = mov_write_mdia_tag(s, pb, mov, track)) < 0) return ret; @@ -3482,16 +3490,108 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s) return 0; } +static int mov_tref_copy_from_side_data(MOVMuxContext *mov, MOVTrack *track, AVFormatContext *s) +{ + int size; + int ret; + int i; + MOVTRef *tref; + int *ref_tracks = NULL; + + char *ptr = (void*) av_stream_get_side_data(track->st, + AV_PKT_DATA_TRACK_REFERENCES, + &size); + char *end = ptr + size; + + if (!ptr) + return 0; + + ret = 0; + while (ptr < end) { + AVTrackReferences refs; + int *track_ids; + int cur_size; + int next_track_index; + + if (ptr + sizeof(AVTrackReferences) > end) { + ret = AVERROR(EINVAL); + goto error; + } + + // avoid any potential alignment issues by copying the struct before accessing it + // to a well-aligned address + memcpy(&refs, ptr, sizeof(refs)); + cur_size = sizeof(AVTrackReferences) + (refs.nb_tracks - 1) * sizeof(refs.tracks[0]); + + if (refs.nb_tracks <= 0 || + ptr + refs.next_tref_ofs > end || + ptr + cur_size > end || + refs.next_tref_ofs < 0 || + (refs.next_tref_ofs > 0 && refs.next_tref_ofs < cur_size)) { + ret = AVERROR(EINVAL); + goto error; + } + + // as well as the ids + ref_tracks = av_malloc_array(refs.nb_tracks, sizeof(refs.tracks[0])); + if (!ref_tracks) + goto error; + memcpy(ref_tracks, &((AVTrackReferences*) ptr)->tracks, sizeof(refs.tracks[0]) * refs.nb_tracks); + + ret = ff_mov_tref_find_or_add(&track->trefs, + MKTAG(refs.tag[0], refs.tag[1], refs.tag[2], refs.tag[3]), + &tref); + if (ret < 0) + goto error; + + ret = ff_mov_tref_alloc(tref, refs.nb_tracks, &track_ids); + if (ret < 0) + goto error; + + next_track_index = 0; + for (i = 0; i < refs.nb_tracks; i++) { + int tref_stream_id = ref_tracks[i]; + int stream_idx; + int found = 0; + for (stream_idx = 0; stream_idx < mov->nb_streams && !found; ++stream_idx) + if (mov->tracks[stream_idx].st && + mov->tracks[stream_idx].st->id == tref_stream_id) { + track_ids[next_track_index] = mov->tracks[stream_idx].track_id; + found = 1; + } + if (found) + next_track_index++; + else + tref->nb_track_ids--; + } + + ptr += refs.next_tref_ofs; + + av_free(ref_tracks); + if (refs.next_tref_ofs == 0) + break; + } + return 0; +error: + av_free(ref_tracks); + return ret; +} + static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) { int i; int64_t pos = avio_tell(pb); + int ret; avio_wb32(pb, 0); /* size placeholder*/ ffio_wfourcc(pb, "moov"); mov_setup_track_ids(mov, s); + for (i = 0; i < mov->nb_streams; i++) + if (mov->tracks[i].st) + mov_tref_copy_from_side_data(mov, &mov->tracks[i], s); + for (i = 0; i < mov->nb_streams; i++) { if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) continue; @@ -3503,34 +3603,42 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, } if (mov->chapter_track) - for (i = 0; i < s->nb_streams; i++) { - mov->tracks[i].tref_tag = MKTAG('c','h','a','p'); - mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id; - } + for (i = 0; i < s->nb_streams; i++) + if (!ff_codec_get_id(ff_codec_metadata_tags, mov->tracks[i].tag)) { + ret = ff_mov_tref_add_one_track(&mov->tracks[i].trefs, MKTAG('c','h','a','p'), mov->chapter_track); + if (ret < 0) + return ret; + } for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; if (track->tag == MKTAG('r','t','p',' ')) { - track->tref_tag = MKTAG('h','i','n','t'); - track->tref_id = mov->tracks[track->src_track].track_id; + ret = ff_mov_tref_add_one_track(&mov->tracks[i].trefs, MKTAG('h','i','n','t'), mov->tracks[track->src_track].track_id); + if (ret < 0) + return ret; } else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) { int * fallback, size; fallback = (int*)av_stream_get_side_data(track->st, AV_PKT_DATA_FALLBACK_TRACK, &size); - if (fallback != NULL && size == sizeof(int)) { - if (*fallback >= 0 && *fallback < mov->nb_streams) { - track->tref_tag = MKTAG('f','a','l','l'); - track->tref_id = mov->tracks[*fallback].track_id; - } + if (fallback != NULL && size == sizeof(int) && + *fallback >= 0 && *fallback < mov->nb_streams) { + ret = ff_mov_tref_add_one_track(&track->trefs, MKTAG('f','a','l','l'), *fallback); + if (ret < 0) + return ret; } } } for (i = 0; i < mov->nb_streams; i++) { if (mov->tracks[i].tag == MKTAG('t','m','c','d')) { int src_trk = mov->tracks[i].src_track; - mov->tracks[src_trk].tref_tag = mov->tracks[i].tag; - mov->tracks[src_trk].tref_id = mov->tracks[i].track_id; - //src_trk may have a different timescale than the tmcd track + + ret = ff_mov_tref_add_one_track(&mov->tracks[src_trk].trefs, + mov->tracks[i].tag, + mov->tracks[i].track_id); + if (ret < 0) + return ret; + + //src_trk may have a different timescale than the tmcd track mov->tracks[i].track_duration = av_rescale(mov->tracks[src_trk].track_duration, mov->tracks[i].timescale, mov->tracks[src_trk].timescale); @@ -5340,6 +5448,7 @@ static void mov_free(AVFormatContext *s) av_freep(&mov->tracks[i].vos_data); ff_mov_cenc_free(&mov->tracks[i].cenc); + ff_mov_tref_free(&mov->tracks[i].trefs); } av_freep(&mov->tracks); diff --git a/libavformat/movenc.h b/libavformat/movenc.h index ea76e39..700d8d5 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -26,6 +26,7 @@ #include "avformat.h" #include "movenccenc.h" +#include "movtref.h" #define MOV_FRAG_INFO_ALLOC_INCREMENT 64 #define MOV_INDEX_CLUSTER_SIZE 1024 @@ -110,8 +111,8 @@ typedef struct MOVTrack { unsigned cluster_capacity; int audio_vbr; int height; ///< active picture (w/o VBI) height for D-10/IMX - uint32_t tref_tag; - int tref_id; ///< trackID of the referenced track + + MOVTRefs trefs; int64_t start_dts; int64_t start_cts; int64_t end_pts; diff --git a/libavformat/movtref.c b/libavformat/movtref.c new file mode 100644 index 0000000..dd6059c --- /dev/null +++ b/libavformat/movtref.c @@ -0,0 +1,115 @@ +/* + * ISO base media file format track references + * Copyright (c) 2016 Erkki Seppälä + * + * 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 +#include +#include + +#include "movtref.h" +#include "libavutil/mem.h" + +MOVTRef *ff_mov_tref_find(MOVTRefs *trefs, uint32_t tag) +{ + int i; + MOVTRef *tref = NULL; + + for (i = 0; i < trefs->nb_trefs && !tref; ++i) { + if (trefs->trefs[i].tag == tag) { + tref = trefs->trefs + i; + } + } + + return tref; +} + +int ff_mov_tref_alloc(MOVTRef *tref, int n, int **track_ids_ret) +{ + int ret = av_reallocp_array(&tref->track_ids, tref->nb_track_ids + n, sizeof(tref->track_ids)); + *track_ids_ret = NULL; + if (ret >= 0) { + *track_ids_ret = tref->track_ids + tref->nb_track_ids; + tref->nb_track_ids += n; + } + return ret; +} + +int ff_mov_tref_find_or_add(MOVTRefs *trefs, uint32_t tag, MOVTRef **tref_ret) +{ + int ret; + int i; + MOVTRef *tref = ff_mov_tref_find(trefs, tag); + *tref_ret = NULL; + + for (i = 0; i < trefs->nb_trefs && !tref; ++i) { + if (trefs->trefs[i].tag == tag) { + tref = trefs->trefs + i; + } + } + + if (!tref) { + ret = av_reallocp_array(&trefs->trefs, trefs->nb_trefs + 1, sizeof(*trefs->trefs)); + if (ret < 0) + return ret; + tref = trefs->trefs + trefs->nb_trefs; + trefs->nb_trefs++; + tref->tag = tag; + tref->track_ids = NULL; + tref->nb_track_ids = 0; + } + + *tref_ret = tref; + return 0; +} + +int ff_mov_tref_add_one_track(MOVTRefs *trefs, unsigned tag, int track_id) +{ + int ret; + MOVTRef *tref; + int *ids; + int track_id_exists = 0; + int i; + + ret = ff_mov_tref_find_or_add(trefs, tag, &tref); + if (ret < 0) + return ret; + + for (i = 0; i < tref->nb_track_ids && !track_id_exists; i++) { + if (tref->track_ids[i] == track_id) { + track_id_exists = 1; + } + } + + if (!track_id_exists) { + ret = ff_mov_tref_alloc(tref, 1, &ids); + if (ret >= 0) { + *ids = track_id; + } + } + return ret; +} + +void ff_mov_tref_free(MOVTRefs *trefs) +{ + int i; + for (i = 0; i < trefs->nb_trefs; ++i) + av_freep(&trefs->trefs[i].track_ids); + av_freep(&trefs->trefs); +} diff --git a/libavformat/movtref.h b/libavformat/movtref.h new file mode 100644 index 0000000..091f467 --- /dev/null +++ b/libavformat/movtref.h @@ -0,0 +1,75 @@ +/* + * ISO base media file format track references + * Copyright (c) 2016 Erkki Seppälä + * + * 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 + */ +#ifndef AVFORMAT_MOVTREF_H +#define AVFORMAT_MOVTREF_H + +typedef struct MOVTRef { + uint32_t tag; + int nb_track_ids; + int* track_ids; +} MOVTRef; + +typedef struct MOVTRefs { + int nb_trefs; + MOVTRef *trefs; +} MOVTRefs; + +/** + * @brief Finds a track reference of certain tag from a track; if not + * found, return NULL + * + * @param track The track to search from + * @param tag The tag (4cc) to search for + * @return a MOVTRef describing the track reference or NULL if not found. + */ +MOVTRef *ff_mov_tref_find(MOVTRefs *trefs, uint32_t tag); + +/** + * @brief Allocate space for n more tracks (and increase nb_ids accordingly) returning + * the pointer in *track_ids + * + * @param tref The track reference to allocate tracks to + * @param n Number of tracks to add + * @param track_ids_ret A non-null pointer where the pointer to the first free track + * id is returned to + * @return Returns a value <0 on error, otherwise 0. + */ +int ff_mov_tref_alloc(MOVTRef *tref, int n, int **track_ids_ret); + +/** + * @brief Finds an existing MOVTRef for a track for the given tag, or + * creates a new one if it is missing. Returns the tref in tref_ret. + */ + +int ff_mov_tref_find_or_add(MOVTRefs *trefs, uint32_t tag, MOVTRef **tref_ret); + +/** + * Adds one tref track reference with given track id if it doesn't + * already exist + */ +int ff_mov_tref_add_one_track(MOVTRefs *trefs, unsigned tag, int track_id); + +/** + * Release MOVTRefs + */ +void ff_mov_tref_free(MOVTRefs *trefs); + +#endif /* AVFORMAT_MOVTREF_H */