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 8837747FBE for ; Tue, 7 Nov 2023 14:13:36 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id F007568C6BA; Tue, 7 Nov 2023 16:13:06 +0200 (EET) Received: from EUR01-VE1-obe.outbound.protection.outlook.com (mail-ve1eur01on2071.outbound.protection.outlook.com [40.107.14.71]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 710DC68CB6C for ; Tue, 7 Nov 2023 16:13:00 +0200 (EET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=LBrGdcl9kOaneJKjXAUiLiNa0YLriZWZRBNYuIeJbf61mDJtLMvbx9DPbTmfQ3duNcL2qdtYM+xTGPm9Ei0Y4LmCbMl9bYL70lGDoyI3v5jNmDNBN0fvrOVQoEOw99FEnFPJ9N8iWGTsW4et+52NQVcN4Np3jQaLPMZSv9e0tnQruRRM3k1fojAsTLPXxhNo+8FBLKyAzXxykaE9U8xqczSBjll3R0IzDWZLpUXxExoNJBg9SGwwzn9FniXNDUFfJSRJT38b2m2duULv51eNjU9/fZwHG4FMKm14dMmEsGjIvczDtAI1llYxcZD9xQBZxZaruM376txlhT+6W9xYNw== 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=XVcmbofZ13j2GNaMxlXV7kKhoMzqAARTJNm1wucR19c=; b=ZUzvjWow4M+x9Jf2t80oroqrNcP9l479Zmy7yQ8IQs7PW0KBf4GQH+eCcy0my34ZOSd+jqqSOx59St67EZoOzXCxwFxD1MHzGwjFZ2NkA4ylR2MYkfrdfPCDaUKTAlqkge4oRl49xMR1tvIdbYdF05gMog9seU9mC2X1XO/aGfrj+PTSlk0jZq915sXUGrf2mjOzEaJf3irkpV5YzmA1OAEBBdqlLWrIjT1dr2PmOsC3roBy0cjTcg2zWLIjPGcCLK9NQx3qmgA8cBHb63OyXVbP2SL5xd75bqD5Lq/UfQ3L2s7J2HjrLhtRH4H0jMAPdhPT+8EMe+6TTJqJvm5lLg== 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=XVcmbofZ13j2GNaMxlXV7kKhoMzqAARTJNm1wucR19c=; b=oEDDE9+zL8ncw40N3QzqTVGaVcwAN3QJz37C6VAAQ/K0lLYuf572ZLg5fNf+xl+6kJGd9LB8PiSFPcNnde6bz9IJJFZpcs3OpMG0tnyu87cmOgR6ibsVrE/9RvRuE43MfDorTqaPRlw1sl4IPjU5G+k0ftbDRu2aNPnXiaE1rbg= 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 GV1PR03MB8333.eurprd03.prod.outlook.com (2603:10a6:150:61::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.29; Tue, 7 Nov 2023 14:12:53 +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:12:53 +0000 Message-ID: Date: Tue, 7 Nov 2023 15:12:52 +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_|GV1PR03MB8333:EE_ X-MS-Office365-Filtering-Correlation-Id: 59b65c95-740f-4ee5-07f8-08dbdf9ba213 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: /tuINJUe0xXv2rvMOF08wHAJOq+rJVbGgTkWtxeKntp4AngJFLWFvvNNmlExykJVeahAAWPPKcG5cjeZkU+5Bn+ST6euwU++fxJNuI6vsEiT+CWHf8ykBKZOL0fJyLanEoVdR6L6yfnsaeKJF4w1a7el3TXYF7xxUYDpA7/yoW7917KsTyDmJVkEkXaiiuZQibZbI+84s7Zp/2kMmYeCujKMoyAZySICMeLltplbDlqy57LuuJ18xUQDiUZxljrEykA3lDLj3HOkuneVeYMcRHPsgt8i8gq8Hw3JUTpBwI4NUVCz3bhxMgt0fQV9SfbPChEncwKAhKeFhFwzy7luk0HR/Ab6l2DB8AfH+M7emAE6YwLK+2chYzcvsKV+NMrIqkeDDg2cOSzx7KR9Ng+1tzSU7GCwz4QAeUIvdpIBnEoW1RXJvLVJn0waM39uYorFdSPXEcIVfUhH428gRclnJ7aTy/75AEBBf/3iCzlIfQta74sTC9ls5uUTlfaoZckNY+uNML39JxQnCIlJ+V0DRWWgeuXNjDHJS2L6lmDjmE52GUZo8cvN0bW09YNlJmoyBpcRAKO1aaBqA6BR6v4dR35QkmbE+fdHnOwedfcRBZnTXRdBlXGxTvI6fZFo9+BRyHnzLL/Im8DSmGpOzuWnqo7Ov10sbwkZyMc4YGywIesx1CRog+opxPE+QRNvgp4zvJz489lPFISFnCMAYALXZA== 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)(39830400003)(346002)(366004)(376002)(136003)(396003)(230273577357003)(230922051799003)(230173577357003)(186009)(1800799009)(64100799003)(451199024)(6486002)(478600001)(66946007)(2616005)(66476007)(26005)(66556008)(6512007)(41300700001)(2906002)(6506007)(8936002)(6916009)(316002)(5660300002)(30864003)(8676002)(44832011)(36756003)(31696002)(86362001)(83380400001)(38100700002)(66899024)(31686004)(2004002)(45980500001)(43740500002); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?eFdDV2x6c1ZxWXYwOHJZVHhqcnRETVJGSVc2NTdLSU80NGthc1JCVllZNS9N?= =?utf-8?B?OTRuWjZ4dlVEUjlXRUVJUUdiVDloRG9MemtzNFhaV1JyYUJzQlMveWxZYVl3?= =?utf-8?B?SWtZdmJGUi9La0k1ZUlzcTFUUi9Lc1A1b0M2YU1Wb1VMNHpZSzFNNW9YVmU4?= =?utf-8?B?UWFxcDVoUXhBRkdoVno3NldmdVNTUEVXZldiVW9wREVWV2xCRnJRdDJIbjhU?= =?utf-8?B?TjczcjdIYzFTSkVxYXcyaHU0TG5IdFRFZ1U3clZRMGpVQ00rUWthUERENUto?= =?utf-8?B?U0dRd0hwWjNCVjFOc21zdVFHajJzb0NxWWRWSjBWc0hHWmZEU0Z3bnBBS3F2?= =?utf-8?B?cHoxS3ZFdWxzcEdwV3pFZHRrNUxhN0JQUW1JTGk0RE5mUXNybXNabjBUMWs4?= =?utf-8?B?NDJYUTRhRG92MFdCWXRtVExCaHRYQXZQOHFzdTE2b0piTXJBd0ZjODNvZjJ1?= =?utf-8?B?VjdkU0VNWkFJNFYrQ0ErZFNxVjNueXNhU25NaVFLUW5lZ3BDcG5ka0xqNURt?= =?utf-8?B?MHc4c2NMMmtVbmgrSS9HNmNxWkJxZGwvblJvU0RUdDFpb3kyb2xpL2lRbUxE?= =?utf-8?B?OU5tRGV0Y0dHbHBQWVZub3FuaHhBNlVMWjAwenpDSEwwTldyMk1UL3FKZjhm?= =?utf-8?B?RWRVZkNPdk1MYkpSN3MyZ1lhbzZNQTdXQlFXRDVDdXo2Vk5tNGtiUEdraUR4?= =?utf-8?B?cUtDdiszSkdlaGt5d2N3cTBQK2NPWmNZaEV0YVh0djF0ZjlNdEs2STlQclI0?= =?utf-8?B?VXFCTllLNkNPa3BjMnpVVk8rdng0TFpmZE1QbmVIQTNZN3NPamtxK283MkJD?= =?utf-8?B?S1lSV2dnSlgxZzVGREdCRW42NkJ6YU53dEp4UnRwdW5heitGUGFxTVdkQU9r?= =?utf-8?B?Z2ZhRER2N0hIV04ydjd5S1NpZllPbGxDNG5KTjJ5Tis1Z0c5c3FLbWtxZlJJ?= =?utf-8?B?M2xLZC9DbEdWNVAwZnphZXNHTjhweWdUNWtYTlNsK2ErcUVCTXMvQnRGb0Fm?= =?utf-8?B?R0xLM0FEMG95T1ZFa25FOVdZSEJubG5OVFFHbzRrWGkxQXVPSERkcVFOYWxU?= =?utf-8?B?TXBoV25vRUVNYndPbnNBdnZYck56RGJyaXFDTGw0SkMya3ZUUDk1aEE3SUw2?= =?utf-8?B?OWlRdGx1MEFiS0ZNdGFXWGw1ak9wRmZFUHlQd2YzOEVYMEk0dEhsbDYrOWdS?= =?utf-8?B?dzRJY2txbFRGMngzYVBUbjRqZFlqa2IxV01QK2VCd2RHTEZBMUQ3blc1V3U5?= =?utf-8?B?MHZPWUxpNkl0Mlhield4cjY2ZHJVeTljQVFOTGtrWW0vUGNRS0hKMUczT2tV?= =?utf-8?B?Q2hFRzJrOVBPa0NIL2k4K3MrWWV1UnBlTnNHL3J1Rnh1RndlaEo2MGNFMWRl?= =?utf-8?B?T2tGZUkwRHJRTXM4SVBKcWtrKzVDTmVTZHRZVjk1b3RDaTIxRks0ZXJVWG1E?= =?utf-8?B?QitWWjlkQXErS2FablNxZU11dUJRRHR5ZjdVdW5TTzFuS1Z4TFV3WUhpYzZV?= =?utf-8?B?bkhnTWpPTE5HNndBdTFNcHBhSGMvVHAxbWVMZVJlTmZiNnF2MUovZ3N0RkFn?= =?utf-8?B?VnJZL2kxWmo3YmVVNmFuNjBLS29mWHhsZG85RzVUTmxVaDIyZ1pzYTE5bjdN?= =?utf-8?B?R0ZIRGtReGQxQUMwVkxJL084V0R0eDV5R0sveG1pcjRnZHEvT2lZM2ZHVDZV?= =?utf-8?B?M0ZaSkt0azBsQTFkQW5ZK2IwcW1nMzMvRm9zWkFxNlRvVXZ0amt5SmhVcUNh?= =?utf-8?B?bTMyVnFLc1VqTG1BdUtzWGt3d0dnYzlYS25PNy9wa0oxOGY1NE41c2ZXdVd1?= =?utf-8?B?VnF5aWMwbzU3WmFndGNWUDRITGpiazF3bFJCeEh1SmZNTytLdmRNUUhMMDBn?= =?utf-8?B?VUwxTStFQUJrRGJ4OW9ta3lkMWZFUlc0UUFWeHVBRkltMms5TzBLSGFQNVlz?= =?utf-8?B?SldURlo5cjZiNUNaZFNocEk1SGhSUUFMV0FVMG1FM29UWHBQTGRwWkZPNWhu?= =?utf-8?B?MmdCRjdmMFZYLzdaN2tNSWZtY3NJUm5nVjNLMlBOWmRMeldZaHZpVUc0bWkx?= =?utf-8?B?QndvMW4rKzlMOU9tcVNPUHllR1pDc29kRTNaVzdwQmlOeUNKUVR4Mk5Tc2JS?= =?utf-8?B?RGF2OStZRVJsNlpITTNQY2JFNHh2UlBBekluZms5M2JJT0tLNUVOVmhVNEVm?= =?utf-8?Q?EbAcBMiyTQkVZA3epLRQpYo=3D?= X-OriginatorOrg: nativewaves.com X-MS-Exchange-CrossTenant-Network-Message-Id: 59b65c95-740f-4ee5-07f8-08dbdf9ba213 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:12:53.6168 (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: JBjyv8WAu2Fb4AMNRFu1hbx+9fuv9wip5TpvmNhi+MRufXmrqyZLNU6y/ZS6h1a970/UQAjQgBAkaUcACSb62Ri8zNHwTzLrzjBhY/WHlBI= X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV1PR03MB8333 Subject: [FFmpeg-devel] [PATCH v2 4/6] libavformat/webrtc: add common code for WebRTC streaming 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 --- MAINTAINERS | 1 + libavformat/webrtc.c (new) | 410 +++++++++++++++++++++++++++++++++++++ libavformat/webrtc.h (new) | 70 +++++++ 3 files changed, 481 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b66c3d09a68..840290c4514 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -505,6 +505,7 @@ Muxers/Demuxers: wav.c Michael Niedermayer wc3movie.c Mike Melanson webm dash (matroskaenc.c) Vignesh Venkatasubramanian + webrtc* Michael Riedl webvtt* Matthew J Heaney westwood.c Mike Melanson wtv.c Peter Ross diff --git a/libavformat/webrtc.c b/libavformat/webrtc.c new file mode 100644 index 00000000000..c5a0ce8f5de --- /dev/null +++ b/libavformat/webrtc.c @@ -0,0 +1,410 @@ +/* + * WebRTC-HTTP ingestion/egress protocol (WHIP/WHEP) common code + * + * 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 "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/uuid.h" +#include "libavutil/random_seed.h" +#include "rtpenc_chain.h" +#include "rtsp.h" +#include "webrtc.h" + +static const char* webrtc_get_state_name(const 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 webrtc_log(const rtcLogLevel rtcLevel, const char *const message) +{ + int level = AV_LOG_VERBOSE; + switch (rtcLevel) + { + case RTC_LOG_NONE: + level = AV_LOG_QUIET; + break; + case RTC_LOG_DEBUG: + case RTC_LOG_VERBOSE: + level = AV_LOG_DEBUG; + break; + 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); +} + +void ff_webrtc_init_logger(void) +{ + rtcLogLevel level = RTC_LOG_VERBOSE; + switch (av_log_get_level()) + { + case AV_LOG_QUIET: + level = RTC_LOG_NONE; + break; + case AV_LOG_DEBUG: + level = RTC_LOG_DEBUG; + break; + case AV_LOG_VERBOSE: + level = RTC_LOG_VERBOSE; + break; + case AV_LOG_WARNING: + level = RTC_LOG_WARNING; + break; + case AV_LOG_ERROR: + level = RTC_LOG_ERROR; + break; + case AV_LOG_FATAL: + level = RTC_LOG_FATAL; + break; + } + + rtcInitLogger(level, webrtc_log); +} + +int ff_webrtc_generate_media_stream_id(char media_stream_id[37]) +{ + int ret; + AVUUID uuid; + + ret = av_random_bytes(uuid, sizeof(uuid)); + if (ret < 0) { + goto fail; + } + av_uuid_unparse(uuid, media_stream_id); + return 0; + +fail: + return ret; +} + +int ff_webrtc_create_resource(WebRTCContext*const ctx) +{ + int ret; + URLContext* h = NULL; + char* headers = NULL; + char offer_sdp[SDP_MAX_SIZE] = { 0 }; + char response_sdp[SDP_MAX_SIZE] = { 0 }; + + /* set local description */ + if (rtcSetLocalDescription(ctx->peer_connection, "offer") != RTC_ERR_SUCCESS) { + av_log(ctx->avctx, AV_LOG_ERROR, "Failed to set local description\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + /* create offer */ + ret = rtcGetLocalDescription(ctx->peer_connection, offer_sdp, sizeof(offer_sdp)); + if (ret < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "Failed to get local description\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + av_log(ctx->avctx, AV_LOG_VERBOSE, "offer_sdp: %s\n", offer_sdp); + + /* alloc the http context */ + if ((ret = ffurl_alloc(&h, ctx->avctx->url, AVIO_FLAG_READ_WRITE, NULL)) < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_alloc failed\n"); + goto fail; + } + + /* set options */ + headers = av_asprintf("Content-type: application/sdp\r\n"); + if (headers && ctx->bearer_token) { + headers = av_asprintf("%sAuthorization: Bearer %s\r\n", headers, ctx->bearer_token); + } + if (!headers) { + ret = AVERROR(ENOMEM); + goto fail; + } + av_log(ctx->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(ctx->avctx, AV_LOG_ERROR, "ffurl_connect failed\n"); + goto fail; + } + + /* read the server reply */ + ret = ffurl_read_complete(h, (unsigned char*)response_sdp, sizeof(response_sdp)); + if (ret < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_read_complete failed\n"); + goto fail; + } + + av_log(ctx->avctx, AV_LOG_VERBOSE, "response: %s\n", response_sdp); + + /* set remote description */ + ret = rtcSetRemoteDescription(ctx->peer_connection, response_sdp, "answer"); + if (ret < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "Failed to set remote description\n"); + goto fail; + } + + /* save resource location for later use */ + av_opt_get(h->priv_data, "new_location", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&ctx->resource_location); + av_log(ctx->avctx, AV_LOG_VERBOSE, "resource_location: %s\n", ctx->resource_location); + + /* close the http context */ + if ((ret = ffurl_closep(&h)) < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_closep failed\n"); + goto fail; + } + + av_freep(&headers); + return 0; + +fail: + if (h) { + ffurl_closep(&h); + } + av_freep(&headers); + return ret; +} + +int ff_webrtc_close_resource(WebRTCContext*const ctx) +{ + int ret; + URLContext* h = NULL; + char* headers = NULL; + + if (!ctx->resource_location) { + return 0; + } + + /* alloc the http context */ + if ((ret = ffurl_alloc(&h, ctx->resource_location, AVIO_FLAG_READ_WRITE, NULL)) < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_alloc failed\n"); + goto fail; + } + + /* set options */ + if (ctx->bearer_token) { + headers = av_asprintf("Authorization: Bearer %s\r\n", ctx->bearer_token); + if (!headers) { + ret = AVERROR(ENOMEM); + goto fail; + } + av_log(ctx->avctx, AV_LOG_VERBOSE, "headers: %s\n", headers); + av_opt_set(h->priv_data, "headers", headers, 0); + } + av_opt_set(h->priv_data, "method", "DELETE", 0); + + /* open the http context */ + if ((ret = ffurl_connect(h, NULL)) < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_connect failed\n"); + goto fail; + } + + /* close the http context */ + if ((ret = ffurl_closep(&h)) < 0) { + av_log(ctx->avctx, AV_LOG_ERROR, "ffurl_close failed\n"); + goto fail; + } + +fail: + if (h) { + ffurl_closep(&h); + } + av_freep(&ctx->resource_location); + av_freep(&headers); + return ret; +} + +/* callback for receiving data */ +static int webrtc_read(URLContext *h, unsigned char *buf, int size) +{ + const WebRTCTrack*const ctx = (const WebRTCTrack*const)h->priv_data; + int ret; + + ret = rtcReceiveMessage(ctx->track_id, (char*)buf, &size); + if (ret == RTC_ERR_NOT_AVAIL) { + return AVERROR(EAGAIN); + } + else if (ret == RTC_ERR_TOO_SMALL) { + return AVERROR_BUFFER_TOO_SMALL; + } + else if (ret != RTC_ERR_SUCCESS) { + av_log(ctx->avctx, AV_LOG_ERROR, "rtcReceiveMessage failed: %d\n", ret); + return AVERROR_EOF; + } + return size; +} + +/* callback for sending data */ +static int webrtc_write(URLContext *h, const unsigned char *buf, int size) +{ + const WebRTCTrack*const ctx = (const WebRTCTrack*const)h->priv_data; + int ret; + + ret = rtcSendMessage(ctx->track_id, (const char*)buf, size); + if (ret != RTC_ERR_SUCCESS) { + av_log(ctx->avctx, AV_LOG_ERROR, "rtcSendMessage failed: %d\n", ret); + return AVERROR_EXTERNAL; + } + return size; +} + +static const URLProtocol ff_webrtc_protocol = { + .name = "webrtc", + .url_read = webrtc_read, + .url_write = webrtc_write, +}; + +int ff_webrtc_init_urlcontext(WebRTCContext*const ctx, int track_idx) +{ + WebRTCTrack*const track = &ctx->tracks[track_idx]; + + track->rtp_url_context = av_mallocz(sizeof(URLContext)); + if (!track->rtp_url_context) { + return AVERROR(ENOMEM); + } + + track->rtp_url_context->prot = &ff_webrtc_protocol; + track->rtp_url_context->priv_data = track; + track->rtp_url_context->max_packet_size = RTP_MAX_PACKET_SIZE; + track->rtp_url_context->flags = AVIO_FLAG_READ_WRITE; + track->rtp_url_context->rw_timeout = ctx->rw_timeout; + return 0; +} + +static void webrtc_on_state_change(int pc, rtcState state, void* ptr) +{ + WebRTCContext*const ctx = (WebRTCContext*const)ptr; + + av_log(ctx->avctx, AV_LOG_VERBOSE, "Connection state changed from %s to %s\n", webrtc_get_state_name(ctx->state), webrtc_get_state_name(state)); + ctx->state = state; +} + +int ff_webrtc_init_connection(WebRTCContext *const ctx) +{ + int ret; + rtcConfiguration config = { 0 }; + + if (!(ctx->peer_connection = rtcCreatePeerConnection(&config))) { + av_log(ctx->avctx, AV_LOG_ERROR, "Failed to create PeerConnection\n"); + return AVERROR_EXTERNAL; + } + + rtcSetUserPointer(ctx->peer_connection, ctx); + + if (rtcSetStateChangeCallback(ctx->peer_connection, webrtc_on_state_change)) { + av_log(ctx->avctx, AV_LOG_ERROR, "Failed to set state change callback\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + return 0; + +fail: + rtcDeletePeerConnection(ctx->peer_connection); + return ret; +} + +int ff_webrtc_convert_codec(enum AVCodecID codec_id, rtcCodec* rtc_codec) +{ + switch (codec_id) + { + case AV_CODEC_ID_H264: + *rtc_codec = RTC_CODEC_H264; + break; + case AV_CODEC_ID_HEVC: + *rtc_codec = RTC_CODEC_H265; + break; + case AV_CODEC_ID_AV1: + *rtc_codec = RTC_CODEC_AV1; + break; + case AV_CODEC_ID_VP8: + *rtc_codec = RTC_CODEC_VP8; + break; + case AV_CODEC_ID_VP9: + *rtc_codec = RTC_CODEC_VP9; + break; + case AV_CODEC_ID_OPUS: + *rtc_codec = RTC_CODEC_OPUS; + break; + case AV_CODEC_ID_AAC: + *rtc_codec = RTC_CODEC_AAC; + break; + case AV_CODEC_ID_PCM_ALAW: + *rtc_codec = RTC_CODEC_PCMA; + break; + case AV_CODEC_ID_PCM_MULAW: + *rtc_codec = RTC_CODEC_PCMU; + break; + default: + *rtc_codec = -1; + return AVERROR(EINVAL); + } + + return 0; +} + +void ff_webrtc_deinit(WebRTCContext*const ctx) +{ + if (ctx->tracks) { + for (int i = 0; i < ctx->nb_tracks; ++i) { + if (ctx->tracks[i].rtp_ctx) + avformat_free_context(ctx->tracks[i].rtp_ctx); + if (ctx->tracks[i].rtp_url_context) + av_freep(&ctx->tracks[i].rtp_url_context); + if (ctx->tracks[i].track_id) + rtcDeleteTrack(ctx->tracks[i].track_id); + } + av_freep(&ctx->tracks); + } + if (ctx->peer_connection) { + rtcDeletePeerConnection(ctx->peer_connection); + ctx->peer_connection = 0; + } + if (ctx->resource_location) + av_freep(&ctx->resource_location); +} \ No newline at end of file diff --git a/libavformat/webrtc.h b/libavformat/webrtc.h new file mode 100644 index 00000000000..786242de377 --- /dev/null +++ b/libavformat/webrtc.h @@ -0,0 +1,70 @@ +/* + * WebRTC-HTTP ingestion/egress protocol (WHIP/WHEP) common code + * + * 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 + */ + +#ifndef AVFORMAT_WEBRTC_H +#define AVFORMAT_WEBRTC_H + +#include "avformat.h" +#include "avio_internal.h" +#include "libavcodec/codec_id.h" +#include "url.h" +#include "rtc/rtc.h" + +#define RTP_MAX_PACKET_SIZE 1450 + +typedef struct WebRTCTrack { + AVFormatContext *avctx; + int track_id; + AVFormatContext *rtp_ctx; + URLContext *rtp_url_context; +} WebRTCTrack; + +typedef struct WebRTCContext { + AVFormatContext *avctx; + int peer_connection; + rtcState state; + WebRTCTrack *tracks; + int nb_tracks; + const char *resource_location; + + /* options */ + char* bearer_token; + int64_t connection_timeout; + int64_t rw_timeout; +} WebRTCContext; + +#define FF_WEBRTC_COMMON_OPTIONS \ + { "bearer_token", "optional bearer token for authentication and authorization", OFFSET(webrtc_ctx.bearer_token), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, \ + { "connection_timeout", "timeout for establishing a connection", OFFSET(webrtc_ctx.connection_timeout), AV_OPT_TYPE_DURATION, { .i64 = 10000000 }, 1, INT_MAX, FLAGS }, \ + { "rw_timeout", "timeout for receiving/writing data", OFFSET(webrtc_ctx.rw_timeout), AV_OPT_TYPE_DURATION, { .i64 = 1000000 }, 1, INT_MAX, FLAGS } + +int ff_webrtc_close_resource(WebRTCContext*const ctx); +int ff_webrtc_convert_codec(enum AVCodecID codec_id, rtcCodec* rtc_codec); +int ff_webrtc_create_resource(WebRTCContext*const ctx); +void ff_webrtc_deinit(WebRTCContext*const ctx); +int ff_webrtc_generate_media_stream_id(char media_stream_id[37]); +int ff_webrtc_init_connection(WebRTCContext*const ctx); +void ff_webrtc_init_logger(void); +int ff_webrtc_init_urlcontext(WebRTCContext*const ctx, int track_idx); + +#endif /* AVFORMAT_WEBRTC_H */ -- 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".