From patchwork Mon Sep 25 09:41:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Riedl X-Patchwork-Id: 43889 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2a18:b0:15d:8365:d4b8 with SMTP id e24csp1104277pzh; Mon, 25 Sep 2023 02:41:48 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFRgvX3ihrAXlAMVXD5O+dzHGGEXeA/aROvT6qArQryOKF8m+urhfUylW0WAMJCsQ0SFlbh X-Received: by 2002:a05:6512:6cc:b0:500:7a21:3e78 with SMTP id u12-20020a05651206cc00b005007a213e78mr6122751lff.55.1695634908508; Mon, 25 Sep 2023 02:41:48 -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 y22-20020aa7ccd6000000b0052e1f352fd9si8187936edt.650.2023.09.25.02.41.47; Mon, 25 Sep 2023 02:41:48 -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=@nativewaves.onmicrosoft.com header.s=selector2-nativewaves-onmicrosoft-com header.b="HgXCs/9H"; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1DECD68C929; Mon, 25 Sep 2023 12:41:44 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from EUR02-DB5-obe.outbound.protection.outlook.com (unknown [40.107.249.61]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 24D2A68C5CE for ; Mon, 25 Sep 2023 12:41:36 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=mwF9Yl3nECc89ng/egxdacbEu/Q8VDzRN02vcF3z1tYzKi6lxecv/FMMWlO9AUCzUOG0bCqTXzr0OLIXrKdZep6rv21i5RH2YjwYYPSH60i3YKJZ6ivVyp7HtyQ4opuzOw+sh4UPVm9KdcaoP4Hw/z6ACm0QLMHPlWsq5buBj+0c2D0WkoxHlz4AEObJZi4Dl9l6wHgpSqKLf5k6Ee2B/MRkf3tbBH9ketGGpXiL+2tLmitfO0lNML7+5qb68CLb8m4AKCbYJIWZIHBQclb14bHYyxwQ+0RGUi8Pd5jp0Y8DiK4WFWNirHO4jupxwJTJX69ezGe7niNgSvNRRQ3m1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=u9snkxGyIbvqD5ZfCo4FBeHhVqd+iQG6m7wpDtLIH+A=; b=eABNargXJxfCDzwMS4ttRF/3VNmChwph7N81BhjnDNfqKcA3wh2JUPXTI/kaIU7ZsUN9/vSY+VaXlKA9AEIPO3lq1rbZrDpJ7ybpHo8xhgYevPpXvT94OHF2wNuQsJCazpeAuztRzjql23sIxgV9477u3D1d5BU84BRi/PDR/MZ4RXmf9bKBVXc+etV15hXbY6pzLy42ReZK4taO5b08QPhrFxaZa7LkMfdw1zqsBX/IxWz0nQZmFaVAiaNmacZItoLC0kzz3wmCsitfO0ryYqOTr0RY/r+DfE8ReFhMjw6LTcPAKKHC6fDqd9hyI0gCJp7tdOiUZVuMOPDBgGOsxA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nativewaves.com; dmarc=pass action=none header.from=nativewaves.com; dkim=pass header.d=nativewaves.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nativewaves.onmicrosoft.com; s=selector2-nativewaves-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=u9snkxGyIbvqD5ZfCo4FBeHhVqd+iQG6m7wpDtLIH+A=; b=HgXCs/9Hf/+qY1J2dIJYBNON5EudJxUdnUSkFgDBfSfNUeKkIio9SJEv9PdzfplD83z+QjHQX7tY5WTMQKG10gfOu0Kic0ZZdwTHcXYJ9I213f4N3PPINJJYFIhu/TuTCHn78KlOsfQ0sWsLDQWrSAjl6KN3O4LtuVVqcI0I2uo= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nativewaves.com; Received: from PA4PR03MB7167.eurprd03.prod.outlook.com (2603:10a6:102:10a::8) by DB3PR0302MB9236.eurprd03.prod.outlook.com (2603:10a6:10:429::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6813.28; Mon, 25 Sep 2023 09:41:33 +0000 Received: from PA4PR03MB7167.eurprd03.prod.outlook.com ([fe80::d525:3ec1:f93a:9876]) by PA4PR03MB7167.eurprd03.prod.outlook.com ([fe80::d525:3ec1:f93a:9876%3]) with mapi id 15.20.6813.027; Mon, 25 Sep 2023 09:41:33 +0000 Message-ID: <31ea1fd8-1ee6-446b-a751-cbba60bd4e0d@nativewaves.com> Date: Mon, 25 Sep 2023 11:41:31 +0200 User-Agent: Mozilla Thunderbird From: Michael Riedl To: ffmpeg-devel@ffmpeg.org Content-Language: en-US X-ClientProxiedBy: FR0P281CA0211.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:ac::6) To PA4PR03MB7167.eurprd03.prod.outlook.com (2603:10a6:102:10a::8) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR03MB7167:EE_|DB3PR0302MB9236:EE_ X-MS-Office365-Filtering-Correlation-Id: 86831cdd-9e6d-495c-a1c2-08dbbdab9a2e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: hZipfMmqVL01xORzp3Z7MX33FvpBeTNBC6U5xPDkjjrar4tqAd7XsqoylPKJZuBoetrUwmVV3N9K+tT3gkH2TikIjwSMz15URsLFCPlNdVFNARgP5HbFMXp0m9WAQlpDwHgzWuSmfcgqRNIZHEqE+th0wcoSemgCKT8lJre5h/8rsZ7fVFml0I34Uvw07BTIcLnmmnfbvgyR/aBddgX+PBuXixB0Tbj2NEP2K+6pdlqOQzkKfzWgmt3rWrQMbQO1A5sUf/Z8TOv/ZiuqlcYYqxH8ZcPNJzoAqIFffT33h28/Lt6THf/DAO2sXlq+U+UUh0wIXmCPazZ1Glf+fic3n2f/taxtxvoU95QRqpVo8RSuLI2l8bnycMdEd0J9X5VT73V5BYFwjKYIwtHMSBaLEtiZDWWIZAtx8au5dyAm6KnGJJcTTrO22o5G+Wdmdv3ArRzlKPJvRv3k5JV4QTT15LHQYeEy17hzYnRXmsJu9GPFOKvfXWrwQOv/fvJQanmwTq4BeFpz5rekT/ZSMHBPQtSK4DVIf3NZIoO1QoP25AIUnWJTf/N/YNae8F0CAM7sl2drxXWRCdYEp8JHL1oCl/Yd+Q1Bx1uLFK22zN5bjzhaBfIgSParR3PzmnLTMBQFNkcVvDvFc3zlDw++mv9ioQ== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PA4PR03MB7167.eurprd03.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(376002)(366004)(136003)(39830400003)(396003)(346002)(230922051799003)(451199024)(1800799009)(186009)(66556008)(66476007)(5660300002)(66946007)(31686004)(41300700001)(6916009)(316002)(44832011)(8936002)(8676002)(2906002)(38100700002)(83380400001)(478600001)(31696002)(26005)(2616005)(86362001)(6506007)(6512007)(6486002)(36756003)(43740500002)(45980500001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?JhhWk1wpOFBWwd0mNLdw89uLBhEc?= =?utf-8?q?bY9JOP0LrJ9zY99CE9of7tgLp9Nrq/0M5hGczqw//ykLir7AfXDEp+LDJ3MvUYqQ6?= =?utf-8?q?GIxUX1Wf0cM1f6lByvcgQ3DzNJXLTcVEb00MH72vm9WGk92ao4NIYeIocow9e+I48?= =?utf-8?q?jpI4ENEMnpV6Qkzz/MiIUHqKridofa+ucpL9VOQpcvSSu6SiRckhw11OnDtn05Iev?= =?utf-8?q?KTrVgqQZMLoParQwg/Hy3tllzklEbo4s7ihrbxai3uAmG1kGh9w5CgjY9B8LomfKW?= =?utf-8?q?iNPgNJvwjg4h1y4NODI0JjikAw/pMdVvoslG6zUwhiY/41yEjWr7sgdU95eSDdXAa?= =?utf-8?q?go5qImTN/PMiuc1vA7Z3W2ixqrNN7FqmMjIApe/Q4VlTw+TfsxVGucO4mSNknOXkm?= =?utf-8?q?jbrelwLBph+92TxfgKtXHtLus/V2W1/XAMG41HP6ctT5QVyg8ppZg6INuF6TZhnoj?= =?utf-8?q?eYgeujFOeAcHZXqSo4+afS3t02eb97dvAScPfgFm3N47kycPiNjfDljGvIIsqBahT?= =?utf-8?q?+hHpBZNcXM8LI0ape15C9ZoYpH285attdM600Q/JyXinGaSKgU0mP9gv2pkfAMf5P?= =?utf-8?q?qnZRJgZobt1kITJYHMSXqQJMCOhHV4I2O1ECPZncrshcGtemTLrvZbqgBVuZEJRvj?= =?utf-8?q?0gRIpyqymgfDteuWtx3Or4//aqHU2ECiBE6XyUd9CFRhT/dIFP9HXhIA2yR1TH80x?= =?utf-8?q?PsVlsDzLhot9L5g4VrRqm1ZxZF8ReYlC3zHhsIHwU08YlDmsUc63brUoJGY/lQ/XB?= =?utf-8?q?gOTzI+FByJbvDZolcNIGsDhJp9u29RricXOmEjaf5y2sbuS4RD2WLxO/0x6mbm+CC?= =?utf-8?q?Cn1+BKDCbbHlk65BhT4deR+L4qr6WyVYMBRm/eXrrnG4QrWWQbvah9AqBMX+u/pxb?= =?utf-8?q?gEjiZi708YL0lRCywLhgPEU/dotYp6tkaYOaIkZc09/kRXaxvgh/nBqzrhs5yX5XV?= =?utf-8?q?U0qcjGLzsJ1PRhFMkCEmL/q5sOdC0YlTSRv9Dlt8mOClPeexo+blWzxt4VzhB1pv/?= =?utf-8?q?ZGwlZhYPYGWgcK+hVjzX7TjC1nr1hIYqdnJipUco9PO9BuaOK9gjyhrXbtGZ6tc5U?= =?utf-8?q?vK63mf3DgJ4IblEwH43/3hmcpYvT4o6tuLcZsbbE4oHhi6VBBfePI7bJyOPr+iYT5?= =?utf-8?q?WYceRRlJChzNXEAOYq5hV7e+wyPA5uZ0RS1tkOB6kV7ygQ5VCaIw/i5so1/fe5jIS?= =?utf-8?q?9skEPt+oQACYDmlgxGr3RbjwQYCN86CNJVaD6P15FZFhoJbMT49H1uJ+e2X9s1cpS?= =?utf-8?q?pn6IbEyl6zoqSbSNl83hRLUtPXoOEJ33kqfGwNif+27YIXWAUbbAQd7jQMJFk4N3J?= =?utf-8?q?1a5YGt5Zx/OBBS8+KBqg+xeACFBrOPMA94uj2WCZ6WbAEoAzbzlYmZC13U1sLdd26?= =?utf-8?q?aHkLup6fqgQXxGszn6KfNXAgczGZbzUCztIYoX+s4tgbkzxq5uOboQCZVnsUiFKlp?= =?utf-8?q?uOVALnfghm+AW1EEj3MDndC7Jt/rpohUZYfPBNbNxXWBCpZcZBrA7v/fy42JJK8qc?= =?utf-8?q?t7m5GK64oCRuzQ0HY5aGfAaCyATgJ+lQftopNrvDpqCX/8DryWFrUTI=3D?= X-OriginatorOrg: nativewaves.com X-MS-Exchange-CrossTenant-Network-Message-Id: 86831cdd-9e6d-495c-a1c2-08dbbdab9a2e X-MS-Exchange-CrossTenant-AuthSource: PA4PR03MB7167.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Sep 2023 09:41:32.8950 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: e4239718-b000-4513-8314-02ef46bd0276 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: tqLRB7wWgILBO6NegZafI+/y7y7tKf/muvh4R+JC7iAvnn6AFxV2Nl/CP7jCWgDvzDv/wsZSkz+PRxR8Y0wqBs0AitWsLNF/MX19N9gDP8E= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB3PR0302MB9236 Subject: [FFmpeg-devel] [PATCH v2 1/2] libavformat/http: expose actual Location header value 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: Q4QXOEa/9ZmD This is needed for muxers which need to access the Location header as transmitted when no redirection happen. Signed-off-by: Michael Riedl --- libavformat/http.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavformat/http.c b/libavformat/http.c index c0fe7c36d91..27f9c8e3000 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -166,6 +166,7 @@ static const AVOption options[] = { { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, "auth_type"}, { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E }, { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E }, + { "new_location", "The location header value of the data received", OFFSET(new_location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, { "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D }, { "end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D }, { "method", "Override the HTTP method or set the expected HTTP method from a client", OFFSET(method), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E }, From patchwork Mon Sep 25 09:41:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Riedl X-Patchwork-Id: 43890 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:2a18:b0:15d:8365:d4b8 with SMTP id e24csp1104345pzh; Mon, 25 Sep 2023 02:41:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE86G1f/vtBQGrT19K2RJlhw6FGX23TE5jGcrrb6V7INusVoq165NT0d+jYWhmQQbO1t2HN X-Received: by 2002:aa7:c402:0:b0:52a:943:9ab5 with SMTP id j2-20020aa7c402000000b0052a09439ab5mr4683244edq.31.1695634918449; Mon, 25 Sep 2023 02:41:58 -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 n21-20020a05640206d500b00530c3bee4dcsi8166090edy.23.2023.09.25.02.41.58; Mon, 25 Sep 2023 02:41:58 -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=@nativewaves.onmicrosoft.com header.s=selector2-nativewaves-onmicrosoft-com header.b=ARyF7Bxi; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3BB0E68C5CE; Mon, 25 Sep 2023 12:41:49 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from EUR02-DB5-obe.outbound.protection.outlook.com (unknown [40.107.249.61]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 40A2468C5CE for ; Mon, 25 Sep 2023 12:41:42 +0300 (EEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SEYBcr5p+TnvlS4N9N+nBJqItDfGgY7NivgL5vTK2p1W76zVBn1Ro/cKOaCRsdAy35cLG0ADa1FUqYH7SdDjoKmTcbS5qLjo/H5XoV4ptsBVAmtWjzdmTUP0tVklCdHAoIoLYOKJTH4JrEfRDCgeocr/Bw44PK1RK3cu+NYV+YzpQ38Va0siku3uhKL4FXPimor5YNWZwL7uOZ9Bqe8n5mDn2uFOmMhBGPzDuW2Ua3RX0oxZdUlKrDEeE//mBM9IAE4FvV4mTHziT0SggQWe3fMC71Z6iR6i+Q8h5gnqMUkHp1VGzoyviB+6Q8qWEJn5y4dAQECtnMPjnCZe2FttTQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=TW+Z7u71oZadQOWfoI5sQ3d544UAigZk6hxY3NeLSjs=; b=DgtkmNz7VNolR6zwQddwm6O59McrwPyZMl4fv9ljxkQS/pzeeTQk99PuWwtYClhWa2/kjxxoBcgMavmT+XoGlFKJIh23tgxZ3kXXck5SyVehmG1Iy4zzrhz/lfR1irliZS1wPN7z5A/4o4e8mqdd1hpdvfP7ilRfCMm3k3seoUDCEjewZVs1Og9Yfdoc3YECnZ1Nw8xfK7tfh8IDo0fTwTa/T9ClhaLlEm8q6Fz1hzA0SQG3cwlpUyLbNvbycy05QOP6mLwAUPFPy6urZbGpdyJmroIG5KyXolUirVE2hNBQAQEs4JPRPJV7B1WTWz47nBuCS2X9C6jXt9egmMNuWg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nativewaves.com; dmarc=pass action=none header.from=nativewaves.com; dkim=pass header.d=nativewaves.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nativewaves.onmicrosoft.com; s=selector2-nativewaves-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=TW+Z7u71oZadQOWfoI5sQ3d544UAigZk6hxY3NeLSjs=; b=ARyF7BxiK0Jz0t0bunqyx5Ws4LD53/3a7zBM6M6MG1FB3ayr9wOOrwOjhgfINoJSobdoo/pb5J+QqYTsaWgFPQzkyr0BlPTNKwoPo2/Io7vqfRGpqJefzxY53Xzvf0uQaYU48lmPgh12IZ3Hcc49G01F4jA63YyY013ldKT/tO8= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nativewaves.com; Received: from PA4PR03MB7167.eurprd03.prod.outlook.com (2603:10a6:102:10a::8) by DB3PR0302MB9236.eurprd03.prod.outlook.com (2603:10a6:10:429::11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6813.28; Mon, 25 Sep 2023 09:41:37 +0000 Received: from PA4PR03MB7167.eurprd03.prod.outlook.com ([fe80::d525:3ec1:f93a:9876]) by PA4PR03MB7167.eurprd03.prod.outlook.com ([fe80::d525:3ec1:f93a:9876%3]) with mapi id 15.20.6813.027; Mon, 25 Sep 2023 09:41:37 +0000 Message-ID: Date: Mon, 25 Sep 2023 11:41:37 +0200 User-Agent: Mozilla Thunderbird From: Michael Riedl To: ffmpeg-devel@ffmpeg.org Content-Language: en-US X-ClientProxiedBy: FR0P281CA0211.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:ac::6) To PA4PR03MB7167.eurprd03.prod.outlook.com (2603:10a6:102:10a::8) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR03MB7167:EE_|DB3PR0302MB9236:EE_ X-MS-Office365-Filtering-Correlation-Id: b8ca1e1c-9399-4d4c-e592-08dbbdab9d1f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: LWwumAZYf5Ij1ZBzfmG9A8H2q/BglPfqiu/28rgJGI6lQGUjOza8aK2O95J166nWQBO33ZXQSUDlSz8qLEXFIeZCxhXZ8xzj+NIDDQLDVARxlddXeDdiTKh9MF8WHaVWO7h6Bmk75wj7hWh6gGDa5O2g6b1YH+4ctl4jbV7Y9wLRbTXBT6wpvXZzxhOe7pu1Ijrh7QMZ2wSjXHSU37k7kKXbgjpxmzAIyQHXURzIO0YdeR3IesfILNSzqV4o4UPy/5XOXCmRKMyC08uoL4M7U7wU5ZZXJ65dsa6xvOmmeZnrlgLuSJI4n0FFf/91OkqOp1YRkfPILmpMbbC/fZzaKVAChMJ1W5jGJwQ//5Kwvjqec+uR3XmvDPctSoHJUmkkwhVPMqj6BLlU6EyZOijKgBebW5NQ7q9am14f0GWS0i1IFK9HjGdmiIlMn/BCu6RpNqO2aafI2QpaePahDlmUp6ucevGkdiTxWQpjix2fVk2FGfIQUgd0QGgWXgmj/rKnucjvLQmOBQWngtgokHXY1NrdYLH3uVtkFLuNUbzRfplKwOcLIM0XUrwD7/hYWBY76l4R7lGzI16qeXRQ4xko9ettCiBCzPLD8bfsOGaRH8K1V62+vS6LpEkc8iHaRlzttrO66XAkO8veCOWHIqgZiJw2LIqJ7Ykl4caYn5e2w2Q1alYmSbMB6Akz1V5fcM3brqeJTNdcrStrYQVxwdBSKQ== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PA4PR03MB7167.eurprd03.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(376002)(366004)(136003)(39830400003)(396003)(346002)(230922051799003)(451199024)(1800799009)(186009)(66556008)(66476007)(5660300002)(66946007)(31686004)(41300700001)(6916009)(316002)(44832011)(8936002)(8676002)(2906002)(30864003)(38100700002)(83380400001)(478600001)(31696002)(26005)(2616005)(86362001)(66899024)(6506007)(6512007)(6486002)(36756003)(2004002)(43740500002)(45980500001)(357404004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?Wd8/vslWRews5YGFM7JNRygZwOfe?= =?utf-8?q?9BebfPbua5ySHxjpakr/ajk8h7DZbV8y3dPWz624xvJZ+5m4XxqWsa7Ziqb+Jbu3h?= =?utf-8?q?gEUVwewAm9WtD0cMh9D5eOe8s6Shj8sEmaYg6uHEtu1+SrtJhSU71gztN346zOfrN?= =?utf-8?q?S2P0yCXK3uVw73vX40CZOKExolHf0yCVsBZiNzuoS8HjqxqaxgRxeAZMVRs0azMgd?= =?utf-8?q?pWFD8fYcPQUc2mgf9PmyqT2FDIT1CzbKKE8oM9wzxTnctJR+wkLfMKXZNAj5d7qNP?= =?utf-8?q?xjPDi+tZJampzDpMuIz6RcfnNh9gMTup4HjmOj/BeKhyaQ9RsEW56EbvFd2Sh71k2?= =?utf-8?q?Eb4hOYA10/FL1MTVyklDbRpV6bUBtp1ox98w8aWYsq6JmL6TsnBOLbbBBbhB+gtKY?= =?utf-8?q?4nIP2A1IxgG1VqeRqkBLbQ306xWl7kDxo+Tdr1aS5viTJkG+7VhDqR38fZWAfwbS7?= =?utf-8?q?SA87n52pr3ZdDCcb3CdoWKW2hyuapKm74R1pM0nKryuZ8e2bj1wJNlWNj63QMXZQE?= =?utf-8?q?cPS9146F5uXrF91saYUXpAGbbgZpmWBO5vVpo2ShF3D9EfxD5GFPeqHpr+EJyUw0Y?= =?utf-8?q?UIql5daEnZxrkmrw70IuonSfaG7lSOSWhhI0a/Gz6iWJyu5P5z9onvwFzf5LoeE4A?= =?utf-8?q?O0HcTfiuNoOWmi3lmFN0bGnPjy7QZFAx5q/IKZrLByewUAYl6zi0jAav4JXRnsbBw?= =?utf-8?q?fk/hKWON1dUnrn9+UUu4CTASSTPu2dNFGBUopcD0RTvQmh8klyaEqCfWlh7fs30B1?= =?utf-8?q?WwUJyLUUimFPbjLGUFMcT2TeEgGn/4bHNRFN3anDRWIkP67/My2+u7P+ZPMZiPoi2?= =?utf-8?q?DV8nT3K+QB4Bb7LfXIhoE/ZqwhUPqi69H5IUxYcGMNorLvvdRaCnC9FDDky5hoper?= =?utf-8?q?Q3kCJ4u4vRM5SbKy0G2WxLgFLJDusChkBncPIzdHx1OESk2RtrWPVUSxMGXPg6in6?= =?utf-8?q?8OOHhHbZ+V+O1cWX17W9ZBUUkMWU7zrdqASuQMIXPKGble1rUNb+Uggg4nkfW44ui?= =?utf-8?q?qDSqy9zp0MzU7xup18O0cZ2zLDQUrbs09aeaNBLZuiiiMPwVgAGzVZirdFuOOEmn2?= =?utf-8?q?rJRhHLijOL9lwUY8Scq/GXlRNJ+/DX63wuI5NJ5lemLdQcALZ92LMysSKrvWQi1ja?= =?utf-8?q?nl6DtgWEFnsBRHTNjWBOO3fCM0dZ17c0qMAvn0p3uBCvcKeQEPfBy7AqO2krts3u8?= =?utf-8?q?jEh8JQ+xfNLLZk0fjF7vf8gBMLoa2t5PcFzh1sUdWPvMhvDisCBQg9AKj5NLDrlzT?= =?utf-8?q?2D+fbKtRTZosMdrirna9HsUCAFESowCsC0NHAux0banafrRvVoBXd9aeIyHH31dlt?= =?utf-8?q?3toBv8/Ds7igaJKxt5dY8+2cRavPBf168+PV6+QqoxImAcK2+E4SWLVUTmuI2QDuR?= =?utf-8?q?qVkNuI7HOHJUGPXeckmE06dyn2sUtUQG4L1JZMrOWhwFyn9FhUuGIOr6ZrWaEKISp?= =?utf-8?q?doalxGZN1y/z4jVntlBVCx8hGwk7P3UV3pkUvnBHlspV2zo57T0FPZsI+qfz37cgl?= =?utf-8?q?nY3Y+20s8pa8oscyuqAqyruZs+uuYbMHl2LH/HLqTKLYsfcxvXdFjCk=3D?= X-OriginatorOrg: nativewaves.com X-MS-Exchange-CrossTenant-Network-Message-Id: b8ca1e1c-9399-4d4c-e592-08dbbdab9d1f X-MS-Exchange-CrossTenant-AuthSource: PA4PR03MB7167.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Sep 2023 09:41:37.7488 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: e4239718-b000-4513-8314-02ef46bd0276 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: MdTl/RT9Qom7bEYqw0XHeLrkmx93jcXkjtQ6oJ8gbd4DFRBH0rgEMJzeznM0BogKlVrofRPyAAFeC1VxSSP5h63CBlqwwi39iHUwzEJ7sRw= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB3PR0302MB9236 Subject: [FFmpeg-devel] [PATCH v2 2/2] libavformat/whip: add WebRTC-HTTP ingestion protocol (WHIP) 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: D4bwXPRWKO2a This is based on the library libdatachannel, which is much more lightweight than other libraries like libwebrtc. At the same time, using this library avoids reimplementing parts of WebRTC in FFmpeg. Signed-off-by: Michael Riedl --- configure | 5 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/whip.c | 538 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 545 insertions(+) create mode 100644 libavformat/whip.c diff --git a/configure b/configure index 48fee07f817..3bb68d3f20c 100755 --- a/configure +++ b/configure @@ -227,6 +227,7 @@ External library support: --enable-libcelt enable CELT decoding via libcelt [no] --enable-libcdio enable audio CD grabbing with libcdio [no] --enable-libcodec2 enable codec2 en/decoding using libcodec2 [no] + --enable-libdatachannel enable WHIP muxing via libdatachannel [no] --enable-libdav1d enable AV1 decoding via libdav1d [no] --enable-libdavs2 enable AVS2 decoding via libdavs2 [no] --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 @@ -1853,6 +1854,7 @@ EXTERNAL_LIBRARY_LIST=" libcaca libcelt libcodec2 + libdatachannel libdav1d libdc1394 libdrm @@ -3566,6 +3568,8 @@ wav_demuxer_select="riffdec" wav_muxer_select="riffenc" webm_chunk_muxer_select="webm_muxer" webm_dash_manifest_demuxer_select="matroska_demuxer" +whip_muxer_deps="libdatachannel" +whip_muxer_select="http_protocol rtpenc_chain" wtv_demuxer_select="mpegts_demuxer riffdec" wtv_muxer_select="mpegts_muxer riffenc" xmv_demuxer_select="riffdec" @@ -6691,6 +6695,7 @@ enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && enabled libcaca && require_pkg_config libcaca caca caca.h caca_create_canvas enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lcodec2 enabled libdav1d && require_pkg_config libdav1d "dav1d >= 0.5.0" "dav1d/dav1d.h" dav1d_version +enabled libdatachannel && require libdatachannel rtc/rtc.h rtcPreload -ldatachannel enabled libdavs2 && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open enabled libdc1394 && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new enabled libdrm && require_pkg_config libdrm libdrm xf86drm.h drmGetVersion diff --git a/libavformat/Makefile b/libavformat/Makefile index 329055ccfd9..db9d8ec5d47 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -621,6 +621,7 @@ OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o +OBJS-$(CONFIG_WHIP_MUXER) += whip.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o OBJS-$(CONFIG_WSAUD_MUXER) += westwood_audenc.o OBJS-$(CONFIG_WSD_DEMUXER) += wsddec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index d4b505a5a32..e8825a92b54 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -504,6 +504,7 @@ extern const FFOutputFormat ff_webm_chunk_muxer; extern const FFOutputFormat ff_webp_muxer; extern const AVInputFormat ff_webvtt_demuxer; extern const FFOutputFormat ff_webvtt_muxer; +extern const FFOutputFormat ff_whip_muxer; extern const AVInputFormat ff_wsaud_demuxer; extern const FFOutputFormat ff_wsaud_muxer; extern const AVInputFormat ff_wsd_demuxer; diff --git a/libavformat/whip.c b/libavformat/whip.c new file mode 100644 index 00000000000..8ed593a9b5c --- /dev/null +++ b/libavformat/whip.c @@ -0,0 +1,538 @@ +/* + * WebRTC-HTTP ingestion protocol (WHIP) muxer using libdatachannel + * + * Copyright (C) 2023 NativeWaves GmbH + * This work is supported by FFG project 47168763. + * + * 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 "internal.h" +#include "mux.h" +#include "url.h" +#include "version.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/log.h" +#include "libavutil/uuid.h" +#include "libavutil/random_seed.h" +#include "libavutil/time.h" +#include "rtp.h" +#include "rtc/rtc.h" + +typedef struct WHIPContext { + AVClass *av_class; + int peer_connection; + int* tracks; + rtcState state; + char* resource_location; + + char* bearer_token; + int max_stored_packets_count; + int64_t connection_timeout; +} WHIPContext; + +static inline const char* whip_get_state_name(rtcState state) +{ + switch (state) + { + case RTC_NEW: + return "RTC_NEW"; + case RTC_CONNECTING: + return "RTC_CONNECTING"; + case RTC_CONNECTED: + return "RTC_CONNECTED"; + case RTC_DISCONNECTED: + return "RTC_DISCONNECTED"; + case RTC_FAILED: + return "RTC_FAILED"; + case RTC_CLOSED: + return "RTC_CLOSED"; + default: + return "UNKNOWN"; + } +} + +static void whip_on_state_change(int pc, rtcState state, void* ptr) +{ + AVFormatContext* avctx = (AVFormatContext*)ptr; + WHIPContext* s = (WHIPContext*)avctx->priv_data; + + av_log(avctx, AV_LOG_VERBOSE, "Connection state changed from %s to %s\n", whip_get_state_name(s->state), whip_get_state_name(state)); + s->state = state; +} + +static void whip_rtc_log(rtcLogLevel rtcLevel, const char *message) +{ + int level = AV_LOG_INFO; + switch (rtcLevel) + { + case RTC_LOG_NONE: + level = AV_LOG_QUIET; + break; + case RTC_LOG_DEBUG: + level = AV_LOG_DEBUG; + break; + case RTC_LOG_VERBOSE: + case RTC_LOG_INFO: + level = AV_LOG_VERBOSE; + break; + case RTC_LOG_WARNING: + level = AV_LOG_WARNING; + break; + case RTC_LOG_ERROR: + level = AV_LOG_ERROR; + break; + case RTC_LOG_FATAL: + level = AV_LOG_FATAL; + break; + } + av_log(NULL, level, "[libdatachannel] %s\n", message); +} + +static void generate_random_uuid(char buffer[37]) +{ + AVUUID uuid; + av_random_bytes(uuid, sizeof(uuid)); + av_uuid_unparse(uuid, buffer); +} + +static void whip_deinit(AVFormatContext* avctx); +static int whip_init(AVFormatContext* avctx) +{ + WHIPContext* s = avctx->priv_data; + rtcConfiguration config; + AVStream* stream; + const AVCodecParameters* codecpar; + int i, ret; + char media_stream_id[37]; + rtcTrackInit track_init; + rtcPacketizationHandlerInit packetizer_init; + uint32_t ssrc; + uint8_t payload_type; + uint32_t clock_rate; + rtcCodec codec; + const AVChannelLayout supported_layout = AV_CHANNEL_LAYOUT_STEREO; + + memset(&config, 0, sizeof(rtcConfiguration)); + + rtcInitLogger(RTC_LOG_DEBUG, whip_rtc_log); + + if (!(s->peer_connection = rtcCreatePeerConnection(&config))) { + av_log(avctx, AV_LOG_ERROR, "Failed to create PeerConnection\n"); + return AVERROR_EXTERNAL; + } + rtcSetUserPointer(s->peer_connection, avctx); + if (rtcSetStateChangeCallback(s->peer_connection, whip_on_state_change)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set state change callback\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + /* configure tracks */ + generate_random_uuid(media_stream_id); + if (!(s->tracks = av_malloc(sizeof(int) * avctx->nb_streams))) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate tracks\n"); + ret = AVERROR(ENOMEM); + goto fail; + } + + for (i = 0; i < avctx->nb_streams; ++i) { + stream = avctx->streams[i]; + codecpar = stream->codecpar; + + ssrc = av_get_random_seed(); + payload_type = (uint8_t)ff_rtp_get_payload_type(NULL, codecpar, i); + av_log(avctx, AV_LOG_VERBOSE, "ssrc: %u, payload_type: %u\n", ssrc, payload_type); + + if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (codecpar->sample_rate != 48000) { + av_log(avctx, AV_LOG_ERROR, "Unsupported audio sample rate. Supported sample rate is 48000\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if (av_channel_layout_compare(&codecpar->ch_layout, &supported_layout)) { + av_log(avctx, AV_LOG_ERROR, "Unsupported audio channel layout. Supported layout is stereo\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + clock_rate = codecpar->sample_rate; + avpriv_set_pts_info(stream, 32, 1, clock_rate); + + switch (codecpar->codec_id) { + case AV_CODEC_ID_OPUS: + codec = RTC_CODEC_OPUS; + break; + case AV_CODEC_ID_AAC: + codec = RTC_CODEC_AAC; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported audio codec\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + memset(&track_init, 0, sizeof(rtcTrackInit)); + track_init.direction = RTC_DIRECTION_SENDONLY; + track_init.codec = codec; + track_init.payloadType = payload_type; + track_init.ssrc = ssrc; + track_init.mid = av_asprintf("%d", i); + track_init.name = LIBAVFORMAT_IDENT; + track_init.msid = media_stream_id; + track_init.trackId = av_asprintf("%s-audio-%d", media_stream_id, i); + + memset(&packetizer_init, 0, sizeof(rtcPacketizationHandlerInit)); + packetizer_init.ssrc = ssrc; + packetizer_init.cname = LIBAVFORMAT_IDENT; + packetizer_init.payloadType = payload_type; + packetizer_init.clockRate = clock_rate; + + s->tracks[i] = rtcAddTrackEx(s->peer_connection, &track_init); + if (!s->tracks[i]) { + av_log(avctx, AV_LOG_ERROR, "Failed to add track\n"); + ret = AVERROR(EINVAL); + goto fail; + } + if (codec == RTC_CODEC_OPUS) { + if (rtcSetOpusPacketizationHandler(s->tracks[i], &packetizer_init)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set Opus packetization handler\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + else if (codec == RTC_CODEC_AAC) { + if (rtcSetAACPacketizationHandler(s->tracks[i], &packetizer_init)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set AAC packetization handler\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + if (rtcChainRtcpSrReporter(s->tracks[i])) { + av_log(avctx, AV_LOG_ERROR, "Failed to chain RTCP SR reporter\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + if (rtcChainRtcpNackResponder(s->tracks[i], s->max_stored_packets_count)) { + av_log(avctx, AV_LOG_ERROR, "Failed to chain RTCP NACK responder\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + else if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + { + clock_rate = 90000; + avpriv_set_pts_info(stream, 32, 1, clock_rate); + + switch (codecpar->codec_id) + { + case AV_CODEC_ID_H264: + codec = RTC_CODEC_H264; + break; + case AV_CODEC_ID_HEVC: + codec = RTC_CODEC_H265; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported video codec\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + memset(&track_init, 0, sizeof(rtcTrackInit)); + track_init.direction = RTC_DIRECTION_SENDONLY; + track_init.codec = codec; + track_init.payloadType = payload_type; + track_init.ssrc = ssrc; + track_init.mid = av_asprintf("%d", i); + track_init.name = LIBAVFORMAT_IDENT; + track_init.msid = media_stream_id; + track_init.trackId = av_asprintf("%s-video-%d", media_stream_id, i); + + memset(&packetizer_init, 0, sizeof(rtcPacketizationHandlerInit)); + packetizer_init.ssrc = ssrc; + packetizer_init.cname = LIBAVFORMAT_IDENT; + packetizer_init.payloadType = payload_type; + packetizer_init.clockRate = clock_rate; + packetizer_init.nalSeparator = RTC_NAL_SEPARATOR_START_SEQUENCE; + + if (!(s->tracks[i] = rtcAddTrackEx(s->peer_connection, &track_init))) { + av_log(avctx, AV_LOG_ERROR, "Failed to add track\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + if (codec == RTC_CODEC_H264) { + if (rtcSetH264PacketizationHandler(s->tracks[i], &packetizer_init)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set H264 packetization handler\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + else if (codec == RTC_CODEC_H265) { + if (rtcSetH265PacketizationHandler(s->tracks[i], &packetizer_init)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set H265 packetization handler\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + if (rtcChainRtcpSrReporter(s->tracks[i])) { + av_log(avctx, AV_LOG_ERROR, "Failed to chain RTCP SR reporter\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + if (rtcChainRtcpNackResponder(s->tracks[i], s->max_stored_packets_count)) { + av_log(avctx, AV_LOG_ERROR, "Failed to chain RTCP NACK responder\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + } + } + + if (rtcSetLocalDescription(s->peer_connection, "offer")) { + av_log(avctx, AV_LOG_ERROR, "Failed to set local description\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + return 0; + +fail: + whip_deinit(avctx); + return ret; +} + +static int whip_write_header(AVFormatContext* avctx) +{ + WHIPContext* s = avctx->priv_data; + int ret; + URLContext* h = NULL; + int64_t timeout; + char* headers; + char offer_sdp[4096] = {0}; + char response[4096] = {0}; + + if (rtcGetLocalDescription(s->peer_connection, offer_sdp, sizeof(offer_sdp)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to get local description\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + av_log(avctx, AV_LOG_VERBOSE, "offer_sdp: %s\n", offer_sdp); + + /* alloc the http context */ + if ((ret = ffurl_alloc(&h, avctx->url, AVIO_FLAG_READ_WRITE, NULL)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_alloc failed\n"); + goto fail; + } + /* set options */ + headers = av_asprintf("Content-type: application/sdp\r\n"); + if (s->bearer_token) { + headers = av_asprintf("%sAuthorization: Bearer %s\r\n", headers, s->bearer_token); + } + av_log(avctx, AV_LOG_VERBOSE, "headers: %s\n", headers); + av_opt_set(h->priv_data, "headers", headers, 0); + av_opt_set(h->priv_data, "method", "POST", 0); + av_opt_set_bin(h->priv_data, "post_data", (uint8_t*)offer_sdp, strlen(offer_sdp), 0); + + /* open the http context */ + if ((ret = ffurl_connect(h, NULL)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_connect failed\n"); + goto fail; + } + + /* read the server reply which contains a unique ID */ + ret = ffurl_read_complete(h, (unsigned char*)response, sizeof(response)); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_read_complete failed\n"); + goto fail; + } + + av_log(avctx, AV_LOG_VERBOSE, "response: %s\n", response); + if (rtcSetRemoteDescription(s->peer_connection, response, "answer")) { + av_log(avctx, AV_LOG_ERROR, "Failed to set remote description\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + /* save resource location for later use */ + av_opt_get(h->priv_data, "new_location", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&s->resource_location); + av_log(avctx, AV_LOG_VERBOSE, "resource_location: %s\n", s->resource_location); + + /* close the http context */ + if ((ret = ffurl_closep(&h)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_closep &failed\n"); + goto fail; + } + + /* wait for connection to be established */ + timeout = av_gettime_relative() + s->connection_timeout; + while (s->state != RTC_CONNECTED) { + if (s->state == RTC_FAILED || s->state == RTC_CLOSED || av_gettime_relative() > timeout) { + av_log(avctx, AV_LOG_ERROR, "Failed to open connection\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + av_log(avctx, AV_LOG_VERBOSE, "Waiting for PeerConnection to open\n"); + av_usleep(100000); + } + + return 0; + +fail: + if (h) { + ffurl_closep(&h); + } + whip_deinit(avctx); + return ret; +} + +static int whip_write_packet(AVFormatContext* avctx, AVPacket* pkt) +{ + const WHIPContext* s = avctx->priv_data; + + if (s->state == RTC_DISCONNECTED || s->state == RTC_FAILED || s->state == RTC_CLOSED) { + return AVERROR_EOF; + } + + if (pkt->pts < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid packet PTS, dropping packet\n"); + return AVERROR(EINVAL); + } + + if (rtcSetTrackRtpTimestamp(s->tracks[pkt->stream_index], (uint32_t)pkt->pts)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set track RTP timestamp\n"); + return AVERROR_EXTERNAL; + } + + if (rtcSendMessage(s->tracks[pkt->stream_index], (const char*)pkt->data, pkt->size)) { + av_log(avctx, AV_LOG_ERROR, "Failed to send message\n"); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static int whip_write_trailer(AVFormatContext* avctx) +{ + WHIPContext* s = avctx->priv_data; + URLContext* h = NULL; + int ret; + char* headers; + + if (s->resource_location) { + av_log(avctx, AV_LOG_VERBOSE, "Closing resource %s\n", s->resource_location); + + /* alloc the http context */ + if ((ret = ffurl_alloc(&h, s->resource_location, AVIO_FLAG_READ_WRITE, NULL)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_alloc failed\n"); + goto fail; + } + + /* set options */ + if (s->bearer_token) { + headers = av_asprintf("Authorization: Bearer %s\r\n", s->bearer_token); + av_log(avctx, AV_LOG_VERBOSE, "headers: %s\n", headers); + } + av_opt_set(h->priv_data, "method", "DELETE", 0); + + /* open the http context */ + if ((ret = ffurl_connect(h, NULL)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_connect failed\n"); + goto fail; + } + + /* close the http context */ + if ((ret = ffurl_closep(&h)) < 0) { + av_log(avctx, AV_LOG_ERROR, "ffurl_close failed\n"); + goto fail; + } + + av_freep(&s->resource_location); + } + + return 0; + +fail: + if (h) { + ffurl_closep(&h); + } + return ret; +} + +static void whip_deinit(AVFormatContext* avctx) +{ + WHIPContext* s = avctx->priv_data; + if (s->tracks) { + for (int i = 0; i < avctx->nb_streams; ++i) { + if (s->tracks[i]) { + rtcDeleteTrack(s->tracks[i]); + } + } + av_freep(&s->tracks); + } + if (s->peer_connection) { + rtcDeletePeerConnection(s->peer_connection); + s->peer_connection = 0; + } +} + +static int whip_query_codec(enum AVCodecID codec_id, int std_compliance) +{ + switch (codec_id) + { + case AV_CODEC_ID_OPUS: + case AV_CODEC_ID_AAC: + case AV_CODEC_ID_H264: + case AV_CODEC_ID_HEVC: + return 1; + default: + return 0; + } +} + +#define OFFSET(x) offsetof(WHIPContext, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "bearer_token", "optional Bearer token for authentication and authorization", OFFSET(bearer_token), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, ENC }, + { "max_stored_packets_count", "maximum number of stored packets for retransmission", OFFSET(max_stored_packets_count), AV_OPT_TYPE_INT, { .i64 = 100 }, 0, INT_MAX, ENC }, + { "connection_timeout", "timeout in seconds for establishing a connection", OFFSET(connection_timeout), AV_OPT_TYPE_DURATION, { .i64 = 10000000 }, 100000, INT_MAX, ENC }, + { NULL }, +}; + +static const AVClass whip_muxer_class = { + .class_name = "WHIP muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFOutputFormat ff_whip_muxer = { + .p.name = "whip", + .p.long_name = NULL_IF_CONFIG_SMALL("WebRTC-HTTP ingestion protocol (WHIP) muxer"), + .p.audio_codec = AV_CODEC_ID_OPUS, // supported by major browsers + .p.video_codec = AV_CODEC_ID_H264, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE, + .p.priv_class = &whip_muxer_class, + .priv_data_size = sizeof(WHIPContext), + .write_packet = whip_write_packet, + .write_header = whip_write_header, + .write_trailer = whip_write_trailer, + .init = whip_init, + .deinit = whip_deinit, + .query_codec = whip_query_codec, +};