From patchwork Tue Aug 23 09:03:27 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: 265 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp2182163vsd; Tue, 23 Aug 2016 02:10:45 -0700 (PDT) X-Received: by 10.28.172.195 with SMTP id v186mr18253479wme.97.1471943445587; Tue, 23 Aug 2016 02:10: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 x25si2682542wma.24.2016.08.23.02.10.43; Tue, 23 Aug 2016 02:10: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=@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 D7692689C15; Tue, 23 Aug 2016 12:10:38 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from EUR01-DB5-obe.outbound.protection.outlook.com (mail-db5eur01on0132.outbound.protection.outlook.com [104.47.2.132]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 563E168993A for ; Tue, 23 Aug 2016 12:10:31 +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=uIQxNzg/ItYjQt2h9D2l4nAgcqzOEgpQAN0KPbKW4Rc=; b=kloWUep2LHBOZzD7/ixqvveYjBSdTg78eJdKfHNwmQt3uc5mbN/87g1/7Oi+6NB92LK2PtHkkfWCBsHjsdLTbIZkZ+LBpB+D3dyuOc8oDdkCypobdTyEFuRtkNX838YqIP5d2KzDjS1fC7iaJWzGsxIETMi8XtaOjfkjhrsq/uA= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=erkki.seppala.ext@nokia.com; Received: from erkkise-laptop.vincit.intranet (131.228.2.4) by DB6PR0701MB2535.eurprd07.prod.outlook.com (10.168.76.23) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384) id 15.1.557.21; Tue, 23 Aug 2016 09:10:30 +0000 Received: by erkkise-laptop.vincit.intranet (Postfix, from userid 1000) id 5BA6C4449EE; Tue, 23 Aug 2016 12:04:04 +0300 (EEST) From: To: Date: Tue, 23 Aug 2016 12:03:27 +0300 Message-ID: <1471943019-14136-10-git-send-email-erkki.seppala.ext@nokia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1471943019-14136-1-git-send-email-erkki.seppala.ext@nokia.com> References: <1471943019-14136-1-git-send-email-erkki.seppala.ext@nokia.com> MIME-Version: 1.0 X-Originating-IP: [131.228.2.4] X-ClientProxiedBy: HE1PR03CA0025.eurprd03.prod.outlook.com (10.163.170.163) To DB6PR0701MB2535.eurprd07.prod.outlook.com (10.168.76.23) X-MS-Office365-Filtering-Correlation-Id: 3788feb8-7457-4253-8303-08d3cb3554e4 X-Microsoft-Exchange-Diagnostics: 1; DB6PR0701MB2535; 2:ZVZQ7qPqndzlQvgEuZR0tk81TQ13zEhP2TRgNIIuXDXRhprJ75hjnBw5EuP/QlqMCLFz9M0eQapAouuemDdNWcNL1zKwahyOhd2ZO8e6mOa4SkIrx4ECm9GePpua1GEm1864eLtjMchuhqA39AFjqBh3C3cZ+2OlTlRIuQJMxMk06mn7iuKum40gILW6S3vA; 3:bXarLg73r22vHSso1f+pfevCxmd4uKZa0017SWrJcXyuTJ4gaP5GZBkEV/TLI92HIZa7+CbZcwIO5wFAXz0jhSjq1XFjapVZ0/8xFMf4YVLu7TwqJXuTy1g4kyTPGYkz; 25:0pz/s8Eeevjmwmci5JYjbG/GXP1mvym/seADGcsY+2hEUxHJtqqJzIHHlV2jnng1xdAFuvS5j/BC5tkzysv20hMISAJgpYsAavMwTlP/HyWdo/XVJhV3O4QZlJnlGjv0RtcoNfMZNbGVl5sGsxdBgq/ni+r7DVy4gI2CRvRQ01mwxB6mY0QNP3N3TsoH/5+QlKZ5bJt4BtcVrMiuJHqqMdU6eR9+tVQT96w1hxhF85CLuBRMLAOs7XX7hq1sUDlvTuz33umErNA3vItk10X9ORo0ULMUaCW3xzi8B/ndqXlYf2usykocMDYrif4x+0Q2WCfUpHhzhCwfdM33d406EqwiTKmfuss/0cg46LwgU7FkESTAFvaPQ+SChxZBXyLfpS0MYsCS0Bn9Jy+xSfbzL7kRKrmwqXQp3AQBaS33h4o= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DB6PR0701MB2535; X-Microsoft-Exchange-Diagnostics: 1; DB6PR0701MB2535; 31:eKKPLn4aaeFB1uhZgYcaNa/vzKa9NA/o9wRswvhp3uZwiWvWtV2O1w9l5z6iEY1vhL/0HQAGvCTxWvShC69fCyWtit30QUssLkKnzCqzrra2KiMBYBBD2tyveDYVmmMcvZa3cgK8C7enYQ+tbULvlMcmLol3Q1lzHWeoJuWDH/d1dUu3bzc9xAF0kPkOEweSwB+MxSve01OYOwPSjuT6Vsrr2/NGMGsBLCiL0Cpjbbk=; 20:ya3Hpoitb9xEOZV2HKzik+3uQtKdiFTAJENL7LdNg+n9W2NzSRb3UWHatyApD/YEjGwSNx2x5tKArx73I+uvLpJpE15rBjex8lq2j2FL9xOQ5GNQOPUJugr0V5VoaSZMI6bAdRDQusY9IQ5Qx9Iq2xt14BbB1+He6BTajZcnCd9XjBpcURcpsscEqdLo+xoYFUO7iq5VMoFlo/yeBNLmo6rHzXeFVBMxbZCjWxk/kA9tbwFu271/ZNOT+e87OggigUxa8dSt2GQotixjGBSXyW4mNPw2Inn2YPj5NeeJX9rjkxJAx9Pc2U3hp80uryDbD3USwlSs+3kgI1djFBJ+DUKTIvVyiYJxlTSOm22CYRk9Bi2NmUxfv6Pdwm3arxsMGD2cYw96F+0KjkmYzri4ktKrz/YiFX4/RdfIEBrbqaW1CTF0mEMfHVp5nH7tf16tFSe2t452KfFFVcRqQlCHONvWDiuWmWr6UT8uaOtA9ns/vH4HVYymtD+xxqTZF4bG 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)(5005006)(8121501046)(3002001)(10201501046)(6055026); SRVR:DB6PR0701MB2535; BCL:0; PCL:0; RULEID:; SRVR:DB6PR0701MB2535; X-Microsoft-Exchange-Diagnostics: 1; DB6PR0701MB2535; 4:mIiLb6Ltbtjf1PduxyHhyq9/fKbyAV3aSBoOiYhdEN27UfKWhV+D9EE2L2FdpbscZyaZhiTxepC69UDuEutJIPsuLWvw0aNwEyvLpoa+p1+onwn/yZhKXNxu4JJidVqALQ28eNSLutf/RrI6jFE4fJFo2dfb0ZKqwMkNbtGiMN0uo7PRj3p4ITNGVruUBEryZC//Nv8jo8UtvH/uMj5pP+nqAoTU4I7vnOEUuIqVYVSod09OVuF12CyFg4T+Ch+g0Qcf2XYv0EX9LGoVlPpJY+nVx8gI4QRh0uCgiYPXMT2Yk2S/0TbfSyCZ/4ExERvxf+V6uxVn653hduAr9nPZMJpqeGUq9w+YZOMcSwVbyG6CzrvCVw+Rd9I+tEtiESngKQW5TE0cJ+ykQRg13NCtbu6Eb4CZJKrMEeBdJ0l3nTuwpkyoDpWDW/fPBK301Kf8 X-Forefront-PRVS: 004395A01C X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(7916002)(199003)(189002)(66066001)(19580405001)(92566002)(33646002)(81166006)(8676002)(81156014)(19580395003)(450100001)(36756003)(50466002)(4001430100002)(229853001)(42186005)(86152002)(50986999)(105586002)(86362001)(2351001)(5660300001)(6116002)(586003)(101416001)(76176999)(68736007)(50226002)(23676002)(52956003)(46386002)(106356001)(4326007)(3846002)(47776003)(110136002)(90966002)(107886002)(2950100001)(2906002)(7846002)(189998001)(7736002)(2870700001)(305945005)(122856001)(5820100001)(45336002)(97736004)(2876002); DIR:OUT; SFP:1102; SCL:1; SRVR:DB6PR0701MB2535; H:erkkise-laptop.vincit.intranet; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; Received-SPF: None (protection.outlook.com: nokia.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtEQjZQUjA3MDFNQjI1MzU7MjM6RGhRREo5MFVXYWpkdWhLcWUvcTlqK3Ji?= =?utf-8?B?K1lNWExYYitueG9yNTdTbG01cm9sTnhCdklUcHp2b0s1YTJhd0RLR2tEWmFF?= =?utf-8?B?QTkrNTJicDBwOFlkMFB3RlZEcEwyK3FyQkRRcC9WakRTTDFEUDI1dGl3a0lI?= =?utf-8?B?cHlkb1l6dC9ZRXVYSmJXSW5XSWtzUnA4VitwTmNsb1NVcXdEemhSQlFUZ2tJ?= =?utf-8?B?S2JBYkJVWVl1WG1ZZzVlK2MwR0VMVWd2UDFOdE14VzhjS2FkNVRYWTN4ZzRE?= =?utf-8?B?ZzhHczhsQ2JIRTlDak1QMFN5cUxNVzVOb3U4TW5ublF5UFJTVndyZHRFeWw2?= =?utf-8?B?YllwRFNJbjZ0cVlpbVI5cjdlWXpBbzBZN0RuZHF5Z2JHMWc0YUE3UUFHNTdM?= =?utf-8?B?TWwzT2s5TmNUVXhBTmFucTBLYjVqdnpCZTg4NThzU1FpR2MyVWo5cW42MnRj?= =?utf-8?B?TjBlUE5tNzhMU3NMUlkyY1pNaXltbkY3Qyt1WW42akNLeGJxRUtMYmwvQ2Yr?= =?utf-8?B?akdab0VIL1lLQlZ4Z205ZHZGWnlGdlRNb0owcWFMRnpxYzNuZitESEJwV0xa?= =?utf-8?B?WUtzNlpNc2E1TDc5N0FTVnEyMVJhYnUrWmx3ZWVFSDZ2dCtPTDdTSXJNak1V?= =?utf-8?B?blBKdGpqZ0dlWlFiMW0zUmorSWdXUktLZERMWUJ0Z3RMWlZOZk1UbjZOSTZI?= =?utf-8?B?a0pmajg0OVRnVTJRREtMMFhsUjJVUU1NY0FTVG9KUnJEdW5EZmZBZ3MzYUpF?= =?utf-8?B?Zk9qeWgrK01HUUdTT3ZvS3Z6NlIzcEJmTzNQdTdXWW5OdERyc1MxL2t1dnpG?= =?utf-8?B?TE1jOFRvaTZRa1g1Y1pqWXNmUStyV1RqL3Q3Z3VDWERVQ2VmMndYak5LRjgw?= =?utf-8?B?ckoxWXhSN3B2emxwYW5jUFRxMm80UVBNNWk3dVYxQWV3aFYvWWhNR0lrYThj?= =?utf-8?B?cmtSVXVBTTJWTThKUjZIbng0WmhYM3hhb1dLaGpPRHZKakRVbGJtWWhjUVN6?= =?utf-8?B?NDc1M0FiNC94TmdZQllMa201SFpNaUJCekxKSFBzQ3I0eDBBM056S3haRHgz?= =?utf-8?B?N3FLNEllNHZtTDlHYm9WOVg4emlqZU1QNmlUbjhXUkM5RFJNWlRUUWhSUGsv?= =?utf-8?B?N1NJVUFXdHluTEQ0OUJ6cXdEamJMcDhEUlBUSDJyWXpncDBLRnlaM05DUnlZ?= =?utf-8?B?ZVhGY09XRjRaRXRTWDU5bzU2a2FtN1ZYeWhPYkM5TDl3Vmlad2xRRWhLNGRX?= =?utf-8?B?QldkWjJzODlrc3ZJYUZWcnpSN2dSeDQxQ1VIOWdXSnFhRWRNUFhJSHZVbkc1?= =?utf-8?B?TzdxdTlVOExURlpKeEJmR2RXRXJUbDZnbm90TWtUM1Z1Q0tzUXBEK0QxaXdY?= =?utf-8?B?WjVTSlRwanc5MWhIR2pramRla3JiSncxZUhLa0ZkZ2lFeTJRQzBLcjNyZ1pw?= =?utf-8?B?ekhFUURVUnVoYWgrUFp6eGJYUnUrQkZlcjFSdFNTWi9kbUNnV2JrSG9FQ3E0?= =?utf-8?B?QXhvRFZrbEh0cTVza0xPV2lpRERKYWpYdXRBMHdmUVMrT0U3aitDK2NSTklz?= =?utf-8?B?aFRBQVZuNisrYXVVRFRoMElaelp4UlNGdTdiczJIWjlnUzNZb25kRS9TalNS?= =?utf-8?B?dXNkWHZjQUxKdUxXT2dqOGx2OTRuMGprMkI4WlNVd3ZJbmdMYWdmTFB3Mm5Y?= =?utf-8?B?Vm95SFhRb3lUWEV1eUd6c0ZMTHdUOWJlbXgra0E5ZnNsTGRBQVRmOUFxOGZN?= =?utf-8?Q?DimC9+Rw0bCgdZ2L8c4mtd6uTrXjofcySkawEyU=3D?= X-Microsoft-Exchange-Diagnostics: 1; DB6PR0701MB2535; 6:2p2Sb7528Wg86YXaen20koyHG0yhqAkY1Cdx2T0U6QQIogld4ps42MYPe7psrieGWun2xQZXiCykmTAhdNavaVIAxVnoNimy3nUHgxzCoIyDRTXwn+OEqFif3sCryVMCvgspGJOiu4zmk8y742iuPMPied+kGiuGDqJqD6Qn2Yzc5Mi9YGbf7oBIrA+ISNBUBBPOka55cT9Vxuzw1Ts17UCUMOj9zZRjH051z8BBoiqVPfl0bEdpIDnvR2OmkJ5tqSLEV2c+6G8RynCsvEL0fap4FSb0wIQna5OooNqDvOpJ5URF7e2l3VNt86hdffxnDZPFIDUVYJKAKD0WuB7uxg==; 5:PFAv10c79fgpC15BCcQnYV3fC1ru3pFScqKlkWpj2BqJLSIPZBxe5l8UW/KEaYq2B//bAneA+hRk5EGGOw5U820w37N871efFlpBiwJj+SR+K//U1dNBLa9Ug0RME4pmXM9OCoVKJYPP7uzBkS1VYw==; 24:SITeopjTRXbN6g6GB82d6yMkqwZsEuRKIpi9d5K+V6tMQyWVSqX2dAGssFyO4mnakgP0PMtCeOmW58qXBatD8t7KQGCVYmAWZzSxASwuSJE=; 7:np/lxCP0MzNbqOMR6AhorOSKivOv2Iw9ySToRgcAMPU0IHGSI9PhE5RVWdn4kyfLqfZB5D3fNxiZ5SYCyKfwf03iBgNd+JzHAR0S94ldVCP1T1OW0XE1ui/Gi62fWE2VcEDoIrPjyZ+OP321i5+TSZ4QZe6SLgllC4yN31sGNBWFo0frZJI+S/18NxNFbqBgz55nGnjEHkELu5xTMmWhGmkY2IDqUi5QLOrpRNLRmYDM6wPOgHxMo07T/HJsTVnS SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: nokia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Aug 2016 09:10:30.6778 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB6PR0701MB2535 Subject: [FFmpeg-devel] [PATCH 09/21] libavformat/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" From: Erkki Seppälä Instead of one track reference, allow multiple. In addition, allow client to explicitly add track references with side packet AV_PKG_DATA_TRACK_REFERENCES containing AVTrackReferences. MOVTrack's track references can be manipulated with helper functions ff_mov_*tref*. 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 | 17 +++- libavformat/movenc.c | 245 ++++++++++++++++++++++++++++++++++++++++++----- libavformat/movenc.h | 61 +++++++++++- libavformat/movenchint.c | 11 ++- 4 files changed, 303 insertions(+), 31 deletions(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 756eda5..893b89b 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1348,6 +1348,13 @@ typedef struct AVCPBProperties { * Types and functions for working with AVPacket. * @{ */ + +typedef struct AVTrackReferences { + char tag[4]; /** 4cc used for describing this */ + int nb_tracks; /** number of tracks */ + /** followed by: int tracks[nb_tracks]; -- tracks this track refers to */ +} AVTrackReferences; + enum AVPacketSideDataType { AV_PKT_DATA_PALETTE, @@ -1525,7 +1532,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/movenc.c b/libavformat/movenc.c index c63fdc4..072e660 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -2631,14 +2631,30 @@ 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); + int64_t pos_sub; + int i; + int tref_idx; + 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 (tref_idx = 0; tref_idx < track->nb_trefs; tref_idx++) { + pos_sub = avio_tell(pb); + avio_wb32(pb, 0); // size (subatom) + avio_wl32(pb, track->trefs[tref_idx].tag); + for (i = 0; i < track->trefs[tref_idx].nb_ids; i++) { + int stream_idx; + int tref_stream_id = track->trefs[tref_idx].ids[i]; + for (stream_idx = 0; stream_idx < mov->nb_streams; ++stream_idx) + if (mov->tracks[stream_idx].st->id == tref_stream_id) { + avio_wb32(pb, mov->tracks[stream_idx].track_id); + break; + } + } + 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) @@ -2666,7 +2682,8 @@ static int mov_write_udta_sdp(AVIOContext *pb, MOVTrack *track) char buf[1000] = ""; int len; - ff_sdp_write_media(buf, sizeof(buf), ctx->streams[0], track->src_track, + ff_sdp_write_media(buf, sizeof(buf), ctx->streams[0], + track->nb_src_tracks ? track->src_tracks[0] : 0, NULL, NULL, 0, 0, ctx); av_strlcatf(buf, sizeof(buf), "a=control:streamid=%d\r\n", track->track_id); len = strlen(buf); @@ -2749,8 +2766,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->nb_trefs) + mov_write_tref_tag(pb, mov, track); if ((ret = mov_write_mdia_tag(s, pb, mov, track)) < 0) return ret; @@ -3480,17 +3497,139 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s) return 0; } +MOVTRef *ff_mov_find_tref(MOVTrack *track, uint32_t tag) +{ + int i; + MOVTRef *tref = NULL; + + for (i = 0; i < track->nb_trefs && !tref; ++i) { + if (track->trefs[i].tag == tag) { + tref = track->trefs + i; + } + } + + return tref; +} + +int ff_mov_find_or_add_tref(MOVTrack *track, uint32_t tag, MOVTRef **tref_ret) +{ + int ret; + int i; + MOVTRef *tref = ff_mov_find_tref(track, tag); + *tref_ret = NULL; + + for (i = 0; i < track->nb_trefs && !tref; ++i) { + if (track->trefs[i].tag == tag) { + tref = track->trefs + i; + } + } + + if (!tref) { + ret = av_reallocp_array(&track->trefs, track->nb_trefs + 1, sizeof(*track->trefs)); + if (ret < 0) + return ret; + tref = track->trefs + track->nb_trefs; + track->nb_trefs++; + tref->tag = tag; + tref->ids = NULL; + tref->nb_ids = 0; + } + + *tref_ret = tref; + return 0; +} + +int ff_mov_add_tref_track(MOVTRef *tref, int id) +{ + int ret = av_reallocp_array(&tref->ids, tref->nb_ids + 1, sizeof(tref->ids)); + if (ret >= 0) { + tref->ids[tref->nb_ids] = id; + tref->nb_ids++; + } + return ret; +} + +// replaces all tref ids with ones that refer to the track's src_tracks +int ff_mov_map_trefs_from_src_tracks(MOVMuxContext *mov, MOVTrack *track, MOVTRef *tref) +{ + int ret; + int i; + + ret = av_reallocp_array(&tref->ids, track->nb_src_tracks, sizeof(tref->ids)); + if (ret < 0) { + // so we may have added in the tag, but it has no ids.. but we + // probably cannot undo it either, because we couldn't + // allocate memory. + return ret; + } + tref->nb_ids = track->nb_src_tracks; + + for (i = 0; i < track->nb_src_tracks; ++i) { + tref->ids[i] = mov->tracks[track->src_tracks[i]].st->id; + } + return ret; +} + +/** sets a single tref id */ +static int mov_set_one_tref_track(MOVTRef *tref, int id) +{ + int ret; + ret = av_reallocp_array(&tref->ids, 1, sizeof(*tref->ids)); + if (ret < 0) + return ret; + tref->nb_ids = 1; + + tref->ids[0] = id; + return ret; +} + +static int mov_copy_tref_side_data(MOVMuxContext *mov, MOVTrack *track, AVFormatContext *s) +{ + int size; + int ret = 0; + int i; + char *ptr = (void*) av_stream_get_side_data(track->st, + AV_PKT_DATA_TRACK_REFERENCES, + &size); + + if (!ptr) + return 0; + + while (ret == 0 && size > 0) { + AVTrackReferences *refs = (void*) ptr; + MOVTRef *tref; + int cur_size; + int* stream_ids = (void*) (refs + 1); + if (ff_mov_find_or_add_tref(track, MKTAG(refs->tag[0], refs->tag[1], refs->tag[2], refs->tag[3]), + &tref) < 0) + ret = -1; + + for (i = 0; i < refs->nb_tracks; i++) + ff_mov_add_tref_track(tref, stream_ids[i]); + + cur_size = sizeof(*refs) + refs->nb_tracks * sizeof(*stream_ids); + ptr += cur_size; + size -= cur_size; + } + 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++) { + mov_copy_tref_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; @@ -3502,14 +3641,27 @@ 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; + if (!ff_codec_get_id(ff_codec_metadata_tags, mov->tracks[i].tag)) { + MOVTrack *track = &mov->tracks[i]; + MOVTRef *tref = NULL; + ret = ff_mov_find_or_add_tref(track, MKTAG('c','h','a','p'), &tref); + if (ret < 0) + return ret; + mov_set_one_tref_track(tref, mov->chapter_track); + track->nb_src_tracks = 1; /* this seems an odd hardcoded number.. */ + } } 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; + MOVTrack *track = &mov->tracks[i]; + MOVTRef *tref; + ret = ff_mov_find_or_add_tref(track, MKTAG('h','i','n','t'), &tref); + if (ret < 0) + return ret; + ret = ff_mov_map_trefs_from_src_tracks(mov, track, tref); + 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, @@ -3517,21 +3669,55 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, &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; + MOVTRef *tref; + ret = ff_mov_find_or_add_tref(track, MKTAG('f','a','l','l'), &tref); + if (ret < 0) + return ret; + ret = mov_set_one_tref_track(tref, *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 - mov->tracks[i].track_duration = av_rescale(mov->tracks[src_trk].track_duration, - mov->tracks[i].timescale, - mov->tracks[src_trk].timescale); + MOVTrack *track = &mov->tracks[i]; + if (ff_codec_get_id(ff_codec_metadata_tags, track->tag) && + track->nb_src_tracks) { + MOVTRef *tref; + ret = ff_mov_find_or_add_tref(track, MKTAG('c','d','s','c'), &tref); + if (ret < 0) + return ret; + ret = ff_mov_map_trefs_from_src_tracks(mov, track, tref); + if (ret < 0) + return ret; + } + } + + for (i = 0; i < mov->nb_streams; i++) { + MOVTRef *tref = ff_mov_find_tref(&mov->tracks[i], MKTAG('t','m','c','d')); + if (tref) { + /* This fragment only works if there is one source track */ + if (mov->tracks[i].nb_src_tracks > 1) { + return -1; + } else { + int src_trk_idx = mov->tracks[i].src_tracks[0]; + MOVTrack *src_trk = &mov->tracks[src_trk_idx]; + MOVTRef *src_tref = NULL; + + ret = ff_mov_find_or_add_tref(src_trk, MKTAG('t','m','c','d'), &src_tref); + if (ret < 0) + return ret; + + ret = mov_set_one_tref_track(src_tref, mov->tracks[i].st->id); + if (ret < 0) + return ret; + + //src_trk may have a different timescale than the tmcd track + mov->tracks[i].track_duration = av_rescale(src_trk->track_duration, + mov->tracks[i].timescale, + src_trk->timescale); + } } } @@ -5239,7 +5425,11 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde /* tmcd track based on video stream */ track->mode = mov->mode; track->tag = MKTAG('t','m','c','d'); - track->src_track = src_index; + ret = av_reallocp_array(&track->src_tracks, 1, sizeof(*track->src_tracks)); + if (ret < 0) + return ret; + track->nb_src_tracks = 1; + track->src_tracks[0] = src_index; track->timescale = mov->tracks[src_index].timescale; if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME) track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME; @@ -5321,7 +5511,7 @@ static void enable_tracks(AVFormatContext *s) static void mov_free(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; - int i; + int i, j; if (mov->chapter_track) { if (mov->tracks[mov->chapter_track].par) @@ -5341,6 +5531,11 @@ static void mov_free(AVFormatContext *s) av_freep(&mov->tracks[i].vos_data); ff_mov_cenc_free(&mov->tracks[i].cenc); + av_freep(&mov->tracks[i].src_tracks); + for (j = 0; j < mov->tracks[i].nb_trefs; j++) { + av_freep(&mov->tracks[i].trefs[j].ids); + } + av_freep(&mov->tracks[i].trefs); } av_freep(&mov->tracks); diff --git a/libavformat/movenc.h b/libavformat/movenc.h index ea76e39..5bf9469 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -78,6 +78,12 @@ typedef struct MOVFragmentInfo { int size; } MOVFragmentInfo; +typedef struct MOVTRef { + uint32_t tag; + int nb_ids; + int* ids; +} MOVTRef; + typedef struct MOVTrack { int mode; int entry; @@ -110,15 +116,16 @@ 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 + MOVTRef* trefs; + int nb_trefs; int64_t start_dts; int64_t start_cts; int64_t end_pts; int end_reliable; int hint_track; ///< the track that hints this track, -1 if no hint track is set - int src_track; ///< the track that this hint (or tmcd) track describes + int nb_src_tracks; ///< number of src tracks + int *src_tracks; ///< the tracks that this hint (or tmcd or cdsc) track describes AVFormatContext *rtp_ctx; ///< the format context for the hinting rtp muxer uint32_t prev_rtp_ts; int64_t cur_rtp_ts_unwrapped; @@ -248,4 +255,52 @@ int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt, uint8_t *sample_data, int sample_size); void ff_mov_close_hinting(MOVTrack *track); +/** + * @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_find_tref(MOVTrack *track, uint32_t tag); + +/** + * @brief Finds a track reference of certain tag from a track; if not + * found, create an empty track reference object for the track put it + * into the track. + * + * @param track The track to search from + * @param tag The tag (4cc) to search for + * @param tref_ret A pointer to the the found or created tref is + * returned here. Must not be NULL. + * @return Zero on success, an AVERROR error code on failure. On error + * no modifications have been performed. + */ +int ff_mov_find_or_add_tref(MOVTrack *track, uint32_t tag, MOVTRef **tref_ret); + +/** + * @brief Adds a a track to a track reference that may or may not be + * put into a track + * + * @param tref The track reference to add the track for + * @param tag The tag (4cc) of the track reference + * @return Zero on success, an AVERROR error code on failure. On error + * no modifications have been performed. + */ +int ff_mov_add_tref_track(MOVTRef *tref, int id); + +/** + * @brief Copies track->src_tracks from a track to the give tref + * + * @param mov The MOV muxer context + * @param track The track to take the src_tracks from + * @param tref The tref to translate the src_tracks into + * @return Zero on success, an AVERROR error code on failure. On error + * no modifications have been performed. However, probably + * prior to this call src_tracks has been manipulated and now + * it is out-of-sync, so this is pretty fatal. + */ +int ff_mov_map_trefs_from_src_tracks(MOVMuxContext *mov, MOVTrack *track, MOVTRef *tref); + #endif /* AVFORMAT_MOVENC_H */ diff --git a/libavformat/movenchint.c b/libavformat/movenchint.c index 964026e..61ad09b 100644 --- a/libavformat/movenchint.c +++ b/libavformat/movenchint.c @@ -32,10 +32,17 @@ int ff_mov_init_hinting(AVFormatContext *s, int index, int src_index) MOVTrack *track = &mov->tracks[index]; MOVTrack *src_track = &mov->tracks[src_index]; AVStream *src_st = s->streams[src_index]; - int ret = AVERROR(ENOMEM); + int ret; track->tag = MKTAG('r','t','p',' '); - track->src_track = src_index; + ret = av_reallocp_array(&track->src_tracks, + track->nb_src_tracks + 1, + sizeof(*track->src_tracks)); + if (ret != 0) + goto fail; + ret = AVERROR(ENOMEM); /* default error for the rest of the errors */ + track->src_tracks[track->nb_src_tracks] = src_index; + track->nb_src_tracks++; track->par = avcodec_parameters_alloc(); if (!track->par)