diff mbox series

[FFmpeg-devel,1/5] Early version of IPFS protocol support.

Message ID 20220131135116.14035-2-markg85@gmail.com
State New
Headers show
Series Add IPFS and IPNS protocol support | expand

Checks

Context Check Description
andriy/commit_msg_x86 warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
andriy/make_x86 fail Make failed
andriy/commit_msg_ppc warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
andriy/make_ppc fail Make failed

Commit Message

Mark Gaiser Jan. 31, 2022, 1:51 p.m. UTC
Signed-off-by: Mark Gaiser <markg85@gmail.com>
---
 configure               |   1 +
 doc/protocols.texi      |  30 ++++++
 libavformat/Makefile    |   1 +
 libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
 libavformat/protocols.c |   2 +
 5 files changed, 236 insertions(+)
 create mode 100644 libavformat/ipfs.c

Comments

Michael Niedermayer Jan. 31, 2022, 3:59 p.m. UTC | #1
On Mon, Jan 31, 2022 at 02:51:12PM +0100, Mark Gaiser wrote:
> Signed-off-by: Mark Gaiser <markg85@gmail.com>
> ---
>  configure               |   1 +
>  doc/protocols.texi      |  30 ++++++
>  libavformat/Makefile    |   1 +
>  libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   2 +
>  5 files changed, 236 insertions(+)
>  create mode 100644 libavformat/ipfs.c
> 
> diff --git a/configure b/configure
> index 5b19a35f59..e466f924a3 100755
> --- a/configure
> +++ b/configure
> @@ -3585,6 +3585,7 @@ udp_protocol_select="network"
>  udplite_protocol_select="network"
>  unix_protocol_deps="sys_un_h"
>  unix_protocol_select="network"
> +ipfs_protocol_select="https_protocol"
>  
>  # external library protocols
>  libamqp_protocol_deps="librabbitmq"
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index d207df0b52..7c9c0a4808 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -2025,5 +2025,35 @@ decoding errors.
>  
>  @end table
>  
> +@section ipfs
> +
> +InterPlanetary File System (IPFS) protocol support. One can access files stored 
> +on the IPFS network through so called gateways. Those are http(s) endpoints.
> +This protocol wraps the IPFS native protocols (ipfs:// and ipns://) to be send 
> +to such a gateway. Users can (and should) host their own node which means this 
> +protocol will use your local machine gateway to access files on the IPFS network.
> +
> +If a user doesn't have a node of their own then the public gateway dweb.link is 
> +used by default.
> +
> +You can use this protocol in 2 ways. Using IPFS:
> +@example
> +ffplay ipfs://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> +@end example
> +
> +Or the IPNS protocol (IPNS is mutable IPFS):
> +@example
> +ffplay ipns://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> +@end example
> +
> +You can also change the gateway to be used:
> +
> +@table @option
> +
> +@item gateway
> +Defines the gateway to use. When nothing is provided the protocol will first try 
> +your local gateway. If that fails dweb.link will be used.
> +
> +@end table
>  
>  @c man end PROTOCOLS
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 3dc6a479cc..983a77f4f2 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -656,6 +656,7 @@ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
>  OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
>  OBJS-$(CONFIG_TEE_PROTOCOL)              += teeproto.o tee_common.o
>  OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
> +OBJS-$(CONFIG_IPFS_PROTOCOL)             += ipfs.o
>  TLS-OBJS-$(CONFIG_GNUTLS)                += tls_gnutls.o
>  TLS-OBJS-$(CONFIG_LIBTLS)                += tls_libtls.o
>  TLS-OBJS-$(CONFIG_MBEDTLS)               += tls_mbedtls.o
> diff --git a/libavformat/ipfs.c b/libavformat/ipfs.c
> new file mode 100644
> index 0000000000..4cc65750ed
> --- /dev/null
> +++ b/libavformat/ipfs.c
> @@ -0,0 +1,202 @@
> +/*
> + * IPFS protocol.
> + * Copyright (c) 2021 Mark Gaiser
> + *
> + * 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/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/internal.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/tree.h"
> +#include "avformat.h"
> +#include <fcntl.h>
> +#if HAVE_IO_H
> +#include <io.h>
> +#endif
> +#if HAVE_UNISTD_H
> +#include <unistd.h>
> +#endif
> +#include <sys/stat.h>
> +#include <stdlib.h>
> +#include "os_support.h"
> +#include "url.h"
> +
> +typedef struct Context {
> +    AVClass *class;
> +    URLContext *inner;
> +    char *fulluri;
> +    char *gateway;
> +} Context;
> +
> +static int ipfs_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
> +{
> +    const char *gatewaysuffix;

> +    int ret = 0;

the initializuation seems redundant


> +    Context *c = h->priv_data;
> +    
> +    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
> +        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
> +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> +        ret = AVERROR(EINVAL);
> +        goto err;
> +    }
> +    
> +    char* ipfs_gateway = "https://ipfs.io/ipfs/";
> +    
> +    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
> +    
> +    strcpy(c->fulluri, ipfs_gateway);
> +    strcat(c->fulluri, gatewaysuffix);

malloc() should be av_malloc() unless there is some API interaction requiring otherwise
also all the  cpy/cat stuff should use "secure" size checking variants
but maybe something like av_asprintf() could simplify this all

[...]

thx
James Almer Jan. 31, 2022, 4:06 p.m. UTC | #2
On 1/31/2022 10:51 AM, Mark Gaiser wrote:
> Signed-off-by: Mark Gaiser <markg85@gmail.com>
> ---
>   configure               |   1 +
>   doc/protocols.texi      |  30 ++++++
>   libavformat/Makefile    |   1 +
>   libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
>   libavformat/protocols.c |   2 +
>   5 files changed, 236 insertions(+)
>   create mode 100644 libavformat/ipfs.c
> 
> diff --git a/configure b/configure
> index 5b19a35f59..e466f924a3 100755
> --- a/configure
> +++ b/configure
> @@ -3585,6 +3585,7 @@ udp_protocol_select="network"
>   udplite_protocol_select="network"
>   unix_protocol_deps="sys_un_h"
>   unix_protocol_select="network"
> +ipfs_protocol_select="https_protocol"
>   
>   # external library protocols
>   libamqp_protocol_deps="librabbitmq"
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index d207df0b52..7c9c0a4808 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -2025,5 +2025,35 @@ decoding errors.
>   
>   @end table
>   
> +@section ipfs
> +
> +InterPlanetary File System (IPFS) protocol support. One can access files stored
> +on the IPFS network through so called gateways. Those are http(s) endpoints.
> +This protocol wraps the IPFS native protocols (ipfs:// and ipns://) to be send
> +to such a gateway. Users can (and should) host their own node which means this
> +protocol will use your local machine gateway to access files on the IPFS network.
> +
> +If a user doesn't have a node of their own then the public gateway dweb.link is
> +used by default.
> +
> +You can use this protocol in 2 ways. Using IPFS:
> +@example
> +ffplay ipfs://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> +@end example
> +
> +Or the IPNS protocol (IPNS is mutable IPFS):
> +@example
> +ffplay ipns://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> +@end example
> +
> +You can also change the gateway to be used:
> +
> +@table @option
> +
> +@item gateway
> +Defines the gateway to use. When nothing is provided the protocol will first try
> +your local gateway. If that fails dweb.link will be used.
> +
> +@end table
>   
>   @c man end PROTOCOLS
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 3dc6a479cc..983a77f4f2 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -656,6 +656,7 @@ OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
>   OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
>   OBJS-$(CONFIG_TEE_PROTOCOL)              += teeproto.o tee_common.o
>   OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
> +OBJS-$(CONFIG_IPFS_PROTOCOL)             += ipfs.o
>   TLS-OBJS-$(CONFIG_GNUTLS)                += tls_gnutls.o
>   TLS-OBJS-$(CONFIG_LIBTLS)                += tls_libtls.o
>   TLS-OBJS-$(CONFIG_MBEDTLS)               += tls_mbedtls.o
> diff --git a/libavformat/ipfs.c b/libavformat/ipfs.c
> new file mode 100644
> index 0000000000..4cc65750ed
> --- /dev/null
> +++ b/libavformat/ipfs.c
> @@ -0,0 +1,202 @@
> +/*
> + * IPFS protocol.
> + * Copyright (c) 2021 Mark Gaiser
> + *
> + * 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/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/internal.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/tree.h"
> +#include "avformat.h"
> +#include <fcntl.h>
> +#if HAVE_IO_H
> +#include <io.h>
> +#endif
> +#if HAVE_UNISTD_H
> +#include <unistd.h>
> +#endif
> +#include <sys/stat.h>
> +#include <stdlib.h>
> +#include "os_support.h"
> +#include "url.h"
> +
> +typedef struct Context {
> +    AVClass *class;
> +    URLContext *inner;
> +    char *fulluri;
> +    char *gateway;
> +} Context;

A more descriptive name would be nice.

> +
> +static int ipfs_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
> +{
> +    const char *gatewaysuffix;
> +    int ret = 0;
> +    Context *c = h->priv_data;
> +
> +    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
> +        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
> +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> +        ret = AVERROR(EINVAL);
> +        goto err;

Just return ret. No need for this goto.

> +    }
> +
> +    char* ipfs_gateway = "https://ipfs.io/ipfs/";

const char*

> +
> +    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);

av_malloc()

Also, check the return value for allocation failure.

> +
> +    strcpy(c->fulluri, ipfs_gateway);
> +    strcat(c->fulluri, gatewaysuffix);
> +
> +    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,

Is fulluri going to be used after this call? If not, then it should be 
local to this function as there's no need to have it in h->priv_data 
(And for that matter, you're not freeing fulluri anywhere).

> +                                    &h->interrupt_callback, options,
> +                                    h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {

You need to define a whitelist including https in both URLProtocol below.

> +        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", c->fulluri);
> +        goto err;
> +    }
> +
> +err:
> +    return ret;
> +}
> +
> +static int ipfs_read(URLContext *h, unsigned char *buf, int size)
> +{
> +    Context *c = h->priv_data;
> +    int ret;
> +
> +    ret = ffurl_read(c->inner, buf, size);
> +
> +    return ret;
> +}
> +
> +static int64_t ipfs_seek(URLContext *h, int64_t pos, int whence)
> +{
> +    Context *c = h->priv_data;
> +    int64_t ret;
> +
> +    ret = ffurl_seek(c->inner, pos, whence);
> +
> +    return ret;
> +}
> +
> +static int ipfs_close(URLContext *h)
> +{
> +    Context *c = h->priv_data;
> +    int ret;
> +
> +    ret = ffurl_closep(&c->inner);
> +
> +    return ret;
> +}
> +
> +static int ipns_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
> +{
> +    const char *gatewaysuffix;
> +    int ret = 0;
> +    Context *c = h->priv_data;
> +
> +    if (!av_strstart(uri, "ipns://", &gatewaysuffix) &&
> +        !av_strstart(uri, "ipns:", &gatewaysuffix)) {
> +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> +        ret = AVERROR(EINVAL);
> +        goto err;
> +    }
> +
> +    char* ipfs_gateway = "https://ipfs.io/ipns/";
> +
> +    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
> +
> +    strcpy(c->fulluri, ipfs_gateway);
> +    strcat(c->fulluri, gatewaysuffix);
> +
> +    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,
> +                                    &h->interrupt_callback, options,
> +                                    h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
> +        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", c->fulluri);
> +        goto err;
> +    }
> +
> +err:
> +    return ret;

Same comments as above apply here.

> +}
> +
> +static int ipns_read(URLContext *h, unsigned char *buf, int size)
> +{
> +    Context *c = h->priv_data;
> +    int ret;
> +
> +    ret = ffurl_read(c->inner, buf, size);
> +
> +    return ret;
> +}
> +
> +static int64_t ipns_seek(URLContext *h, int64_t pos, int whence)
> +{
> +    Context *c = h->priv_data;
> +    int64_t ret;
> +
> +    ret = ffurl_seek(c->inner, pos, whence);
> +
> +    return ret;
> +}
> +
> +static int ipns_close(URLContext *h)
> +{
> +    Context *c = h->priv_data;
> +    int ret;
> +
> +    ret = ffurl_closep(&c->inner);
> +
> +    return ret;
> +}
> +
> +#define OFFSET(x) offsetof(Context, x)
> +#define D AV_OPT_FLAG_DECODING_PARAM
> +
> +static const AVOption options[] = {
> +    {"gateway", "The gateway to ask for IPFS data.", OFFSET(gateway), AV_OPT_TYPE_BINARY, .flags = D },
> +    {NULL},
> +};
> +
> +static const AVClass ipfs_context_class = {
> +    .class_name = "IPFS",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const URLProtocol ff_ipfs_protocol = {
> +    .name                = "ipfs",
> +    .url_open2           = ipfs_open,
> +    .url_read            = ipfs_read,
> +    .url_seek            = ipfs_seek,
> +    .url_close           = ipfs_close,
> +    .priv_data_size      = sizeof(Context),
> +    .priv_data_class     = &ipfs_context_class,
> +};
> +
> +const URLProtocol ff_infs_protocol = {
> +    .name                = "ipns",
> +    .url_open2           = ipfs_open,
> +    .url_read            = ipfs_read,
> +    .url_seek            = ipfs_seek,
> +    .url_close           = ipfs_close,
> +    .priv_data_size      = sizeof(Context),
> +    .priv_data_class     = &ipfs_context_class,
> +};
> diff --git a/libavformat/protocols.c b/libavformat/protocols.c
> index 948fae411f..675b684bd3 100644
> --- a/libavformat/protocols.c
> +++ b/libavformat/protocols.c
> @@ -73,6 +73,8 @@ extern const URLProtocol ff_libsrt_protocol;
>   extern const URLProtocol ff_libssh_protocol;
>   extern const URLProtocol ff_libsmbclient_protocol;
>   extern const URLProtocol ff_libzmq_protocol;
> +extern const URLProtocol ff_ipfs_protocol;
> +extern const URLProtocol ff_ipns_protocol;
>   
>   #include "libavformat/protocol_list.c"
>
Mark Gaiser Jan. 31, 2022, 4:34 p.m. UTC | #3
On Mon, Jan 31, 2022 at 5:06 PM James Almer <jamrial@gmail.com> wrote:

>
>
> On 1/31/2022 10:51 AM, Mark Gaiser wrote:
> > Signed-off-by: Mark Gaiser <markg85@gmail.com>
> > ---
> >   configure               |   1 +
> >   doc/protocols.texi      |  30 ++++++
> >   libavformat/Makefile    |   1 +
> >   libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
> >   libavformat/protocols.c |   2 +
> >   5 files changed, 236 insertions(+)
> >   create mode 100644 libavformat/ipfs.c
> >
> > diff --git a/configure b/configure
> > index 5b19a35f59..e466f924a3 100755
> > --- a/configure
> > +++ b/configure
> > @@ -3585,6 +3585,7 @@ udp_protocol_select="network"
> >   udplite_protocol_select="network"
> >   unix_protocol_deps="sys_un_h"
> >   unix_protocol_select="network"
> > +ipfs_protocol_select="https_protocol"
> >
> >   # external library protocols
> >   libamqp_protocol_deps="librabbitmq"
> > diff --git a/doc/protocols.texi b/doc/protocols.texi
> > index d207df0b52..7c9c0a4808 100644
> > --- a/doc/protocols.texi
> > +++ b/doc/protocols.texi
> > @@ -2025,5 +2025,35 @@ decoding errors.
> >
> >   @end table
> >
> > +@section ipfs
> > +
> > +InterPlanetary File System (IPFS) protocol support. One can access
> files stored
> > +on the IPFS network through so called gateways. Those are http(s)
> endpoints.
> > +This protocol wraps the IPFS native protocols (ipfs:// and ipns://) to
> be send
> > +to such a gateway. Users can (and should) host their own node which
> means this
> > +protocol will use your local machine gateway to access files on the
> IPFS network.
> > +
> > +If a user doesn't have a node of their own then the public gateway
> dweb.link is
> > +used by default.
> > +
> > +You can use this protocol in 2 ways. Using IPFS:
> > +@example
> > +ffplay ipfs://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> > +@end example
> > +
> > +Or the IPNS protocol (IPNS is mutable IPFS):
> > +@example
> > +ffplay ipns://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
> > +@end example
> > +
> > +You can also change the gateway to be used:
> > +
> > +@table @option
> > +
> > +@item gateway
> > +Defines the gateway to use. When nothing is provided the protocol will
> first try
> > +your local gateway. If that fails dweb.link will be used.
> > +
> > +@end table
> >
> >   @c man end PROTOCOLS
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index 3dc6a479cc..983a77f4f2 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -656,6 +656,7 @@ OBJS-$(CONFIG_SRTP_PROTOCOL)             +=
> srtpproto.o srtp.o
> >   OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
> >   OBJS-$(CONFIG_TEE_PROTOCOL)              += teeproto.o tee_common.o
> >   OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
> > +OBJS-$(CONFIG_IPFS_PROTOCOL)             += ipfs.o
> >   TLS-OBJS-$(CONFIG_GNUTLS)                += tls_gnutls.o
> >   TLS-OBJS-$(CONFIG_LIBTLS)                += tls_libtls.o
> >   TLS-OBJS-$(CONFIG_MBEDTLS)               += tls_mbedtls.o
> > diff --git a/libavformat/ipfs.c b/libavformat/ipfs.c
> > new file mode 100644
> > index 0000000000..4cc65750ed
> > --- /dev/null
> > +++ b/libavformat/ipfs.c
> > @@ -0,0 +1,202 @@
> > +/*
> > + * IPFS protocol.
> > + * Copyright (c) 2021 Mark Gaiser
> > + *
> > + * 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/avassert.h"
> > +#include "libavutil/avstring.h"
> > +#include "libavutil/internal.h"
> > +#include "libavutil/opt.h"
> > +#include "libavutil/tree.h"
> > +#include "avformat.h"
> > +#include <fcntl.h>
> > +#if HAVE_IO_H
> > +#include <io.h>
> > +#endif
> > +#if HAVE_UNISTD_H
> > +#include <unistd.h>
> > +#endif
> > +#include <sys/stat.h>
> > +#include <stdlib.h>
> > +#include "os_support.h"
> > +#include "url.h"
> > +
> > +typedef struct Context {
> > +    AVClass *class;
> > +    URLContext *inner;
> > +    char *fulluri;
> > +    char *gateway;
> > +} Context;
>
> A more descriptive name would be nice.
>
> > +
> > +static int ipfs_open(URLContext *h, const char *uri, int flags,
> AVDictionary **options)
> > +{
> > +    const char *gatewaysuffix;
> > +    int ret = 0;
> > +    Context *c = h->priv_data;
> > +
> > +    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
> > +        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
> > +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> > +        ret = AVERROR(EINVAL);
> > +        goto err;
>
> Just return ret. No need for this goto.
>
> > +    }
> > +
> > +    char* ipfs_gateway = "https://ipfs.io/ipfs/";
>
> const char*
>
> > +
> > +    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
>
> av_malloc()
>
> Also, check the return value for allocation failure.
>
> > +
> > +    strcpy(c->fulluri, ipfs_gateway);
> > +    strcat(c->fulluri, gatewaysuffix);
> > +
> > +    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,
>
> Is fulluri going to be used after this call? If not, then it should be
> local to this function as there's no need to have it in h->priv_data
> (And for that matter, you're not freeing fulluri anywhere).
>
> > +                                    &h->interrupt_callback, options,
> > +                                    h->protocol_whitelist,
> h->protocol_blacklist, h)) < 0) {
>
> You need to define a whitelist including https in both URLProtocol below.
>
> > +        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n",
> c->fulluri);
> > +        goto err;
> > +    }
> > +
> > +err:
> > +    return ret;
> > +}
> > +
> > +static int ipfs_read(URLContext *h, unsigned char *buf, int size)
> > +{
> > +    Context *c = h->priv_data;
> > +    int ret;
> > +
> > +    ret = ffurl_read(c->inner, buf, size);
> > +
> > +    return ret;
> > +}
> > +
> > +static int64_t ipfs_seek(URLContext *h, int64_t pos, int whence)
> > +{
> > +    Context *c = h->priv_data;
> > +    int64_t ret;
> > +
> > +    ret = ffurl_seek(c->inner, pos, whence);
> > +
> > +    return ret;
> > +}
> > +
> > +static int ipfs_close(URLContext *h)
> > +{
> > +    Context *c = h->priv_data;
> > +    int ret;
> > +
> > +    ret = ffurl_closep(&c->inner);
> > +
> > +    return ret;
> > +}
> > +
> > +static int ipns_open(URLContext *h, const char *uri, int flags,
> AVDictionary **options)
> > +{
> > +    const char *gatewaysuffix;
> > +    int ret = 0;
> > +    Context *c = h->priv_data;
> > +
> > +    if (!av_strstart(uri, "ipns://", &gatewaysuffix) &&
> > +        !av_strstart(uri, "ipns:", &gatewaysuffix)) {
> > +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> > +        ret = AVERROR(EINVAL);
> > +        goto err;
> > +    }
> > +
> > +    char* ipfs_gateway = "https://ipfs.io/ipns/";
> > +
> > +    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
> > +
> > +    strcpy(c->fulluri, ipfs_gateway);
> > +    strcat(c->fulluri, gatewaysuffix);
> > +
> > +    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,
> > +                                    &h->interrupt_callback, options,
> > +                                    h->protocol_whitelist,
> h->protocol_blacklist, h)) < 0) {
> > +        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n",
> c->fulluri);
> > +        goto err;
> > +    }
> > +
> > +err:
> > +    return ret;
>
> Same comments as above apply here.
>

Awesome, thank you for your review time!
Will fix all those in V2.

>
> > +}
> > +
> > +static int ipns_read(URLContext *h, unsigned char *buf, int size)
> > +{
> > +    Context *c = h->priv_data;
> > +    int ret;
> > +
> > +    ret = ffurl_read(c->inner, buf, size);
> > +
> > +    return ret;
> > +}
> > +
> > +static int64_t ipns_seek(URLContext *h, int64_t pos, int whence)
> > +{
> > +    Context *c = h->priv_data;
> > +    int64_t ret;
> > +
> > +    ret = ffurl_seek(c->inner, pos, whence);
> > +
> > +    return ret;
> > +}
> > +
> > +static int ipns_close(URLContext *h)
> > +{
> > +    Context *c = h->priv_data;
> > +    int ret;
> > +
> > +    ret = ffurl_closep(&c->inner);
> > +
> > +    return ret;
> > +}
> > +
> > +#define OFFSET(x) offsetof(Context, x)
> > +#define D AV_OPT_FLAG_DECODING_PARAM
> > +
> > +static const AVOption options[] = {
> > +    {"gateway", "The gateway to ask for IPFS data.", OFFSET(gateway),
> AV_OPT_TYPE_BINARY, .flags = D },
> > +    {NULL},
> > +};
> > +
> > +static const AVClass ipfs_context_class = {
> > +    .class_name = "IPFS",
> > +    .item_name  = av_default_item_name,
> > +    .option     = options,
> > +    .version    = LIBAVUTIL_VERSION_INT,
> > +};
> > +
> > +const URLProtocol ff_ipfs_protocol = {
> > +    .name                = "ipfs",
> > +    .url_open2           = ipfs_open,
> > +    .url_read            = ipfs_read,
> > +    .url_seek            = ipfs_seek,
> > +    .url_close           = ipfs_close,
> > +    .priv_data_size      = sizeof(Context),
> > +    .priv_data_class     = &ipfs_context_class,
> > +};
> > +
> > +const URLProtocol ff_infs_protocol = {
> > +    .name                = "ipns",
> > +    .url_open2           = ipfs_open,
> > +    .url_read            = ipfs_read,
> > +    .url_seek            = ipfs_seek,
> > +    .url_close           = ipfs_close,
> > +    .priv_data_size      = sizeof(Context),
> > +    .priv_data_class     = &ipfs_context_class,
> > +};
> > diff --git a/libavformat/protocols.c b/libavformat/protocols.c
> > index 948fae411f..675b684bd3 100644
> > --- a/libavformat/protocols.c
> > +++ b/libavformat/protocols.c
> > @@ -73,6 +73,8 @@ extern const URLProtocol ff_libsrt_protocol;
> >   extern const URLProtocol ff_libssh_protocol;
> >   extern const URLProtocol ff_libsmbclient_protocol;
> >   extern const URLProtocol ff_libzmq_protocol;
> > +extern const URLProtocol ff_ipfs_protocol;
> > +extern const URLProtocol ff_ipns_protocol;
> >
> >   #include "libavformat/protocol_list.c"
> >
> _______________________________________________
> 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".
>
Lynne Jan. 31, 2022, 8:26 p.m. UTC | #4
Jan 31, 2022, 14:51 by markg85@gmail.com:

> Signed-off-by: Mark Gaiser <markg85@gmail.com>
> ---
>  configure               |   1 +
>  doc/protocols.texi      |  30 ++++++
>  libavformat/Makefile    |   1 +
>  libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   2 +
>  5 files changed, 236 insertions(+)
>  create mode 100644 libavformat/ipfs.c
>
> +
> +static int ipfs_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
> +{
> +    const char *gatewaysuffix;
> +    int ret = 0;
> +    Context *c = h->priv_data;
> + 
> +    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
> +        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
> +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> +        ret = AVERROR(EINVAL);
> +        goto err;
> +    }
> + 
> +    char* ipfs_gateway = "https://ipfs.io/ipfs/";
>

That's a no from me. I'd rather have native support rather
than depend on some third party service. Users can just convert
the link themselves if they want to. Surely the IPFS project
has libraries one could use instead.
Mark Gaiser Jan. 31, 2022, 10:04 p.m. UTC | #5
On Mon, Jan 31, 2022 at 9:26 PM Lynne <dev@lynne.ee> wrote:

> Jan 31, 2022, 14:51 by markg85@gmail.com:
>
> > Signed-off-by: Mark Gaiser <markg85@gmail.com>
> > ---
> >  configure               |   1 +
> >  doc/protocols.texi      |  30 ++++++
> >  libavformat/Makefile    |   1 +
> >  libavformat/ipfs.c      | 202 ++++++++++++++++++++++++++++++++++++++++
> >  libavformat/protocols.c |   2 +
> >  5 files changed, 236 insertions(+)
> >  create mode 100644 libavformat/ipfs.c
> >
> > +
> > +static int ipfs_open(URLContext *h, const char *uri, int flags,
> AVDictionary **options)
> > +{
> > +    const char *gatewaysuffix;
> > +    int ret = 0;
> > +    Context *c = h->priv_data;
> > +
> > +    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
> > +        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
> > +        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
> > +        ret = AVERROR(EINVAL);
> > +        goto err;
> > +    }
> > +
> > +    char* ipfs_gateway = "https://ipfs.io/ipfs/";
> >
>
> That's a no from me. I'd rather have native support rather
> than depend on some third party service. Users can just convert
> the link themselves if they want to. Surely the IPFS project
> has libraries one could use instead.
>

I'm sorry, that part isn't in the final version.
I had sent the patches as I made the implementation. This kinda made this
part (and a couple others) weird because a later patch gets rid of it.

I'll send the v2 revision as a single patch which will give a clear picture
of the changes. No fixed url is in it.

Just a little oops from a first time contributor :)


> _______________________________________________
> 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".
>
diff mbox series

Patch

diff --git a/configure b/configure
index 5b19a35f59..e466f924a3 100755
--- a/configure
+++ b/configure
@@ -3585,6 +3585,7 @@  udp_protocol_select="network"
 udplite_protocol_select="network"
 unix_protocol_deps="sys_un_h"
 unix_protocol_select="network"
+ipfs_protocol_select="https_protocol"
 
 # external library protocols
 libamqp_protocol_deps="librabbitmq"
diff --git a/doc/protocols.texi b/doc/protocols.texi
index d207df0b52..7c9c0a4808 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -2025,5 +2025,35 @@  decoding errors.
 
 @end table
 
+@section ipfs
+
+InterPlanetary File System (IPFS) protocol support. One can access files stored 
+on the IPFS network through so called gateways. Those are http(s) endpoints.
+This protocol wraps the IPFS native protocols (ipfs:// and ipns://) to be send 
+to such a gateway. Users can (and should) host their own node which means this 
+protocol will use your local machine gateway to access files on the IPFS network.
+
+If a user doesn't have a node of their own then the public gateway dweb.link is 
+used by default.
+
+You can use this protocol in 2 ways. Using IPFS:
+@example
+ffplay ipfs://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
+@end example
+
+Or the IPNS protocol (IPNS is mutable IPFS):
+@example
+ffplay ipns://QmbGtJg23skhvFmu9mJiePVByhfzu5rwo74MEkVDYAmF5T
+@end example
+
+You can also change the gateway to be used:
+
+@table @option
+
+@item gateway
+Defines the gateway to use. When nothing is provided the protocol will first try 
+your local gateway. If that fails dweb.link will be used.
+
+@end table
 
 @c man end PROTOCOLS
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 3dc6a479cc..983a77f4f2 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -656,6 +656,7 @@  OBJS-$(CONFIG_SRTP_PROTOCOL)             += srtpproto.o srtp.o
 OBJS-$(CONFIG_SUBFILE_PROTOCOL)          += subfile.o
 OBJS-$(CONFIG_TEE_PROTOCOL)              += teeproto.o tee_common.o
 OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
+OBJS-$(CONFIG_IPFS_PROTOCOL)             += ipfs.o
 TLS-OBJS-$(CONFIG_GNUTLS)                += tls_gnutls.o
 TLS-OBJS-$(CONFIG_LIBTLS)                += tls_libtls.o
 TLS-OBJS-$(CONFIG_MBEDTLS)               += tls_mbedtls.o
diff --git a/libavformat/ipfs.c b/libavformat/ipfs.c
new file mode 100644
index 0000000000..4cc65750ed
--- /dev/null
+++ b/libavformat/ipfs.c
@@ -0,0 +1,202 @@ 
+/*
+ * IPFS protocol.
+ * Copyright (c) 2021 Mark Gaiser
+ *
+ * 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/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/internal.h"
+#include "libavutil/opt.h"
+#include "libavutil/tree.h"
+#include "avformat.h"
+#include <fcntl.h>
+#if HAVE_IO_H
+#include <io.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <stdlib.h>
+#include "os_support.h"
+#include "url.h"
+
+typedef struct Context {
+    AVClass *class;
+    URLContext *inner;
+    char *fulluri;
+    char *gateway;
+} Context;
+
+static int ipfs_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+    const char *gatewaysuffix;
+    int ret = 0;
+    Context *c = h->priv_data;
+    
+    if (!av_strstart(uri, "ipfs://", &gatewaysuffix) &&
+        !av_strstart(uri, "ipfs:", &gatewaysuffix)) {
+        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
+        ret = AVERROR(EINVAL);
+        goto err;
+    }
+    
+    char* ipfs_gateway = "https://ipfs.io/ipfs/";
+    
+    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
+    
+    strcpy(c->fulluri, ipfs_gateway);
+    strcat(c->fulluri, gatewaysuffix);
+    
+    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,
+                                    &h->interrupt_callback, options,
+                                    h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
+        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", c->fulluri);
+        goto err;
+    }
+    
+err:
+    return ret;
+}
+
+static int ipfs_read(URLContext *h, unsigned char *buf, int size)
+{
+    Context *c = h->priv_data;
+    int ret;
+
+    ret = ffurl_read(c->inner, buf, size);
+
+    return ret;
+}
+
+static int64_t ipfs_seek(URLContext *h, int64_t pos, int whence)
+{
+    Context *c = h->priv_data;
+    int64_t ret;
+
+    ret = ffurl_seek(c->inner, pos, whence);
+
+    return ret;
+}
+
+static int ipfs_close(URLContext *h)
+{
+    Context *c = h->priv_data;
+    int ret;
+
+    ret = ffurl_closep(&c->inner);
+
+    return ret;
+}
+
+static int ipns_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
+{
+    const char *gatewaysuffix;
+    int ret = 0;
+    Context *c = h->priv_data;
+    
+    if (!av_strstart(uri, "ipns://", &gatewaysuffix) &&
+        !av_strstart(uri, "ipns:", &gatewaysuffix)) {
+        av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
+        ret = AVERROR(EINVAL);
+        goto err;
+    }
+    
+    char* ipfs_gateway = "https://ipfs.io/ipns/";
+    
+    c->fulluri = malloc(strlen(ipfs_gateway)+strlen(gatewaysuffix) + 1);
+    
+    strcpy(c->fulluri, ipfs_gateway);
+    strcat(c->fulluri, gatewaysuffix);
+    
+    if ((ret = ffurl_open_whitelist(&c->inner, c->fulluri, flags,
+                                    &h->interrupt_callback, options,
+                                    h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
+        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", c->fulluri);
+        goto err;
+    }
+    
+err:
+    return ret;
+}
+
+static int ipns_read(URLContext *h, unsigned char *buf, int size)
+{
+    Context *c = h->priv_data;
+    int ret;
+
+    ret = ffurl_read(c->inner, buf, size);
+
+    return ret;
+}
+
+static int64_t ipns_seek(URLContext *h, int64_t pos, int whence)
+{
+    Context *c = h->priv_data;
+    int64_t ret;
+
+    ret = ffurl_seek(c->inner, pos, whence);
+
+    return ret;
+}
+
+static int ipns_close(URLContext *h)
+{
+    Context *c = h->priv_data;
+    int ret;
+
+    ret = ffurl_closep(&c->inner);
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(Context, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+    {"gateway", "The gateway to ask for IPFS data.", OFFSET(gateway), AV_OPT_TYPE_BINARY, .flags = D },
+    {NULL},
+};
+
+static const AVClass ipfs_context_class = {
+    .class_name = "IPFS",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_ipfs_protocol = {
+    .name                = "ipfs",
+    .url_open2           = ipfs_open,
+    .url_read            = ipfs_read,
+    .url_seek            = ipfs_seek,
+    .url_close           = ipfs_close,
+    .priv_data_size      = sizeof(Context),
+    .priv_data_class     = &ipfs_context_class,
+};
+
+const URLProtocol ff_infs_protocol = {
+    .name                = "ipns",
+    .url_open2           = ipfs_open,
+    .url_read            = ipfs_read,
+    .url_seek            = ipfs_seek,
+    .url_close           = ipfs_close,
+    .priv_data_size      = sizeof(Context),
+    .priv_data_class     = &ipfs_context_class,
+};
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 948fae411f..675b684bd3 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -73,6 +73,8 @@  extern const URLProtocol ff_libsrt_protocol;
 extern const URLProtocol ff_libssh_protocol;
 extern const URLProtocol ff_libsmbclient_protocol;
 extern const URLProtocol ff_libzmq_protocol;
+extern const URLProtocol ff_ipfs_protocol;
+extern const URLProtocol ff_ipns_protocol;
 
 #include "libavformat/protocol_list.c"