From patchwork Tue Oct 29 21:50:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jose Santiago X-Patchwork-Id: 52553 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a59:b6a7:0:b0:48e:c0f8:d0de with SMTP id r7csp645391vqj; Tue, 29 Oct 2024 15:09:49 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCUWOhV+STEKcvgs5DCuXFHWSYhGgd976Ltntfm2uGpy+lacEbbc4M9GGSRj0xEiUY0ixXCmNJzgWwPI96KzMDPa@gmail.com X-Google-Smtp-Source: AGHT+IGQij2UpDmmyasPYX45kCBCCx0Y8+vy5WEuHlDlVLuH/MHP8seTieCFr7K+tW0FNibGNN9z X-Received: by 2002:a05:6402:42d5:b0:5c9:6b2b:5502 with SMTP id 4fb4d7f45d1cf-5cbbf89209fmr4331527a12.3.1730239789520; Tue, 29 Oct 2024 15:09:49 -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 4fb4d7f45d1cf-5cbb6230131si6248930a12.135.2024.10.29.15.09.48; Tue, 29 Oct 2024 15:09:49 -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=@haivision.com header.s=selector1 header.b=E6TWUW0p; arc=fail (body hash mismatch); 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 sp=NONE dis=NONE) header.from=haivision.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 05CD968DC7B; Tue, 29 Oct 2024 23:50:42 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from YT6PR01CU002.outbound.protection.outlook.com (mail-canadacentralazon11022129.outbound.protection.outlook.com [40.107.193.129]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F048768DB20 for ; Tue, 29 Oct 2024 23:50:34 +0200 (EET) ARC-Seal: i=2; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=fail; b=Tmy7EQpr4kf3M/ZNKdkAq4sXXbGCXaiOoMiFMDiZoMYmr5/P5dKSBFulv0BBmHrsXwl8GVsEHzQzlhWRhMH4A+MVLqf+YgkkzK6js1OG8/+p/fO1AwYgriCjOXppssoga8FH1Zefh1SCzqDXhJhnp12VTHXt6wHyC4VrbnRdbaOb4FVWtCDo8KlD0aHlsXtRkRLGOehXAthpMNuwXbUO2XfHUwAOroVCBkZXZRc9NxqdjVz9Wf0ccTk9NcTcoYN4CwJOq5UZmMw5MhwYioSf1WLT/OtOaI0GORvl8m9JCyJ25/QBqeNtMWhpDaUaSS+szYwGM9a+syA5ByPukhMIpQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=kGE5kcAVeqYbWLD2jl8sgJQAAeu6Gp9FpIpccrYoiFM=; b=Zl+ELy/+Z11pNfbWSublEgyX/PCqs4/K4uK1GjGc6EIGHQCOFZTLfSR/jdpi3aYehZBe+PjfCk5MFMkxLv5uojTV2/Eq6rXeJl0LdaAz2QbiwpBDlA2WvxaZm5ar0buiK38T4hRqVPUw4CUj5atnvCtsUQ7XJTYcdw9afvP07tX4yy7BVgvYNwRIZ82ROJ1JP2hJSuWrls04SFavKOtJjfhgp1/XWmx53EdNvcYm4fPNXVlV9YURvmHDJFk4qhIBgTprtQxuQ/AIqGRBYBIcBNljxcjrucXxbw71wkl9vnK/Xc4eUodpLxO9qyB33101i/R3JDG7gn3i0P8gP+Uq7Q== ARC-Authentication-Results: i=2; mx.microsoft.com 1; spf=pass (sender ip is 51.81.11.26) smtp.rcpttodomain=ffmpeg.org smtp.mailfrom=haivision.com; dmarc=pass (p=none sp=none pct=100) action=none header.from=haivision.com; dkim=none (message not signed); arc=fail (47) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=haivision.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=kGE5kcAVeqYbWLD2jl8sgJQAAeu6Gp9FpIpccrYoiFM=; b=E6TWUW0p7taTFQ6IncrPLWteRQymk8GilT2oUElfoiuFOKhh3heNkjMYifHbLwO9CiFkiZCIGuIRovzcu88za9XKkIxb7Ui1Ju4rgwnQpEJmpHyZIn7YlF1kUmPFAKQMO12bBj1FI5ABvCNGRTvBPXN9rbgituE+uItFGSldLiM= Received: from QB1P288CA0028.CANP288.PROD.OUTLOOK.COM (2603:10b6:c00:2d::41) by YQBPR0101MB9904.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:c01:7c::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8093.32; Tue, 29 Oct 2024 21:50:28 +0000 Received: from QB1PEPF00004E0F.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:c00:2d:cafe::56) by QB1P288CA0028.outlook.office365.com (2603:10b6:c00:2d::41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8093.26 via Frontend Transport; Tue, 29 Oct 2024 21:50:28 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 51.81.11.26) smtp.mailfrom=haivision.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=haivision.com; Received-SPF: Pass (protection.outlook.com: domain of haivision.com designates 51.81.11.26 as permitted sender) receiver=protection.outlook.com; client-ip=51.81.11.26; helo=vin2-relay.sendergen.com; pr=C Received: from vin2-relay.sendergen.com (51.81.11.26) by QB1PEPF00004E0F.mail.protection.outlook.com (10.167.240.7) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.8114.16 via Frontend Transport; Tue, 29 Oct 2024 21:50:28 +0000 Received: from YT5PR01CU002.outbound.protection.outlook.com (mail-canadacentralazlp17011054.outbound.protection.outlook.com [40.93.18.54]) by vin2-relay.sendergen.com (Postfix) with ESMTPS id 1D41A107BEE5E for ; Tue, 29 Oct 2024 17:50:27 -0400 (EDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=N1eGHytxvVgQcg7PJlwn43+ND/xyhWv72xHr6SJvOS+ir0s8pnZA+7CzLbGpcGAkgBGcmm8w58tgOXWQqHC9J/v4cNKmIU9F4nlgWF9SymNMraiX79poHAS+i692yKTaCPuTpGi8O1SGxj+//CMz9sOu3tWSBi6xKCQrkIIzQaP0LbtPv92B7KMHTaG5eG+aBpAUuDnyFfW29PrfsKnoaZtehgiVk73AbJSOjSCs9hj3OtKVx7oMSSWYbdqfuZ7HbE5/mVUl+nB8y2H2pRYtH8PBJQxuVQjUbEGasLHN7GiSGQmjWmsFwO15Bi28QRinS+iu2rj1W5T6SnzCi8Karg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ztxAvJpndMHMCI6ehWvZ/LbkN1ZO/AXGOkBbXfViTEQ=; b=UOrSHPag5rEZpEt2S4RRvIp6icScVj/v+i6EyPTQ+TejgHcz2JmQrFXL5Ith7z9hqBeyfnaUg1mwy199yTzmgOUWpk0hzSwHLE9HlUtqPD5i27NkiFB/AtAeS0/JhV2WGV3db1YpemHYSHpiIUBUFOngGPcQ8JzEIsCLqi1VjzPIvFJxrZul8PS9N/2i2Km0vV2ocmBamR0HLYO5sxQkJhhxQa0KsHLi1TlZPj2D3y3W9VfH2rEE0IElN7fxI3BRGdhlPPoJ3n+j73w3sMvkG6jrNQx6JhIYKLzXyU7qopvvMUOznaJhNFjyZTU3qnGaOGzFwcR4Y+bZYh/h3k0cYg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=haivision.com; dmarc=pass action=none header.from=haivision.com; dkim=pass header.d=haivision.com; arc=none Authentication-Results-Original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=haivision.com; Received: from YQBPR0101MB8847.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:c01:5a::21) by YT1PR01MB8236.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:b01:c3::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8093.32; Tue, 29 Oct 2024 21:50:25 +0000 Received: from YQBPR0101MB8847.CANPRD01.PROD.OUTLOOK.COM ([fe80::e543:3c2c:cf16:e170]) by YQBPR0101MB8847.CANPRD01.PROD.OUTLOOK.COM ([fe80::e543:3c2c:cf16:e170%7]) with mapi id 15.20.8093.027; Tue, 29 Oct 2024 21:50:25 +0000 Message-ID: <6bc8f78c-ec30-4697-845a-086cff73e51f@haivision.com> X-SG-Signature: true Date: Tue, 29 Oct 2024 16:50:24 -0500 User-Agent: Mozilla Thunderbird Content-Language: en-US To: ffmpeg-devel@ffmpeg.org From: Jose Santiago X-ClientProxiedBy: CH0PR13CA0029.namprd13.prod.outlook.com (2603:10b6:610:b1::34) To YQBPR0101MB8847.CANPRD01.PROD.OUTLOOK.COM (2603:10b6:c01:5a::21) MIME-Version: 1.0 X-MS-TrafficTypeDiagnostic: YQBPR0101MB8847:EE_|YT1PR01MB8236:EE_|QB1PEPF00004E0F:EE_|YQBPR0101MB9904:EE_ X-MS-Office365-Filtering-Correlation-Id: 9c9b3052-4dcd-4677-df18-08dcf863b3d3 X-MS-Exchange-AtpMessageProperties: SA X-SG-Stamp: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0;ARA:13230040|1800799024|376014|366016; X-Microsoft-Antispam-Message-Info-Original: qqW92OwhE6McCAyRfUvk3gXf7oXPUsTrOCpj6bPb1EXLd5NENSstHFlIirsTYnmddDwq9qwhd9dTV/Pf1RoxEnyotsNPaWGm+RFhExwmOMTz+5sWHb8PatT4rVRTvcVb0JKFompjKdR8ubvYpZ9cCDJSBCx4D8zFLA9xNbBx0iaar7661HWHxpnugbadpwSvbTCNycXGYUsclFDCBQhQHxyeCWyVfhK+8BbWT52mL9EmEBe/cbUJGrCH7lTg1oGANXLjsaO25KO5BV0nnj55jZcJn/ddyrMkDjxG2E2fNNMzrnYhfnuJEwPmYqIIjJ3xOEn3PuuuebnXrB99JiemZ1P/lc2iua8v+RZMDayomV3vVLCXLVwC/PD6IddpHmVCK6gjNNM2T939DLMDoNcwMLZU6u0GprDxmT7qzAqwM3/UBd9NGrx3W0ZW2dBIZhdOJC4iuhfMr40vmydwTEsF5hL9se3bJS/AOBUhc13on8DVDfVQ9QkkNwnebZJUmzEVRXNorD8tD0BfgugM9H7cDTn5iO8W+Nl4Y8zqaC0hPdeauwOJXOGPTsaMfMgoOGbqJTfAN0gXjMIDO/gxWRewd6yPEBsYB9FGzhT2wAlCn0MexY277ECvR4xN2OnxnJFhc6fSCx2ixOjvnyDPT5mDOLZpgODNT1Hs5OHW7C62f0Hv3OuRxouNgZhT6MGWw/CnWFZP5APe6s6kG9gaZivOYWCtNYwhT45m225WJDHt3N1T661Q/4qXm3AXRseHxNlQ1LlOus3av6GNz9rFGpqwAHWURtcNqZPeEaDl0U+rGL6hkODLv3slowLJ3COfX2u2iinuYe7ial0q+BM1MzCtpDeyVVlyR+lM3wUXSfkOgbfWZS0aizau3pEvseiKh1LCXV5hbfFn/bQm0mAILMA9bWTMbqkkgxtS6spuOJlt0yQUkZaZywmwNyHsO2B5G6cGLLuzEvreOqxzwOoNvEoQ/MUTPBJ+Y7or+BrsBAy7Xa54o0+Uj37KLflHpwYpQTz9lLCrFzOwvPjgcejwCeiCEWfwW/2d6oHT6Qfwvr62bY46qIgy+lUMDYYjstziGICM/G3KK6Cpcaa3OwRfwX/h6cpkhnbD3QIPd2qMmHbkNGVuV6mRvuw5cATzQcV+xsAaNhYfrDHZQ2L4Yo+gBJ5WzfUfl+1W/xjwVZLYUyloi+8z82euRFRmOZmE18F5wFVQHu12Y8bBEjM+1aoLKH3f/+K+DrylsU822zAaF8cDqHOL9/Q/vk625+N2td618sqlpgUURkZ+p0wjtyBUf/hEbBvPrBi8BGX4JPsY1OYE6cl75YLdU03FAS7AEpk5j0a8 X-Forefront-Antispam-Report-Untrusted: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:YQBPR0101MB8847.CANPRD01.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(13230040)(1800799024)(376014)(366016); DIR:OUT; SFP:1102; X-MS-Exchange-Transport-CrossTenantHeadersStamped: YT1PR01MB8236 X-Scanned-By: EmailPostmaster on 51.81.11.26 X-EOPAttributedMessage: 0 X-MS-Exchange-Transport-CrossTenantHeadersStripped: QB1PEPF00004E0F.CANPRD01.PROD.OUTLOOK.COM X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: a16ebd93-f79f-413a-fbc9-08dcf863b1c6 X-Microsoft-Antispam: BCL:0; ARA:13230040|36860700013|35042699022|82310400026|376014|1800799024|8096899003; X-Microsoft-Antispam-Message-Info: =?utf-8?q?gNh21/0iHu5/LXKGuVgg8Rh0tVQApEZ?= =?utf-8?q?NERMrQnUDaGJS768sYJA9RTnwpz0j0DUloeHLT0mqgEAAIqwJDX6msQee6iXPS0dl?= =?utf-8?q?V/rJDr4pzm6Z44XBVqkxmqtsm3w8rOtCQ6hNQ80hgBZXrJ0O3Ik9QIwxUYybe4+7P?= =?utf-8?q?vyC1ZrYyzrolX6i0nDf7ycA9/9Lq/mGV/wx2anN9NM/G+VsEDwRdL3RM5kI5XzkEV?= =?utf-8?q?ZXC7FM+spcCJKoHnbvbfBFQBpt8ccsNtzpB/ITMHxcKDRXshdlVIozMtPv29qCPNH?= =?utf-8?q?DF4Iq9HWDS4wENOm0uQbIBp7i4/sFKUO0dBLnQvf0XjYVbfB1trskG9o9zUjOt+ZR?= =?utf-8?q?ZlkTKvWNeSZ7/zxiFd6Qd1+fHHH/uEAdmk6hoce1aSMqtU9mIpjsLbk+mW3UhW/a0?= =?utf-8?q?j/O192maHq1S5fDPsBGKMrpo/shqdjGhGX8IGi+FKaQ/OSXw4QpYLjY7LcNwMOOvc?= =?utf-8?q?IuQ8fWW8TvQggYAYL2JIXY8GmXDqN6CFkzCwv+n0R5/hzbRQjHx32FfIqO1l0rRbF?= =?utf-8?q?L3GK6CGLBxZzIS8ydckYegSVuSkGwUhgSPQm+KvFVnQ1W55siRArQby2ub/9lw8Uj?= =?utf-8?q?elkWBN0TAvMzvG2kXux6GcBbrfQ7omYStDcbLCpkxGo9tQqRgBN7FSSQGudc5fKhY?= =?utf-8?q?ZbDG0ndpKdn01lLWX525Ny46T97tOUPpt3Jowp4tZQ0smqLqVJ1645usjuDa97lpo?= =?utf-8?q?aF/l4nOXzI648kklBxGk/BeCCcv3uez3YaXyPUsYCIxc8B4RO74vNe3U+xsdRnGa/?= =?utf-8?q?BLuYmFhPCqciCxGNzCI9LZ2czqZQO+TcMQphzWt0tVoNZAlnsCDupIagT/FWh7XgK?= =?utf-8?q?KLmsM9g15WcqJX/V3CI24IyfMc/r05bBGpPJffyCVe/KyWSySYCB2i8B8f7BpmQPc?= =?utf-8?q?rJayClyTXr2zrQ36qtUb64OJEJZhP7SoaMLZCEo+vmaIBXY9GxkG0ir/IoXw7SSzW?= =?utf-8?q?H67nsB1Cb+ygAdbJA/D5S+082ZSJwWN2AfOpBg2JJQ5qasMVjGKIRtSSKp16uxS28?= =?utf-8?q?f8iLy+7rTDqDs6KM3JN+SG8kRaqBXpuqCbjXsIk5/4xBkHieL09mbkvi4bR8J3K6Q?= =?utf-8?q?qrTy0UcrfxK1nAGSIfmdr7nJd44HZ9EwFzI3mNLw0ezSSZZEaqz8At93jGlQdgAyX?= =?utf-8?q?R7qGMtAVIVckBBeAkZUKWVz+f6lXBjU05qXU9qmWV8rC2DHlEM1aL1m9C24ufPKtO?= =?utf-8?q?caXJ6fIGv0sJDknAcOwhobVAfIPBag/m44JQrS4J8NmMQTSMUBj9AtGmusXCrcseX?= =?utf-8?q?QnI9sg821YXLsoLqthBwnQMdHf3elp+gEyNgGicZrIMGWRRWZne8JFRs=3D?= X-Forefront-Antispam-Report: CIP:51.81.11.26; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:vin2-relay.sendergen.com; PTR:vin2-relay.sendergen.com; CAT:NONE; SFS:(13230040)(36860700013)(35042699022)(82310400026)(376014)(1800799024)(8096899003); DIR:OUT; SFP:1102; X-OriginatorOrg: haivision.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Oct 2024 21:50:28.1287 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 9c9b3052-4dcd-4677-df18-08dcf863b3d3 X-MS-Exchange-CrossTenant-Id: a88156c4-f3f7-4104-8fad-43b93f27493d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=a88156c4-f3f7-4104-8fad-43b93f27493d; Ip=[51.81.11.26]; Helo=[vin2-relay.sendergen.com] X-MS-Exchange-CrossTenant-AuthSource: QB1PEPF00004E0F.CANPRD01.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: YQBPR0101MB9904 X-Content-Filtered-By: Mailman/MimeDel 2.1.29 Subject: [FFmpeg-devel] [PATCH] Patch to add interlaced HEVC decoding to HEVCDEC X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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" X-TUID: lVyJn1TzOZ7S From 349d0751a6aee1f7380a379d207311c161451a83 Mon Sep 17 00:00:00 2001 From: Jose Santiago Date: Tue, 29 Oct 2024 15:58:30 -0500 Subject: [PATCH] Patch to add interlaced HEVC decoding to HEVCDEC --- src/libavcodec/hevc/hevcdec.c |  24 +- src/libavcodec/hevc/hevcdec.h |  13 ++ src/libavcodec/hevc/refs.c | 415 +++++++++++++++++++++++++++++++++- src/libavcodec/hevc/sei.c |  16 +- src/libavcodec/hevc/sei.h | 129 ++++++++++- 5 files changed, 572 insertions(+), 25 deletions(-) +} + + typedef struct HEVCSEIPictureHash { uint8_t md5[3][16]; uint8_t is_md5; -- 2.46.1 Jose Santiago Senior Architect 847-362-6800 ext 7411 diff --git a/src/libavcodec/hevc/hevcdec.c b/src/libavcodec/hevc/hevcdec.c index 0dc24f82f8..0c75ff6293 100644 --- a/src/libavcodec/hevc/hevcdec.c +++ b/src/libavcodec/hevc/hevcdec.c @@ -358,7 +358,18 @@ static void export_stream_params(HEVCContext *s, const HEVCSPS *sps) avctx->profile = sps->ptl.general_ptl.profile_idc; avctx->level = sps->ptl.general_ptl.level_idc; - ff_set_sar(avctx, sps->vui.common.sar); + // There are some streams in the wild that were encode field pitcures + // and set double height aspect ratio so that some players that do not + // support interlaced HEVC display the field pictures with double height. + // Since we are now combining the field pictures into a single interlaced + // frame, fix the sample aspect ratio to restore the correct shape for the + // reconstructed interlaced frames. + if (ff_hevc_sei_pict_struct_is_field_picture(s->sei.picture_timing.picture_struct) && + sps->vui.common.sar.num == 1 && sps->vui.common.sar.den == 2) { + ff_set_sar(avctx, (AVRational){1, 1}); + } else { + ff_set_sar(avctx, sps->vui.common.sar); + } if (sps->vui.common.video_signal_type_present_flag) avctx->color_range = sps->vui.common.video_full_range_flag ? AVCOL_RANGE_JPEG @@ -3785,6 +3796,7 @@ static int hevc_ref_frame(HEVCFrame *dst, const HEVCFrame *src) dst->rpl = ff_refstruct_ref(src->rpl); dst->nb_rpl_elems = src->nb_rpl_elems; + dst->sei_pic_struct = src->sei_pic_struct; dst->poc = src->poc; dst->ctb_count = src->ctb_count; dst->flags = src->flags; @@ -3814,6 +3826,8 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx) av_freep(&s->md5_ctx); + ff_hevc_output_frame_construction_ctx_unref(s); + ff_container_fifo_free(&s->output_fifo); for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) { @@ -3854,6 +3868,11 @@ static av_cold int hevc_init_context(AVCodecContext *avctx) s->local_ctx[0].logctx = avctx; s->local_ctx[0].common_cabac_state = &s->cabac; + if (ff_hevc_output_frame_construction_ctx_alloc(s) != 0 || + !s->output_frame_construction_ctx) { + return AVERROR(ENOMEM); + } + s->output_fifo = ff_container_fifo_alloc_avframe(0); if (!s->output_fifo) return AVERROR(ENOMEM); @@ -3908,6 +3927,8 @@ static int hevc_update_thread_context(AVCodecContext *dst, } } + ff_hevc_output_frame_construction_ctx_replace(s, s0); + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.vps_list); i++) ff_refstruct_replace(&s->ps.vps_list[i], s0->ps.vps_list[i]); @@ -3971,6 +3992,7 @@ static int hevc_update_thread_context(AVCodecContext *dst, s->sei.common.content_light = s0->sei.common.content_light; s->sei.common.aom_film_grain = s0->sei.common.aom_film_grain; s->sei.tdrdi = s0->sei.tdrdi; + s->sei.picture_timing = s0->sei.picture_timing; return 0; } diff --git a/src/libavcodec/hevc/hevcdec.h b/src/libavcodec/hevc/hevcdec.h index 6ba2ca3887..6a39f921b9 100644 --- a/src/libavcodec/hevc/hevcdec.h +++ b/src/libavcodec/hevc/hevcdec.h @@ -369,6 +369,10 @@ typedef struct HEVCFrame { int ctb_count; int poc; + // SEI Picture Timing Picture Structure Type. + // HEVC_SEI_PicStructType. + int sei_pic_struct; + const HEVCPPS *pps; ///< RefStruct reference RefPicListTab *rpl; ///< RefStruct reference int nb_rpl_elems; @@ -484,6 +488,8 @@ typedef struct HEVCLayerContext { struct FFRefStructPool *rpl_tab_pool; } HEVCLayerContext; +struct HEVCOutputFrameConstructionContext; + typedef struct HEVCContext { const AVClass *c; // needed by private avoptions AVCodecContext *avctx; @@ -502,6 +508,9 @@ typedef struct HEVCContext { /** 1 if the independent slice segment header was successfully parsed */ uint8_t slice_initialized; + // Interlaced Frame Construction Context. + struct HEVCOutputFrameConstructionContext *output_frame_construction_ctx; ///< RefStruct reference + struct ContainerFifo *output_fifo; HEVCParamSets ps; @@ -661,6 +670,10 @@ static av_always_inline int ff_hevc_nal_is_nonref(enum HEVCNALUnitType type) return 0; } +int ff_hevc_output_frame_construction_ctx_alloc(HEVCContext *s); +void ff_hevc_output_frame_construction_ctx_replace(HEVCContext *dst, HEVCContext *src); +void ff_hevc_output_frame_construction_ctx_unref(HEVCContext *s); + /** * Find frames in the DPB that are ready for output and either write them to the * output FIFO or drop their output flag, depending on the value of discard. diff --git a/src/libavcodec/hevc/refs.c b/src/libavcodec/hevc/refs.c index 6ba667e9f5..4b8721ba68 100644 --- a/src/libavcodec/hevc/refs.c +++ b/src/libavcodec/hevc/refs.c @@ -20,9 +20,13 @@ * 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/avassert.h" #include "libavutil/mem.h" +#include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" +#include "libavutil/thread.h" +#include "libavutil/timestamp.h" #include "container_fifo.h" #include "decode.h" @@ -31,6 +35,100 @@ #include "progressframe.h" #include "refstruct.h" +typedef struct HEVCOutputFrameConstructionContext { + // TODO: Determine if this is necessary. + // Thread Data Access/Synchronization. + AVMutex mutex; + + // Decoder Output Tracking. + uint64_t dpb_counter; + int dpb_poc; + uint64_t dpb_poc_ooorder_counter; + + // Collect the First Field. + int have_first_field; + int first_field_poc; + int first_field_sei_pic_struct; + AVFrame *first_field; + + uint64_t orphaned_field_pictures; + + // Reconstructed Interlaced Frames From Field Pictures for Output. + AVFrame *constructed_frame; + + // Output Frame Counter. + uint64_t output_counter; + int output_poc; + uint64_t output_poc_ooorder_counter; +} HEVCOutputFrameConstructionContext; + +static void hevc_output_frame_construction_ctx_free(FFRefStructOpaque opaque, void *obj) +{ + HEVCOutputFrameConstructionContext * ctx = (HEVCOutputFrameConstructionContext *)obj; + + if (!ctx) + return; + + av_frame_free(&ctx->first_field); + av_frame_free(&ctx->constructed_frame); + av_assert0(ff_mutex_destroy(&ctx->mutex) == 0); +} + +int ff_hevc_output_frame_construction_ctx_alloc(HEVCContext *s) +{ + if (s->output_frame_construction_ctx) { + av_log(s->avctx, AV_LOG_ERROR, + "s->output_frame_construction_ctx is already set.\n"); + return AVERROR_INVALIDDATA; + } + + s->output_frame_construction_ctx = + ff_refstruct_alloc_ext(sizeof(*(s->output_frame_construction_ctx)), + 0, NULL, hevc_output_frame_construction_ctx_free); + if (!s->output_frame_construction_ctx) + return AVERROR(ENOMEM); + + av_assert0(ff_mutex_init(&s->output_frame_construction_ctx->mutex, NULL) == 0); + + return 0; +} + +void ff_hevc_output_frame_construction_ctx_replace(HEVCContext *dst, HEVCContext *src) +{ + ff_refstruct_replace(&dst->output_frame_construction_ctx, + src->output_frame_construction_ctx); +} + +void ff_hevc_output_frame_construction_ctx_unref(HEVCContext *s) +{ + if (s->output_frame_construction_ctx && + ff_refstruct_exclusive(s->output_frame_construction_ctx)) { + + HEVCOutputFrameConstructionContext * ctx = s->output_frame_construction_ctx; + + ff_mutex_lock(&ctx->mutex); + + if (ctx->dpb_counter) { + av_log(s->avctx, AV_LOG_ERROR, + "[HEVCOutputFrameConstructionContext @ 0x%p]:\n" + "      DPB: Counter=%" PRIu64 " POCOutOfOrder=%" PRIu64 " Orphaned=%" PRIu64 "\n" + "      Output: Counter=%" PRIu64 " POCOutOfOrder=%" PRIu64 "\n" + "%s", + ctx, + ctx->dpb_counter, + ctx->dpb_poc_ooorder_counter, + ctx->orphaned_field_pictures, + ctx->output_counter, + ctx->output_poc_ooorder_counter, + ""); + } + + ff_mutex_unlock(&ctx->mutex); + } + + ff_refstruct_unref(&s->output_frame_construction_ctx); +} + void ff_hevc_unref_frame(HEVCFrame *frame, int flags) { frame->flags &= ~flags; @@ -151,11 +249,15 @@ static HEVCFrame *alloc_frame(HEVCContext *s, HEVCLayerContext *l) for (j = 0; j < frame->ctb_count; j++) frame->rpl_tab[j] = frame->rpl; - if (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) - frame->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; - if ((s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || - (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD)) + frame->sei_pic_struct = s->sei.picture_timing.picture_struct; + if (ff_hevc_sei_pic_struct_is_interlaced(frame->sei_pic_struct)) { frame->f->flags |= AV_FRAME_FLAG_INTERLACED; + if (ff_hevc_sei_pic_struct_is_tff(frame->sei_pic_struct)) + frame->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + if (frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF || + frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF) + frame->f->repeat_pict = 1; + } ret = ff_hwaccel_frame_priv_alloc(s->avctx, &frame->hwaccel_picture_private); if (ret < 0) @@ -223,6 +325,81 @@ static void unref_missing_refs(HEVCLayerContext *l) } } +static void copy_field2(AVFrame *_dst, const AVFrame *_src) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_src->format); + int i, j, planes_nb = 0; + for (i = 0; i < desc->nb_components; i++) + planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1); + for (i = 0; i < planes_nb; i++) { + int h = _src->height; + uint8_t *dst = _dst->data[i] + (_dst->linesize[i] / 2); + uint8_t *src = _src->data[i]; + if (i == 1 || i == 2) { + h = FF_CEIL_RSHIFT(_src->height, desc->log2_chroma_h); + } + for (j = 0; j < h; j++) { + memcpy(dst, src, _src->linesize[i]); + dst += _dst->linesize[i]; + src += _src->linesize[i]; + } + } +} + +static int interlaced_frame_from_fields(AVFrame *dst, + const AVFrame *field1, + const AVFrame *field2) +{ + int i, ret = 0; + + av_frame_unref(dst); + + dst->format = field1->format; + dst->width = field1->width; + dst->height = field1->height * 2; + dst->nb_samples = field1->nb_samples; + ret = av_channel_layout_copy(&dst->ch_layout, &field1->ch_layout); + if (ret < 0) + return ret; + + ret = av_frame_copy_props(dst, field1); + if (ret < 0) + return ret; + if (field2->duration > 0 && field2->duration != AV_NOPTS_VALUE) + dst->duration = field2->duration * 2; + else if (field2->duration > 0 && field2->duration != AV_NOPTS_VALUE) + dst->duration = field2->duration * 2; + + for (i = 0; i < field2->nb_side_data; i++) { + const AVFrameSideData *sd_src = field2->side_data[i]; + AVFrameSideData *sd_dst; + AVBufferRef *ref = av_buffer_ref(sd_src->buf); + sd_dst = av_frame_new_side_data_from_buf(dst, sd_src->type, ref); + if (!sd_dst) { + av_buffer_unref(&ref); + return AVERROR(ENOMEM); + } + } + + for (i = 0; i < AV_NUM_DATA_POINTERS; i++) + dst->linesize[i] = field1->linesize[i]*2; + + ret = av_frame_get_buffer(dst, 0); + if (ret < 0) + return ret; + + ret = av_frame_copy(dst, field1); + if (ret < 0) + av_frame_unref(dst); + + copy_field2(dst, field2); + + for (i = 0; i < AV_NUM_DATA_POINTERS; i++) + dst->linesize[i] = field1->linesize[i]; + + return ret; +} + int ff_hevc_output_frames(HEVCContext *s, unsigned layers_active_decode, unsigned layers_active_output, unsigned max_output, unsigned max_dpb, int discard) @@ -265,10 +442,234 @@ int ff_hevc_output_frames(HEVCContext *s, AVFrame *f = frame->needs_fg ? frame->frame_grain : frame->f; int output = !discard && (layers_active_output & (1 << min_layer)); + if (frame->poc != s->poc) { + if (s->avctx->active_thread_type == FF_THREAD_FRAME) + { + // Wait for other thread to finish decoding this frame/field picture. + // Otherwise I have seen image corruption for some streams.. + av_log(s->avctx, AV_LOG_DEBUG, + "Waiting on Frame POC: %d.\n", + frame->poc); + ff_progress_frame_await(&frame->tf, INT_MAX); + } + } else { + // This is the Context currently decoding.. + // Skip it to ensure that this frame is completely decoded and finalized. + // This will allow the next context to process it + // Otherwise I have seen image corruption for some streams. + av_log(s->avctx, AV_LOG_DEBUG, + "Schedule Frame for Next Pass POC: %d.\n", + frame->poc); + return 0; + } + + av_assert0(s->output_frame_construction_ctx); + av_assert0(ff_mutex_lock(&s->output_frame_construction_ctx->mutex) == 0); + if (output) { - f->pkt_dts = s->pkt_dts; - ret = ff_container_fifo_write(s->output_fifo, f); + const int dpb_poc = frame->poc; + const int dpb_sei_pic_struct = frame->sei_pic_struct; + AVFrame *output = f; + int output_poc = dpb_poc; + int output_sei_pic_struct = dpb_sei_pic_struct; + + s->output_frame_construction_ctx->dpb_counter++; + if (s->output_frame_construction_ctx->dpb_counter > 1 && + dpb_poc < s->output_frame_construction_ctx->dpb_poc && + dpb_poc > 0) { + s->output_frame_construction_ctx->dpb_poc_ooorder_counter++; + av_log(s->avctx, AV_LOG_ERROR, + "DPB POC Out of Order POC %d < PrevPOC %d " + ": Counter=%" PRIu64 " OORCounter=%" PRIu64 ".\n", + dpb_poc, + s->output_frame_construction_ctx->dpb_poc, + s->output_frame_construction_ctx->dpb_counter, + s->output_frame_construction_ctx->dpb_poc_ooorder_counter); + } + s->output_frame_construction_ctx->dpb_poc = dpb_poc; + + if (ff_hevc_sei_pict_struct_is_field_picture(dpb_sei_pic_struct)) { + const int have_first_field = s->output_frame_construction_ctx->have_first_field; + const int is_first_field = + (ff_hevc_sei_pic_struct_is_tff(dpb_sei_pic_struct) && + ff_hevc_sei_pic_struct_is_tf(dpb_sei_pic_struct)) || + (ff_hevc_sei_pic_struct_is_bff(dpb_sei_pic_struct) && + ff_hevc_sei_pic_struct_is_bf(dpb_sei_pic_struct)) || + (!s->output_frame_construction_ctx->have_first_field && + (dpb_poc % 2) == 0) || + (s->output_frame_construction_ctx->have_first_field && + s->output_frame_construction_ctx->first_field_sei_pic_struct == dpb_sei_pic_struct && + (dpb_poc % 2) == 0 && + dpb_poc > s->output_frame_construction_ctx->first_field_poc); + + output = NULL; + + if (!s->output_frame_construction_ctx->first_field) + { + s->output_frame_construction_ctx->first_field = av_frame_alloc(); + if (!s->output_frame_construction_ctx->first_field) { + av_log(s->avctx, AV_LOG_ERROR, "AVERROR(ENOMEM)"); + ret = AVERROR(ENOMEM); + goto unref_frame_and_check_ret; + } + } + if (!s->output_frame_construction_ctx->constructed_frame) { + s->output_frame_construction_ctx->constructed_frame = av_frame_alloc(); + if (!s->output_frame_construction_ctx->constructed_frame) { + av_log(s->avctx, AV_LOG_ERROR, "AVERROR(ENOMEM)"); + ret = AVERROR(ENOMEM); + goto unref_frame_and_check_ret; + } + } + + if (is_first_field) { + // This is a first field picture. + av_log(s->avctx, AV_LOG_DEBUG, + "Found first field picture POC %d.\n", + dpb_poc); + if (s->output_frame_construction_ctx->have_first_field) { + // We were waiting for a second field, but got another frist + // field instead. + av_log(s->avctx, AV_LOG_ERROR, + "Discarded Orphaned First Field with POC %d.\n", + s->output_frame_construction_ctx->first_field_poc); + } + s->output_frame_construction_ctx->have_first_field = 1; + s->output_frame_construction_ctx->first_field_sei_pic_struct = dpb_sei_pic_struct; + s->output_frame_construction_ctx->first_field_poc = dpb_poc; + ret = av_frame_ref(s->output_frame_construction_ctx->first_field, f); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Failure updating first Field picture POC %d.\n", + dpb_poc); + s->output_frame_construction_ctx->have_first_field = 0; + s->output_frame_construction_ctx->orphaned_field_pictures++; + goto unref_frame_and_check_ret; + } + } else if (have_first_field) { + // We Found the next field. + if (f->width == s->output_frame_construction_ctx->first_field->width && + f->height == s->output_frame_construction_ctx->first_field->height) { + // Combine the top and bottom fields into one frame for output. + AVFrame *constructed_frame = s->output_frame_construction_ctx->constructed_frame; + AVFrame *top_field; + AVFrame *bottom_field; + int tfPoc, bfPoc; + if (ff_hevc_sei_pic_struct_is_tf(dpb_sei_pic_struct)) { + top_field = f; + tfPoc = dpb_poc; + bottom_field = s->output_frame_construction_ctx->first_field; + bfPoc = s->output_frame_construction_ctx->first_field_poc; + } else { + top_field = s->output_frame_construction_ctx->first_field; + tfPoc = s->output_frame_construction_ctx->first_field_poc; + bottom_field = f; + bfPoc = dpb_poc; + } + ret = interlaced_frame_from_fields(constructed_frame, top_field, bottom_field); + if (ret >= 0) { + output = constructed_frame; + output_poc = s->output_frame_construction_ctx->first_field_poc; + output_sei_pic_struct = s->output_frame_construction_ctx->first_field_sei_pic_struct; + output->flags |= AV_FRAME_FLAG_INTERLACED; + if (!ff_hevc_sei_pic_struct_is_bf(output_sei_pic_struct)) { + output->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + } else { + output->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + } + output->repeat_pict = (output_sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF || + output_sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF) ? 1: 0; + } else { + av_log(s->avctx, AV_LOG_ERROR, + "Interlaced Frame Construction Failure POCs: %d %d.\n", + tfPoc, bfPoc); + s->output_frame_construction_ctx->orphaned_field_pictures += 2; + } + } else if ((dpb_poc % 2) == 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Discarded orphaned first field pictures POC: %d.\n", + s->output_frame_construction_ctx->first_field_poc); + s->output_frame_construction_ctx->orphaned_field_pictures++; + // This may be the next first field. + s->output_frame_construction_ctx->have_first_field = 0; + av_assert0(ff_mutex_unlock(&s->output_frame_construction_ctx->mutex) == 0); + continue; + } else { + av_log(s->avctx, AV_LOG_ERROR, + "Discarded mismatched field pictures POCs: %d %d.\n", + s->output_frame_construction_ctx->first_field_poc, + dpb_poc); + s->output_frame_construction_ctx->orphaned_field_pictures++; + } + // Find the next first field. + s->output_frame_construction_ctx->have_first_field = 0; + } else { + // We have a second field without a first field. + av_log(s->avctx, AV_LOG_ERROR, + "Discarded orphaned second field picture with POC %d.\n", + dpb_poc); + s->output_frame_construction_ctx->orphaned_field_pictures++; + } + } else if (s->output_frame_construction_ctx->have_first_field) { + av_log(s->avctx, AV_LOG_ERROR, + "Discarded orphaned first field pictures POC: %d.\n", + s->output_frame_construction_ctx->first_field_poc); + s->output_frame_construction_ctx->orphaned_field_pictures++; + // Find the next first field. + s->output_frame_construction_ctx->have_first_field = 0; + } + + if (output) { + output->pkt_dts = s->pkt_dts; + + //av_log(s->avctx, AV_LOG_ERROR, + av_log(s->avctx, AV_LOG_DEBUG, + "s=0x%" PRIx64 " s->avctx=0x%" PRIx64 "\n" + "  ====Output: FrameType:%s\n" + "  === POC=%d PKTDTS=%s PTS=%s Duration=%s\n" + "  === SEIPic=%d Interlaced=%s TFF=%s PictType='%c' Key=%s\n" + "  === WxH=%dx%d SAR=%dx%d\n" + "%s", + (uint64_t)s, (uint64_t)s->avctx, + (output->flags & AV_FRAME_FLAG_INTERLACED) ? "Interlaced" : "Progressive", + output_poc, + av_ts2str(output->pkt_dts), + av_ts2str(output->pts), + av_ts2str(output->duration), + output_sei_pic_struct, + (output->flags & AV_FRAME_FLAG_INTERLACED) ? "Yes" : "No", + (output->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? "Yes" : "No", + av_get_picture_type_char(output->pict_type), + (output->flags & AV_FRAME_FLAG_KEY) ? "Yes" : "No", + output->width, output->height, + (int)output->sample_aspect_ratio.num, + (int)output->sample_aspect_ratio.den, + ""); + + s->output_frame_construction_ctx->output_counter++; + if (output_poc != dpb_poc && + s->output_frame_construction_ctx->output_counter > 1 && + output_poc < s->output_frame_construction_ctx->output_poc && + output_poc > 0) { + s->output_frame_construction_ctx->output_poc_ooorder_counter++; + av_log(s->avctx, AV_LOG_ERROR, + "Output POC Out of Order POC %d < PrevPOC %d " + ": Counter=%" PRIu64 " OORCounter=%" PRIu64 ".\n", + output_poc, + s->output_frame_construction_ctx->output_poc, + s->output_frame_construction_ctx->output_counter, + s->output_frame_construction_ctx->output_poc_ooorder_counter); + } + s->output_frame_construction_ctx->output_poc = output_poc; + + ret = ff_container_fifo_write(s->output_fifo, output); + } } + +unref_frame_and_check_ret: + + av_assert0(ff_mutex_unlock(&s->output_frame_construction_ctx->mutex) == 0); + ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT); if (ret < 0) return ret; diff --git a/src/libavcodec/hevc/sei.c b/src/libavcodec/hevc/sei.c index e11a33773c..50b669c34b 100644 --- a/src/libavcodec/hevc/sei.c +++ b/src/libavcodec/hevc/sei.c @@ -59,21 +59,7 @@ static int decode_nal_sei_pic_timing(HEVCSEI *s, GetBitContext *gb, return AVERROR_INVALIDDATA; if (sps->vui.frame_field_info_present_flag) { - int pic_struct = get_bits(gb, 4); - h->picture_struct = AV_PICTURE_STRUCTURE_UNKNOWN; - if (pic_struct == 2 || pic_struct == 10 || pic_struct == 12) { - av_log(logctx, AV_LOG_DEBUG, "BOTTOM Field\n"); - h->picture_struct = AV_PICTURE_STRUCTURE_BOTTOM_FIELD; - } else if (pic_struct == 1 || pic_struct == 9 || pic_struct == 11) { - av_log(logctx, AV_LOG_DEBUG, "TOP Field\n"); - h->picture_struct = AV_PICTURE_STRUCTURE_TOP_FIELD; - } else if (pic_struct == 7) { - av_log(logctx, AV_LOG_DEBUG, "Frame/Field Doubling\n"); - h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING; - } else if (pic_struct == 8) { - av_log(logctx, AV_LOG_DEBUG, "Frame/Field Tripling\n"); - h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING; - } + h->picture_struct = get_bits(gb, 4); } return 0; diff --git a/src/libavcodec/hevc/sei.h b/src/libavcodec/hevc/sei.h index 806540fac6..2eacd77bd5 100644 --- a/src/libavcodec/hevc/sei.h +++ b/src/libavcodec/hevc/sei.h @@ -33,10 +33,135 @@ typedef enum { - HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, - HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 + // SEI Picture Timing Picture Structure. + // From the ITU=T H.265 Standards Document v3 (04/2015): + // Table D.2: Interpretation of pic_struct: + // When present, pic_struct is constrained to use one of the following: + // - all pictures in CSV are one of: 0, 7 or 8. + // - all pictures in CSV are one of: 1, 2, 9, 10, 11, or 12.. + // - all pictures in CSV are one of: 3, 4, 5 or 6... + + // progressive frame. + HEVC_SEI_PIC_STRUCT_FRAME_PROGRESSIVE = 0, + + // top field. + HEVC_SEI_PIC_STRUCT_FIELD_TOP = 1, + // bottom field. + HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM = 2, + + // top field, bottom field, in that order. Top Field First. + HEVC_SEI_PIC_STRUCT_FRAME_TFBF = 3, + // bottom Field, top field, in that order. Bottom Field First. + HEVC_SEI_PIC_STRUCT_FRAME_BFTF = 4, + + // top field, bottom field, top field repeated, Top Field First. + HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF = 5, + // bottom field, top field, bottom field repeated, Bottom Field First. + HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF = 6, + + // frame doubling. + HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + // frame trippling. + HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8, + + // top field paired with previous bottom field. Bottom Field First. + HEVC_SEI_PIC_STRUCT_FIELD_TFPBF = 9, + // bottom field paired with previous top field. Top Field First. + HEVC_SEI_PIC_STRUCT_FIELD_BFPTF = 10, + + // top field paired with next bottom field. Top Field First. + HEVC_SEI_PIC_STRUCT_FIELD_TFNBF = 11, + // bottom field paired with next top field. Bottom Field First. + HEVC_SEI_PIC_STRUCT_FIELD_BFNTF = 12, } HEVC_SEI_PicStructType; +// Returns 1 - when type is interlaced, 0 - othewise. +static inline int ff_hevc_sei_pic_struct_is_interlaced(HEVC_SEI_PicStructType type) +{ + switch (type) { + case HEVC_SEI_PIC_STRUCT_FIELD_TOP: + case HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM: + case HEVC_SEI_PIC_STRUCT_FRAME_TFBF: + case HEVC_SEI_PIC_STRUCT_FRAME_BFTF: + case HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF: + case HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF: + case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF: + case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF: + case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF: + case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF: + return 1; + default: + return 0; + } +} + +// Returns 1 - when type is top field first, 0 - othewise. +static inline int ff_hevc_sei_pic_struct_is_tff(HEVC_SEI_PicStructType type) +{ + switch (type) { + case HEVC_SEI_PIC_STRUCT_FRAME_TFBF: + case HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF: + case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF: + case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF: + return 1; + default: + return 0; + } +} + +// Returns 1 - when type is bottom field first, 0 - othewise. +static inline int ff_hevc_sei_pic_struct_is_bff(HEVC_SEI_PicStructType type) +{ + switch (type) { + case HEVC_SEI_PIC_STRUCT_FRAME_BFTF: + case HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF: + case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF: + case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF: + return 1; + default: + return 0; + } +} + +// Returns 1 - when type is top field, 0 - othewise. +static inline int ff_hevc_sei_pic_struct_is_tf(HEVC_SEI_PicStructType type) +{ + switch (type) { + case HEVC_SEI_PIC_STRUCT_FIELD_TOP: + case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF: + case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF: + return 1; + default: + return 0; + } +} + +// Returns 1 - when type is bottom field, 0 - othewise. +static inline int ff_hevc_sei_pic_struct_is_bf(HEVC_SEI_PicStructType type) +{ + switch (type) { + case HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM: + case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF: + case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF: + return 1; + default: + return 0; + } +} + +// Returns 1 - when type is a field picture, 0 - othewise. +static inline int ff_hevc_sei_pict_struct_is_field_picture(HEVC_SEI_PicStructType type) +{ + return (ff_hevc_sei_pic_struct_is_tf(type) || ff_hevc_sei_pic_struct_is_bf(type)) ? 1 : 0; +} + +// Returns 1 - when type is a frame picture, 0 - othewise. +static inline int ff_hevc_sei_pict_struct_is_frame_picture(HEVC_SEI_PicStructType type) +{ + return ff_hevc_sei_pict_struct_is_field_picture(type) ? 0 : 1;