From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTP id 14EA247FC6 for ; Tue, 7 Nov 2023 14:13:46 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id F287168CB89; Tue, 7 Nov 2023 16:13:08 +0200 (EET) Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2086.outbound.protection.outlook.com [40.107.21.86]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id CB83F68CB83 for ; Tue, 7 Nov 2023 16:13:06 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=LPX+QujBlOR1vs5X3BrvzrCzXfkNA/mIRerVUQW/dyQad0gQmXaMqKs13Ojf9uuqJFJ7mMzg5kwFc/UWzrq3+T/TGLOg4TBX+DJM6xOL0FsorifjX5pOB+Uacwc8p4W6o1xqfyv7aJu8DTSOKLgvAzq333yq+xJfEMe7JafqVTKs5+r7zKioAw+G7Mmif+SyUz+EHfpRyWLY7EfOz6ZJFniSN1ze23uUDVo8zZPAMmk78+3xoUbDWRk+SA0n+tJm/i5Jno3kV9sJuCfFsCcwgBHFZKvBZt3bjgRlCgFOpCqEcUFehN7VtrGiwOmdyt47N2lN3jBoZJ9l0FCFq6BcrA== 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=nkPRbJQrjwCrzHzvR4muOVKo9fb/oxWKHa/tty7P5KM=; b=mF/eV9BeR/MksTiE3g44JqxI9hnFTXsqxQjHKnq46x3QsYtd/npSHmMGgs7zBfhRCh+wUDqOzlq0O6Dfa1KQZ9loTs1jF8nWtaeKSbD0XhFN7nNGF92mhvg9h9I9nkjWm3JfnAvV4bXFswPOyewKdVMOGrgIRVw2bb67jWFgIOghHtRBbub1ysfC8q+59lQxp5F9cr4xM4vyqUGEB/RMYNE9+wJh1yMC/LlhKFppkdIfMLfGJ7oqkMHkb1uEf2a4x3hdO5zXNrp2izngA2IFJwA9fVzNX70CmUez7Wl1qdOIfUlvMx9VSyGUE1iM7wcCyd1dW/szLEwL/490jhwgOw== 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=nkPRbJQrjwCrzHzvR4muOVKo9fb/oxWKHa/tty7P5KM=; b=GpEqACaeGsi7uy01C2Jc1KSXg9tI7HtEdDhUxxTVqZiq39mrOCkb1zekoltmwsedNPTKwh0WMiHUW9Wb1BTdKi+WE1RyDwFDtXNRaabV5rkbfRVVb6Kr0TS2zik6C/wmWQpLFI5DN3iZSL4bCq3RmRze1LkkCecpBl45y/zw6vk= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nativewaves.com; Received: from VI1PR03MB4239.eurprd03.prod.outlook.com (2603:10a6:803:5f::26) by GV1PR03MB8685.eurprd03.prod.outlook.com (2603:10a6:150:93::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.28; Tue, 7 Nov 2023 14:13:04 +0000 Received: from VI1PR03MB4239.eurprd03.prod.outlook.com ([fe80::150d:b41:699f:42b6]) by VI1PR03MB4239.eurprd03.prod.outlook.com ([fe80::150d:b41:699f:42b6%6]) with mapi id 15.20.6954.028; Tue, 7 Nov 2023 14:13:04 +0000 Message-ID: <3ba38498-a74a-4a56-955f-c1aa16153404@nativewaves.com> Date: Tue, 7 Nov 2023 15:13:03 +0100 User-Agent: Mozilla Thunderbird From: Michael Riedl To: ffmpeg-devel@ffmpeg.org Content-Language: en-US X-ClientProxiedBy: ZR0P278CA0220.CHEP278.PROD.OUTLOOK.COM (2603:10a6:910:6a::22) To VI1PR03MB4239.eurprd03.prod.outlook.com (2603:10a6:803:5f::26) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: VI1PR03MB4239:EE_|GV1PR03MB8685:EE_ X-MS-Office365-Filtering-Correlation-Id: 6041096a-3e61-47b5-de8c-08dbdf9ba8a3 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: RR9uBTxroHbmYx0+grwfK0Ek+UE3hfrZolSbOfwz9UlZgxh1WoZ0ToN53CavubqPfEaB67gMYL+bgwxJim+sR3igD/0ceIQtmUX9gCIE9b3JTam5HSTF7zoitV6SvrrkL/nGBWJouo0zBFizf8FkSy8/40yjnLxCtl2I5a+A2aOf67RHAXIkSf9C/PBB8/tqbYoCdDq+khbUnPPB1OdnV1ldc2IBGQmdD0eo2ofWmazrhFPY+OvHwRWUQD+IgL0tgOjxI988a+zDu4cYPZf4A1Yn0Z83msRmwPd04gg+dYlnQoyhbUHahcg899XupYfX3UK9G3QhnZIma5T/zR0Rtl2QI+PjTXPZ214qyUCbS9/QaRfO+zLAbAppom8Etv4f64D7aJ4y2wsNztXwHnaRZbYAA2IvbDVPWHqs/DVxPNUNnJ1xvQ8pkm3RX1WF5/FcgbDZtfpXO97MFrF6tA4DI1b0vho7/IUb4k1uGww/pj4eQFw2DQHbqKPK/ItiLT+z9qgnfW8mbu07RgASymVZBYzas3S5ZbX6n5gNrawZvx5KR3X994dUCRjzut0xrIg5MnWOuwJvWcZ/u6N7eKJuBY5nuL/zaWJG1W1iqbD/vJCrTAxhJS5h4IbpjfJNBpSmS31grnit/ZLKJaI9tzqEIlgGBkQU5tukSruzfxOrDY6Rlqc1xd2yId84Y/Ju7UOM5fN0XpSp1kxcLp4bNAw0ZQ== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR03MB4239.eurprd03.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(366004)(396003)(376002)(346002)(136003)(39830400003)(230173577357003)(230273577357003)(230922051799003)(64100799003)(451199024)(1800799009)(186009)(6486002)(2906002)(38100700002)(31686004)(30864003)(8676002)(5660300002)(41300700001)(83380400001)(86362001)(8936002)(31696002)(44832011)(6512007)(2616005)(316002)(66476007)(66946007)(66556008)(6916009)(6506007)(26005)(36756003)(478600001)(2004002)(43740500002)(45980500001); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?R2RpakI1VkZIRHo2d1Rub2pXaGNMQTQ4dUxZaVEwenBjOTBSRzltOXBmdG1P?= =?utf-8?B?WG56dDJaa0FRMlcySEdYQ3ZtRzd0bG9jZlRFTG9aa3pTSmplQXZvelhRRFdC?= =?utf-8?B?R3AzVUR3dVJFZTZjbmJFYllwTVFYOCtoeURTWTNLZ2hhbFFEVEZNQmgxS0hk?= =?utf-8?B?dHlUK2R0MjJ0bUw4aUd5cGEwVzFGUXZEeklIZTlKRGtmUDhxRGdyZGRwUUV0?= =?utf-8?B?S3VYaUR5K0k0dy85WXI0RnRSRVE3ZUlxdXh6clJrNFVUaldTRE9Zd25oNDFt?= =?utf-8?B?aGZQRmVzMnFaSXdsU2UxRTh5R29EanJKZlh4b0o1UkNaM1VMcnQ0REZqM3ht?= =?utf-8?B?a21UeVN1TjZCRjc5d1dndjIyZm9rU1FTcXVRUVFXRUVnYmp2cCttbHVZR1F5?= =?utf-8?B?RmtaSmtuVGhlTmJRekdINE9pQ3hNVzVWcTYwZWZ6OVd6eWJOdGdYR2xHNE10?= =?utf-8?B?S2ozeTB1NmVmb2RDVlVCRGo0RmU2NTZFMm9xOTNzRjZoVFIrSGQ1WFFJOTBo?= =?utf-8?B?cDFCUVlvTVVBd1AwNjNnbTRVOXFsaDFlZWtQZ0tleit1UGJZMzFIOGhSUWRp?= =?utf-8?B?UXJwN256OFRrNzMxa1dBekljNDB2SVFkM0V0d0ZjVXpET1hzemgzUFFCcm50?= =?utf-8?B?NnozM2JnUUVHbDFOaXZwanE2TUt6clZkTFlBRUFEaUhOeVZGWDlXd3ZrNjlU?= =?utf-8?B?c1ByL1JDUHdONE9RRFMzaHZ0L2xwSkRWK1B0RUFjdklVRGw5ekU3MVRKM3Y5?= =?utf-8?B?UDMyTTVESkNYdy9MWlZzZmZGdVh6azRmTDlMMWdQdlRmaUNUR080UWJPZkhE?= =?utf-8?B?djhUK0wzNllETWc1eVpsRnc0Ukg0U2Nhd1JTQW4wVmVUdnpCUWoza09kUlhC?= =?utf-8?B?TjE5OWxoNjZSQlNtQktJaWc2WU1IS05NRGxWcWswcy83c3NzZzlscHVPSVdK?= =?utf-8?B?KzBNN2NHZTlzNTFKSlpSNWd2ZGtWV2VNN1huV2F0cDdiVjEydys2YnNYdVVr?= =?utf-8?B?VUY1QjVNU2ZqQzNNRlBxTDUwbDVyV21FbHJ3UWpKbXBSOGxhN3N4WHJscWtP?= =?utf-8?B?V3dxRUJOMVBMRGcwQ0dvODkwcUE0MTZsOHlROUtUUWlXdWdGaU81ekp1L3pu?= =?utf-8?B?L1JaMWlxSUtFU0c4QmhkRFpVa2xPN0RmNDFzUlVZaWw5NUI3NzNlU1JqOVBD?= =?utf-8?B?RUFqNlBOSFg3dURxTFlBZDlQeGFtYUdFY0tYVThPVDNKb0dXOEgxSzlYWXZV?= =?utf-8?B?ckRUVU9xRGRIUnB1YnN6V1hNY0hZSENNc1pzVUJKdDBwRkdsM1BoMVdOejdJ?= =?utf-8?B?VE5MMWZVWjNrb2taT0NWMUlGekUwVS9McGpkNzdKS1YweU1hVk1MbUh2YXI0?= =?utf-8?B?NWpTTG1laXRUSWpRNHhNWGdYWGo4bkJ6U3FCWFRaZDVyQ01UV1dlUnh6MFRK?= =?utf-8?B?Mmd1MGtNV1ljODRqa1dlVVhNZndOcmFtZUJlRFc3bEFrVFBuNkhFcmRkNFhM?= =?utf-8?B?WHV3bjVEVWROdVhPNEdzcEMxSnJSTVJWa25sTWpKdjRIMUtLVWpvN1RhVHlN?= =?utf-8?B?Wk1CL1EyckMxM0pvS05HLzd4WUlteUJIN2Z3YzVtbktZZFZlazBRUWlYSzQy?= =?utf-8?B?T0E0Y2R3SkJ2eFlqS0lDdXJadWU3RU1MSS8rOHJma0lGVHpTZVFRUEZkVGlw?= =?utf-8?B?UkJrdmtBVjhEZkUzOUZLM1d2cS9USGFHSU5MS0FwYThKcEw5dWdNSzYvQlRm?= =?utf-8?B?dlRsNVVSRXFVMDBkbUxEQUx2dGpvaGV5NmNtRS9tWjBXRVVyTnB1SEdqdDhZ?= =?utf-8?B?Z1AxWHZEWVF4ckMwZlBTejNjQTVXVkJnTHh0cVh1em9DdEt6Nnkzb0UyWW9H?= =?utf-8?B?T0pPNCtVRXppbzBDWUY3MmQwUGhCWVJTQ01VS25WMFk1R1N2aHpKQXp4ZXZP?= =?utf-8?B?R05jY1NEbzVvVzRYaStleWttM1dHOWxvbUlQU252cVZ1cFR3cFRiNmtkbVlx?= =?utf-8?B?dGhQY0QvSzI0cklOZG00T04rOFVRR1YrYjhSQmpzTlBJZ09Mdm5OY0wxVStS?= =?utf-8?B?Z0xNalFvQ3BnWHJieEZpaFFSTy8wU1dOd3g0VXZ6SitBbk1SZkVkaXNHNWhW?= =?utf-8?B?aHNxWDRzeEEzM3FXUjRqRFBPZTM4eDFNU0FLSWlnMWZ4bWZKa2lQRXFjSmRT?= =?utf-8?Q?volgaIYNGQM7FEO7h8pEZ0Q=3D?= X-OriginatorOrg: nativewaves.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6041096a-3e61-47b5-de8c-08dbdf9ba8a3 X-MS-Exchange-CrossTenant-AuthSource: VI1PR03MB4239.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 07 Nov 2023 14:13:04.6541 (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: T5oo7yaTCmYGQQEAlYmElYO9EW9o3dO4VhWkVx8MIFRqeqAvA/8UiTSGKTc9XrveCwybGc6GgKmMHRN4JZP/lJC4x+ZwnjpKfeF9prqga1o= X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV1PR03MB8685 Subject: [FFmpeg-devel] [PATCH v2 6/6] libavformat/webrtc_mux: add WebRTC-HTTP ingestion protocol (WHIP) muxer 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 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: Signed-off-by: Michael Riedl --- Changelog | 1 + configure | 2 + doc/muxers.texi | 21 +++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/webrtc_mux.c (new) | 273 +++++++++++++++++++++++++++++++++ 6 files changed, 299 insertions(+) diff --git a/Changelog b/Changelog index 45c6c752a06..2604a2925bb 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version : - WHEP demuxer +- WHIP muxer version 6.1: diff --git a/configure b/configure index 02c6f7f2c5d..05cfbbb2376 100755 --- a/configure +++ b/configure @@ -3557,6 +3557,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 rtp_muxer" +whip_muxer_select="http_protocol rtpenc_chain" whep_demuxer_deps="libdatachannel sdp_demuxer" whep_demuxer_select="http_protocol" wtv_demuxer_select="mpegts_demuxer riffdec" diff --git a/doc/muxers.texi b/doc/muxers.texi index f6071484ff6..144b0638571 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -2846,4 +2846,25 @@ ffmpeg -f webm_dash_manifest -i video1.webm \ manifest.xml @end example +@section whip + +WebRTC-HTTP ingestion protocol (WHIP) muxer. + +This muxer allows sending audio and video streams to a remote media server +using the WebRTC-HTTP ingestion protocol (WHIP) as defined in +@url{https://datatracker.ietf.org/doc/draft-ietf-wish-whip/}. + +This muxer supports the following options: +@table @option +@item bearer_token +Optional bearer token for authentication and authorization to the HTTP server. +Default is @code{NULL}. +@item connection_timeout +Timeout for establishing a connection to the media server. +Default is 10 seconds. +@item rw_timeout +Timeout for receiving/writing data from/to the media server. +Default is 1 second. +@end table + @c man end MUXERS diff --git a/libavformat/Makefile b/libavformat/Makefile index f790fa8cae4..000fd308be2 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) += webrtc.o webrtc_mux.o OBJS-$(CONFIG_WHEP_DEMUXER) += webrtc.o webrtc_demux.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o OBJS-$(CONFIG_WSAUD_MUXER) += westwood_audenc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 7acb05634c8..2ad2a6dcba2 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_whep_demuxer; extern const AVInputFormat ff_wsaud_demuxer; extern const FFOutputFormat ff_wsaud_muxer; diff --git a/libavformat/webrtc_mux.c b/libavformat/webrtc_mux.c new file mode 100644 index 00000000000..2a659ffa41b --- /dev/null +++ b/libavformat/webrtc_mux.c @@ -0,0 +1,273 @@ +/* + * 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 "avformat.h" +#include "internal.h" +#include "libavutil/avstring.h" +#include "libavutil/time.h" +#include "mux.h" +#include "rtpenc.h" +#include "rtpenc_chain.h" +#include "rtsp.h" +#include "webrtc.h" +#include "version.h" + +typedef struct WHIPContext { + AVClass *av_class; + WebRTCContext webrtc_ctx; +} WHIPContext; + + +static void whip_deinit(AVFormatContext* avctx); +static int whip_init(AVFormatContext* avctx) +{ + WHIPContext*const ctx = (WHIPContext*const)avctx->priv_data; + AVStream* stream; + const AVCodecParameters* codecpar; + int i, ret; + char media_stream_id[37] = { 0 }; + rtcTrackInit track_init; + const AVChannelLayout supported_layout = AV_CHANNEL_LAYOUT_STEREO; + const RTPMuxContext* rtp_mux_ctx; + WebRTCTrack* track; + char sdp_stream[SDP_MAX_SIZE] = { 0 }; + char* fmtp; + + ctx->webrtc_ctx.avctx = avctx; + ff_webrtc_init_logger(); + ret = ff_webrtc_init_connection(&ctx->webrtc_ctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize connection\n"); + goto fail; + } + + if (!(ctx->webrtc_ctx.tracks = av_mallocz(sizeof(WebRTCTrack) * avctx->nb_streams))) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate tracks\n"); + ret = AVERROR(ENOMEM); + goto fail; + } + + /* configure tracks */ + ret = ff_webrtc_generate_media_stream_id(media_stream_id); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to generate media stream id\n"); + goto fail; + } + + for (i = 0; i < avctx->nb_streams; ++i) { + stream = avctx->streams[i]; + codecpar = stream->codecpar; + track = &ctx->webrtc_ctx.tracks[i]; + + switch (codecpar->codec_type) + { + case AVMEDIA_TYPE_VIDEO: + /* based on rtpenc */ + avpriv_set_pts_info(stream, 32, 1, 90000); + break; + case AVMEDIA_TYPE_AUDIO: + if (codecpar->sample_rate != 48000) { + av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate. Only 48kHz is supported\n"); + ret = AVERROR(EINVAL); + goto fail; + } + if (av_channel_layout_compare(&codecpar->ch_layout, &supported_layout) != 0) { + av_log(avctx, AV_LOG_ERROR, "Unsupported channel layout. Only stereo is supported\n"); + ret = AVERROR(EINVAL); + goto fail; + } + /* based on rtpenc */ + avpriv_set_pts_info(stream, 32, 1, codecpar->sample_rate); + break; + default: + continue; + } + + ret = ff_webrtc_init_urlcontext(&ctx->webrtc_ctx, i); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "webrtc_init_urlcontext failed\n"); + goto fail; + } + + ret = ff_rtp_chain_mux_open(&track->rtp_ctx, avctx, stream, track->rtp_url_context, RTP_MAX_PACKET_SIZE, i); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "ff_rtp_chain_mux_open failed\n"); + goto fail; + } + rtp_mux_ctx = (const RTPMuxContext*)ctx->webrtc_ctx.tracks[i].rtp_ctx->priv_data; + + memset(&track_init, 0, sizeof(rtcTrackInit)); + track_init.direction = RTC_DIRECTION_SENDONLY; + track_init.payloadType = rtp_mux_ctx->payload_type; + track_init.ssrc = rtp_mux_ctx->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); + + ret = ff_webrtc_convert_codec(codecpar->codec_id, &track_init.codec); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to convert codec\n"); + goto fail; + } + + /* parse fmtp from global header */ + ret = ff_sdp_write_media(sdp_stream, sizeof(sdp_stream), stream, i, NULL, NULL, 0, 0, NULL); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write sdp\n"); + goto fail; + } + fmtp = strstr(sdp_stream, "a=fmtp:"); + if (fmtp) { + track_init.profile = av_strndup(fmtp + 10, strchr(fmtp, '\r') - fmtp - 10); + track_init.profile = av_asprintf("%s;level-asymmetry-allowed=1", track_init.profile); + memset(sdp_stream, 0, sizeof(sdp_stream)); + } + + track->track_id = rtcAddTrackEx(ctx->webrtc_ctx.peer_connection, &track_init); + if (track->track_id < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to add track\n"); + ret = AVERROR(EINVAL); + goto fail; + } + } + + return 0; + +fail: + return ret; +} + +static int whip_write_header(AVFormatContext* avctx) +{ + WHIPContext*const ctx = (WHIPContext*const)avctx->priv_data; + int ret; + int64_t timeout; + + ret = ff_webrtc_create_resource(&ctx->webrtc_ctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to create resource\n"); + goto fail; + } + + /* wait for connection to be established */ + timeout = av_gettime_relative() + ctx->webrtc_ctx.connection_timeout; + while (ctx->webrtc_ctx.state != RTC_CONNECTED) { + if (ctx->webrtc_ctx.state == RTC_FAILED || ctx->webrtc_ctx.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(1000); + } + + return 0; + +fail: + return ret; +} + +static int whip_write_packet(AVFormatContext* avctx, AVPacket* pkt) +{ + WHIPContext*const ctx = (WHIPContext*const)avctx->priv_data; + AVFormatContext* rtpctx = ctx->webrtc_ctx.tracks[pkt->stream_index].rtp_ctx; + pkt->stream_index = 0; + + if (ctx->webrtc_ctx.state != RTC_CONNECTED) { + av_log(avctx, AV_LOG_ERROR, "Connection is not open\n"); + return AVERROR(EINVAL); + } + + return av_write_frame(rtpctx, pkt); +} + +static int whip_write_trailer(AVFormatContext* avctx) +{ + WHIPContext*const ctx = (WHIPContext*const)avctx->priv_data; + return ff_webrtc_close_resource(&ctx->webrtc_ctx); +} + +static void whip_deinit(AVFormatContext* avctx) +{ + WHIPContext*const ctx = (WHIPContext*const)avctx->priv_data; + ff_webrtc_deinit(&ctx->webrtc_ctx); +} + +static int whip_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) +{ + /* insert SPS/PPS into every keyframe otherwise browsers won't play the stream */ + if (st->codecpar->extradata_size && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + return ff_stream_add_bitstream_filter(st, "dump_extra", "freq=keyframe"); + return 1; +} + +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_PCM_MULAW: + case AV_CODEC_ID_PCM_ALAW: + case AV_CODEC_ID_H264: + case AV_CODEC_ID_HEVC: + case AV_CODEC_ID_AV1: + case AV_CODEC_ID_VP9: + return 1; + default: + return 0; + } +} + +#define OFFSET(x) offsetof(WHIPContext, x) +#define FLAGS AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + FF_WEBRTC_COMMON_OPTIONS, + { 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_NOFILE | AVFMT_GLOBALHEADER | AVFMT_EXPERIMENTAL, + .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, + .check_bitstream = whip_check_bitstream, +}; -- 2.39.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".