1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * IPFS and IPNS protocol support through IPFS Gateway. 3cabdff1aSopenharmony_ci * Copyright (c) 2022 Mark Gaiser 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 23cabdff1aSopenharmony_ci#include "libavutil/getenv_utf8.h" 24cabdff1aSopenharmony_ci#include "libavutil/opt.h" 25cabdff1aSopenharmony_ci#include <sys/stat.h> 26cabdff1aSopenharmony_ci#include "os_support.h" 27cabdff1aSopenharmony_ci#include "url.h" 28cabdff1aSopenharmony_ci 29cabdff1aSopenharmony_ci// Define the posix PATH_MAX if not there already. 30cabdff1aSopenharmony_ci// This fixes a compile issue for MSVC. 31cabdff1aSopenharmony_ci#ifndef PATH_MAX 32cabdff1aSopenharmony_ci#define PATH_MAX 4096 33cabdff1aSopenharmony_ci#endif 34cabdff1aSopenharmony_ci 35cabdff1aSopenharmony_citypedef struct IPFSGatewayContext { 36cabdff1aSopenharmony_ci AVClass *class; 37cabdff1aSopenharmony_ci URLContext *inner; 38cabdff1aSopenharmony_ci // Is filled by the -gateway argument and not changed after. 39cabdff1aSopenharmony_ci char *gateway; 40cabdff1aSopenharmony_ci // If the above gateway is non null, it will be copied into this buffer. 41cabdff1aSopenharmony_ci // Else this buffer will contain the auto detected gateway. 42cabdff1aSopenharmony_ci // In either case, the gateway to use will be in this buffer. 43cabdff1aSopenharmony_ci char gateway_buffer[PATH_MAX]; 44cabdff1aSopenharmony_ci} IPFSGatewayContext; 45cabdff1aSopenharmony_ci 46cabdff1aSopenharmony_ci// A best-effort way to find the IPFS gateway. 47cabdff1aSopenharmony_ci// Only the most appropiate gateway is set. It's not actually requested 48cabdff1aSopenharmony_ci// (http call) to prevent a potential slowdown in startup. A potential timeout 49cabdff1aSopenharmony_ci// is handled by the HTTP protocol. 50cabdff1aSopenharmony_cistatic int populate_ipfs_gateway(URLContext *h) 51cabdff1aSopenharmony_ci{ 52cabdff1aSopenharmony_ci IPFSGatewayContext *c = h->priv_data; 53cabdff1aSopenharmony_ci char ipfs_full_data_folder[PATH_MAX]; 54cabdff1aSopenharmony_ci char ipfs_gateway_file[PATH_MAX]; 55cabdff1aSopenharmony_ci struct stat st; 56cabdff1aSopenharmony_ci int stat_ret = 0; 57cabdff1aSopenharmony_ci int ret = AVERROR(EINVAL); 58cabdff1aSopenharmony_ci FILE *gateway_file = NULL; 59cabdff1aSopenharmony_ci char *env_ipfs_gateway, *env_ipfs_path; 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ci // Test $IPFS_GATEWAY. 62cabdff1aSopenharmony_ci env_ipfs_gateway = getenv_utf8("IPFS_GATEWAY"); 63cabdff1aSopenharmony_ci if (env_ipfs_gateway != NULL) { 64cabdff1aSopenharmony_ci int printed = snprintf(c->gateway_buffer, sizeof(c->gateway_buffer), 65cabdff1aSopenharmony_ci "%s", env_ipfs_gateway); 66cabdff1aSopenharmony_ci freeenv_utf8(env_ipfs_gateway); 67cabdff1aSopenharmony_ci if (printed >= sizeof(c->gateway_buffer)) { 68cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 69cabdff1aSopenharmony_ci "The IPFS_GATEWAY environment variable " 70cabdff1aSopenharmony_ci "exceeds the maximum length. " 71cabdff1aSopenharmony_ci "We allow a max of %zu characters\n", 72cabdff1aSopenharmony_ci sizeof(c->gateway_buffer)); 73cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 74cabdff1aSopenharmony_ci goto err; 75cabdff1aSopenharmony_ci } 76cabdff1aSopenharmony_ci 77cabdff1aSopenharmony_ci ret = 1; 78cabdff1aSopenharmony_ci goto err; 79cabdff1aSopenharmony_ci } else 80cabdff1aSopenharmony_ci av_log(h, AV_LOG_DEBUG, "$IPFS_GATEWAY is empty.\n"); 81cabdff1aSopenharmony_ci 82cabdff1aSopenharmony_ci // We need to know the IPFS folder to - eventually - read the contents of 83cabdff1aSopenharmony_ci // the "gateway" file which would tell us the gateway to use. 84cabdff1aSopenharmony_ci env_ipfs_path = getenv_utf8("IPFS_PATH"); 85cabdff1aSopenharmony_ci if (env_ipfs_path == NULL) { 86cabdff1aSopenharmony_ci int printed; 87cabdff1aSopenharmony_ci char *env_home = getenv_utf8("HOME"); 88cabdff1aSopenharmony_ci 89cabdff1aSopenharmony_ci av_log(h, AV_LOG_DEBUG, "$IPFS_PATH is empty.\n"); 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci // Try via the home folder. 92cabdff1aSopenharmony_ci if (env_home == NULL) { 93cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, "$HOME appears to be empty.\n"); 94cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 95cabdff1aSopenharmony_ci goto err; 96cabdff1aSopenharmony_ci } 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci // Verify the composed path fits. 99cabdff1aSopenharmony_ci printed = snprintf( 100cabdff1aSopenharmony_ci ipfs_full_data_folder, sizeof(ipfs_full_data_folder), 101cabdff1aSopenharmony_ci "%s/.ipfs/", env_home); 102cabdff1aSopenharmony_ci freeenv_utf8(env_home); 103cabdff1aSopenharmony_ci if (printed >= sizeof(ipfs_full_data_folder)) { 104cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 105cabdff1aSopenharmony_ci "The IPFS data path exceeds the " 106cabdff1aSopenharmony_ci "max path length (%zu)\n", 107cabdff1aSopenharmony_ci sizeof(ipfs_full_data_folder)); 108cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 109cabdff1aSopenharmony_ci goto err; 110cabdff1aSopenharmony_ci } 111cabdff1aSopenharmony_ci 112cabdff1aSopenharmony_ci // Stat the folder. 113cabdff1aSopenharmony_ci // It should exist in a default IPFS setup when run as local user. 114cabdff1aSopenharmony_ci stat_ret = stat(ipfs_full_data_folder, &st); 115cabdff1aSopenharmony_ci 116cabdff1aSopenharmony_ci if (stat_ret < 0) { 117cabdff1aSopenharmony_ci av_log(h, AV_LOG_INFO, 118cabdff1aSopenharmony_ci "Unable to find IPFS folder. We tried:\n" 119cabdff1aSopenharmony_ci "- $IPFS_PATH, which was empty.\n" 120cabdff1aSopenharmony_ci "- $HOME/.ipfs (full uri: %s) which doesn't exist.\n", 121cabdff1aSopenharmony_ci ipfs_full_data_folder); 122cabdff1aSopenharmony_ci ret = AVERROR(ENOENT); 123cabdff1aSopenharmony_ci goto err; 124cabdff1aSopenharmony_ci } 125cabdff1aSopenharmony_ci } else { 126cabdff1aSopenharmony_ci int printed = snprintf( 127cabdff1aSopenharmony_ci ipfs_full_data_folder, sizeof(ipfs_full_data_folder), 128cabdff1aSopenharmony_ci "%s", env_ipfs_path); 129cabdff1aSopenharmony_ci freeenv_utf8(env_ipfs_path); 130cabdff1aSopenharmony_ci if (printed >= sizeof(ipfs_full_data_folder)) { 131cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 132cabdff1aSopenharmony_ci "The IPFS_PATH environment variable " 133cabdff1aSopenharmony_ci "exceeds the maximum length. " 134cabdff1aSopenharmony_ci "We allow a max of %zu characters\n", 135cabdff1aSopenharmony_ci sizeof(c->gateway_buffer)); 136cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 137cabdff1aSopenharmony_ci goto err; 138cabdff1aSopenharmony_ci } 139cabdff1aSopenharmony_ci } 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci // Copy the fully composed gateway path into ipfs_gateway_file. 142cabdff1aSopenharmony_ci if (snprintf(ipfs_gateway_file, sizeof(ipfs_gateway_file), "%sgateway", 143cabdff1aSopenharmony_ci ipfs_full_data_folder) 144cabdff1aSopenharmony_ci >= sizeof(ipfs_gateway_file)) { 145cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 146cabdff1aSopenharmony_ci "The IPFS gateway file path exceeds " 147cabdff1aSopenharmony_ci "the max path length (%zu)\n", 148cabdff1aSopenharmony_ci sizeof(ipfs_gateway_file)); 149cabdff1aSopenharmony_ci ret = AVERROR(ENOENT); 150cabdff1aSopenharmony_ci goto err; 151cabdff1aSopenharmony_ci } 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci // Get the contents of the gateway file. 154cabdff1aSopenharmony_ci gateway_file = avpriv_fopen_utf8(ipfs_gateway_file, "r"); 155cabdff1aSopenharmony_ci if (!gateway_file) { 156cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 157cabdff1aSopenharmony_ci "The IPFS gateway file (full uri: %s) doesn't exist. " 158cabdff1aSopenharmony_ci "Is the gateway enabled?\n", 159cabdff1aSopenharmony_ci ipfs_gateway_file); 160cabdff1aSopenharmony_ci ret = AVERROR(ENOENT); 161cabdff1aSopenharmony_ci goto err; 162cabdff1aSopenharmony_ci } 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci // Read a single line (fgets stops at new line mark). 165cabdff1aSopenharmony_ci if (!fgets(c->gateway_buffer, sizeof(c->gateway_buffer) - 1, gateway_file)) { 166cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, "Unable to read from file (full uri: %s).\n", 167cabdff1aSopenharmony_ci ipfs_gateway_file); 168cabdff1aSopenharmony_ci ret = AVERROR(ENOENT); 169cabdff1aSopenharmony_ci goto err; 170cabdff1aSopenharmony_ci } 171cabdff1aSopenharmony_ci 172cabdff1aSopenharmony_ci // Replace first occurence of end of line with \0 173cabdff1aSopenharmony_ci c->gateway_buffer[strcspn(c->gateway_buffer, "\r\n")] = 0; 174cabdff1aSopenharmony_ci 175cabdff1aSopenharmony_ci // If strlen finds anything longer then 0 characters then we have a 176cabdff1aSopenharmony_ci // potential gateway url. 177cabdff1aSopenharmony_ci if (*c->gateway_buffer == '\0') { 178cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 179cabdff1aSopenharmony_ci "The IPFS gateway file (full uri: %s) appears to be empty. " 180cabdff1aSopenharmony_ci "Is the gateway started?\n", 181cabdff1aSopenharmony_ci ipfs_gateway_file); 182cabdff1aSopenharmony_ci ret = AVERROR(EILSEQ); 183cabdff1aSopenharmony_ci goto err; 184cabdff1aSopenharmony_ci } else { 185cabdff1aSopenharmony_ci // We're done, the c->gateway_buffer has something that looks valid. 186cabdff1aSopenharmony_ci ret = 1; 187cabdff1aSopenharmony_ci goto err; 188cabdff1aSopenharmony_ci } 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_cierr: 191cabdff1aSopenharmony_ci if (gateway_file) 192cabdff1aSopenharmony_ci fclose(gateway_file); 193cabdff1aSopenharmony_ci 194cabdff1aSopenharmony_ci return ret; 195cabdff1aSopenharmony_ci} 196cabdff1aSopenharmony_ci 197cabdff1aSopenharmony_cistatic int translate_ipfs_to_http(URLContext *h, const char *uri, int flags, AVDictionary **options) 198cabdff1aSopenharmony_ci{ 199cabdff1aSopenharmony_ci const char *ipfs_cid; 200cabdff1aSopenharmony_ci char *fulluri = NULL; 201cabdff1aSopenharmony_ci int ret; 202cabdff1aSopenharmony_ci IPFSGatewayContext *c = h->priv_data; 203cabdff1aSopenharmony_ci 204cabdff1aSopenharmony_ci // Test for ipfs://, ipfs:, ipns:// and ipns:. This prefix is stripped from 205cabdff1aSopenharmony_ci // the string leaving just the CID in ipfs_cid. 206cabdff1aSopenharmony_ci int is_ipfs = av_stristart(uri, "ipfs://", &ipfs_cid); 207cabdff1aSopenharmony_ci int is_ipns = av_stristart(uri, "ipns://", &ipfs_cid); 208cabdff1aSopenharmony_ci 209cabdff1aSopenharmony_ci // We must have either ipns or ipfs. 210cabdff1aSopenharmony_ci if (!is_ipfs && !is_ipns) { 211cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 212cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, "Unsupported url %s\n", uri); 213cabdff1aSopenharmony_ci goto err; 214cabdff1aSopenharmony_ci } 215cabdff1aSopenharmony_ci 216cabdff1aSopenharmony_ci // If the CID has a length greater then 0 then we assume we have a proper working one. 217cabdff1aSopenharmony_ci // It could still be wrong but in that case the gateway should save us and 218cabdff1aSopenharmony_ci // ruturn a 403 error. The http protocol handles this. 219cabdff1aSopenharmony_ci if (strlen(ipfs_cid) < 1) { 220cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, "A CID must be provided.\n"); 221cabdff1aSopenharmony_ci ret = AVERROR(EILSEQ); 222cabdff1aSopenharmony_ci goto err; 223cabdff1aSopenharmony_ci } 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci // Populate c->gateway_buffer with whatever is in c->gateway 226cabdff1aSopenharmony_ci if (c->gateway != NULL) { 227cabdff1aSopenharmony_ci if (snprintf(c->gateway_buffer, sizeof(c->gateway_buffer), "%s", 228cabdff1aSopenharmony_ci c->gateway) 229cabdff1aSopenharmony_ci >= sizeof(c->gateway_buffer)) { 230cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 231cabdff1aSopenharmony_ci "The -gateway parameter is too long. " 232cabdff1aSopenharmony_ci "We allow a max of %zu characters\n", 233cabdff1aSopenharmony_ci sizeof(c->gateway_buffer)); 234cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 235cabdff1aSopenharmony_ci goto err; 236cabdff1aSopenharmony_ci } 237cabdff1aSopenharmony_ci } else { 238cabdff1aSopenharmony_ci // Populate the IPFS gateway if we have any. 239cabdff1aSopenharmony_ci // If not, inform the user how to properly set one. 240cabdff1aSopenharmony_ci ret = populate_ipfs_gateway(h); 241cabdff1aSopenharmony_ci 242cabdff1aSopenharmony_ci if (ret < 1) { 243cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, 244cabdff1aSopenharmony_ci "IPFS does not appear to be running.\n\n" 245cabdff1aSopenharmony_ci "Installing IPFS locally is recommended to " 246cabdff1aSopenharmony_ci "improve performance and reliability, " 247cabdff1aSopenharmony_ci "and not share all your activity with a single IPFS gateway.\n" 248cabdff1aSopenharmony_ci "There are multiple options to define this gateway.\n" 249cabdff1aSopenharmony_ci "1. Call ffmpeg with a gateway param, " 250cabdff1aSopenharmony_ci "without a trailing slash: -gateway <url>.\n" 251cabdff1aSopenharmony_ci "2. Define an $IPFS_GATEWAY environment variable with the " 252cabdff1aSopenharmony_ci "full HTTP URL to the gateway " 253cabdff1aSopenharmony_ci "without trailing forward slash.\n" 254cabdff1aSopenharmony_ci "3. Define an $IPFS_PATH environment variable " 255cabdff1aSopenharmony_ci "and point it to the IPFS data path " 256cabdff1aSopenharmony_ci "- this is typically ~/.ipfs\n"); 257cabdff1aSopenharmony_ci ret = AVERROR(EINVAL); 258cabdff1aSopenharmony_ci goto err; 259cabdff1aSopenharmony_ci } 260cabdff1aSopenharmony_ci } 261cabdff1aSopenharmony_ci 262cabdff1aSopenharmony_ci // Test if the gateway starts with either http:// or https:// 263cabdff1aSopenharmony_ci if (av_stristart(c->gateway_buffer, "http://", NULL) == 0 264cabdff1aSopenharmony_ci && av_stristart(c->gateway_buffer, "https://", NULL) == 0) { 265cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, 266cabdff1aSopenharmony_ci "The gateway URL didn't start with http:// or " 267cabdff1aSopenharmony_ci "https:// and is therefore invalid.\n"); 268cabdff1aSopenharmony_ci ret = AVERROR(EILSEQ); 269cabdff1aSopenharmony_ci goto err; 270cabdff1aSopenharmony_ci } 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci // Concatenate the url. 273cabdff1aSopenharmony_ci // This ends up with something like: http://localhost:8080/ipfs/Qm..... 274cabdff1aSopenharmony_ci // The format of "%s%s%s%s" is the following: 275cabdff1aSopenharmony_ci // 1st %s = The gateway. 276cabdff1aSopenharmony_ci // 2nd %s = If the gateway didn't end in a slash, add a "/". Otherwise it's an empty string 277cabdff1aSopenharmony_ci // 3rd %s = Either ipns/ or ipfs/. 278cabdff1aSopenharmony_ci // 4th %s = The IPFS CID (Qm..., bafy..., ...). 279cabdff1aSopenharmony_ci fulluri = av_asprintf("%s%s%s%s", 280cabdff1aSopenharmony_ci c->gateway_buffer, 281cabdff1aSopenharmony_ci (c->gateway_buffer[strlen(c->gateway_buffer) - 1] == '/') ? "" : "/", 282cabdff1aSopenharmony_ci (is_ipns) ? "ipns/" : "ipfs/", 283cabdff1aSopenharmony_ci ipfs_cid); 284cabdff1aSopenharmony_ci 285cabdff1aSopenharmony_ci if (!fulluri) { 286cabdff1aSopenharmony_ci av_log(h, AV_LOG_ERROR, "Failed to compose the URL\n"); 287cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 288cabdff1aSopenharmony_ci goto err; 289cabdff1aSopenharmony_ci } 290cabdff1aSopenharmony_ci 291cabdff1aSopenharmony_ci // Pass the URL back to FFMpeg's protocol handler. 292cabdff1aSopenharmony_ci ret = ffurl_open_whitelist(&c->inner, fulluri, flags, 293cabdff1aSopenharmony_ci &h->interrupt_callback, options, 294cabdff1aSopenharmony_ci h->protocol_whitelist, 295cabdff1aSopenharmony_ci h->protocol_blacklist, h); 296cabdff1aSopenharmony_ci if (ret < 0) { 297cabdff1aSopenharmony_ci av_log(h, AV_LOG_WARNING, "Unable to open resource: %s\n", fulluri); 298cabdff1aSopenharmony_ci goto err; 299cabdff1aSopenharmony_ci } 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_cierr: 302cabdff1aSopenharmony_ci av_free(fulluri); 303cabdff1aSopenharmony_ci return ret; 304cabdff1aSopenharmony_ci} 305cabdff1aSopenharmony_ci 306cabdff1aSopenharmony_cistatic int ipfs_read(URLContext *h, unsigned char *buf, int size) 307cabdff1aSopenharmony_ci{ 308cabdff1aSopenharmony_ci IPFSGatewayContext *c = h->priv_data; 309cabdff1aSopenharmony_ci return ffurl_read(c->inner, buf, size); 310cabdff1aSopenharmony_ci} 311cabdff1aSopenharmony_ci 312cabdff1aSopenharmony_cistatic int64_t ipfs_seek(URLContext *h, int64_t pos, int whence) 313cabdff1aSopenharmony_ci{ 314cabdff1aSopenharmony_ci IPFSGatewayContext *c = h->priv_data; 315cabdff1aSopenharmony_ci return ffurl_seek(c->inner, pos, whence); 316cabdff1aSopenharmony_ci} 317cabdff1aSopenharmony_ci 318cabdff1aSopenharmony_cistatic int ipfs_close(URLContext *h) 319cabdff1aSopenharmony_ci{ 320cabdff1aSopenharmony_ci IPFSGatewayContext *c = h->priv_data; 321cabdff1aSopenharmony_ci return ffurl_closep(&c->inner); 322cabdff1aSopenharmony_ci} 323cabdff1aSopenharmony_ci 324cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(IPFSGatewayContext, x) 325cabdff1aSopenharmony_ci 326cabdff1aSopenharmony_cistatic const AVOption options[] = { 327cabdff1aSopenharmony_ci {"gateway", "The gateway to ask for IPFS data.", OFFSET(gateway), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM}, 328cabdff1aSopenharmony_ci {NULL}, 329cabdff1aSopenharmony_ci}; 330cabdff1aSopenharmony_ci 331cabdff1aSopenharmony_cistatic const AVClass ipfs_context_class = { 332cabdff1aSopenharmony_ci .class_name = "IPFS", 333cabdff1aSopenharmony_ci .item_name = av_default_item_name, 334cabdff1aSopenharmony_ci .option = options, 335cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 336cabdff1aSopenharmony_ci}; 337cabdff1aSopenharmony_ci 338cabdff1aSopenharmony_ciconst URLProtocol ff_ipfs_protocol = { 339cabdff1aSopenharmony_ci .name = "ipfs", 340cabdff1aSopenharmony_ci .url_open2 = translate_ipfs_to_http, 341cabdff1aSopenharmony_ci .url_read = ipfs_read, 342cabdff1aSopenharmony_ci .url_seek = ipfs_seek, 343cabdff1aSopenharmony_ci .url_close = ipfs_close, 344cabdff1aSopenharmony_ci .priv_data_size = sizeof(IPFSGatewayContext), 345cabdff1aSopenharmony_ci .priv_data_class = &ipfs_context_class, 346cabdff1aSopenharmony_ci}; 347cabdff1aSopenharmony_ci 348cabdff1aSopenharmony_ciconst URLProtocol ff_ipns_protocol = { 349cabdff1aSopenharmony_ci .name = "ipns", 350cabdff1aSopenharmony_ci .url_open2 = translate_ipfs_to_http, 351cabdff1aSopenharmony_ci .url_read = ipfs_read, 352cabdff1aSopenharmony_ci .url_seek = ipfs_seek, 353cabdff1aSopenharmony_ci .url_close = ipfs_close, 354cabdff1aSopenharmony_ci .priv_data_size = sizeof(IPFSGatewayContext), 355cabdff1aSopenharmony_ci .priv_data_class = &ipfs_context_class, 356cabdff1aSopenharmony_ci}; 357