background

Ffmpeg has its own set of network IO code, but relatively simple, now you need to use the self-developed network library to take over ffMPEG network IO function. There are two ways to do this:

  1. Add 127.0.0.1 before the original URL and use the local proxy
  2. Add your own protocol instead of HTTP

The first method is relatively simple, this article mainly introduces the second method. Let’s first introduce the libavFormat framework, and then introduce how to add a custom protocol to ffMPEG.

Libavformat protocol framework

The relevant documents

libavformat/protocol_list.c
libavformat/protocols.c
libavformat/avio.c
Copy the code

C is executing the code generated dynamically by./configure based on the parameters. The default parameter generates the following code:

static const URLProtocol *url_protocols[] = {
    ...
    &ff_hls_protocol,
    &ff_http_protocol,
    &ff_httpproxy_protocol,
    &ff_rtmp_protocol,
    &ff_rtmpt_protocol,
    &ff_rtp_protocol,
    &ff_srtp_protocol,
    &ff_tcp_protocol,
    &ff_udp_protocol,
    &ff_unix_protocol,
    NULL };
Copy the code

Url_protocols is a static array that stores implementations of common protocols such as HTTP, TCP, UDP, and RTMP. Make a link unrelated to this article to explain all the protocols ffMPEG supports

The key URLProtocol now appears

typedef struct URLProtocol {
    const char *name;
    int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
    int     (*url_read)( URLContext *h, unsigned char *buf, int size);
    int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
    int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
    int     (*url_shutdown)(URLContext *h, int flags);
    int     (*url_close)(URLContext *h);    
    int priv_data_size;
    const AVClass *priv_data_class;
    intflags; . } URLProtocol;Copy the code

The URLProtocol can be understood as a generic interface. Each protocol in avFormat implements the functions of interest in the URLProtocol. Here THE URLProtocol is a technique that implements C++ polymorphism with function Pointers in C language.

Let’s take a quick look at the HTTP protocol. HTTP is an application-layer protocol that relies on the transport layer protocol at the next layer to transmit binary data. So, when a user requests a resource using HTTP or HTTPS, how does FFMPEG find the specific protocol processing code? The libavformat/avio. C

static const struct URLProtocol *url_find_protocol(const char *filename)
{...for (i = 0; protocols[i]; i++) {
         const URLProtocol *up = protocols[i];
        if (!strcmp(proto_str, up->name)) { // Match by name field in URLProtocol
            av_freep(&protocols);
            returnup; }}...return NULL;
}
Copy the code

Here, look at the protocol registration code in libavformat/http.c, and it is clear that the name field of the HTTP protocol is HTTP

const URLProtocol ff_http_protocol = {
    .name                = "http",
    .url_open2           = http_open,
    .url_accept          = http_accept,
    .url_handshake       = http_handshake,
    .url_read            = http_read,
    .url_write           = http_write,
    .url_seek            = http_seek,
    .url_close           = http_close,
    .url_get_file_handle = http_get_file_handle,
    .url_get_short_seek  = http_get_short_seek,
    .url_shutdown        = http_shutdown,
    .priv_data_size      = sizeof(HTTPContext),
    .priv_data_class     = &http_context_class,
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
    .default_whitelist   = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
};
Copy the code

The same goes for the TCP protocol in libavFormat /tcp.c

const URLProtocol ff_tcp_protocol = {
    .name                = "tcp",
    .url_open            = tcp_open,
    .url_accept          = tcp_accept,
    .url_read            = tcp_read,
    .url_write           = tcp_write,
    .url_close           = tcp_close,
    .url_get_file_handle = tcp_get_file_handle,
    .url_get_short_seek  = tcp_get_window_size,
    .url_shutdown        = tcp_shutdown,
    .priv_data_size      = sizeof(TCPContext),
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
    .priv_data_class     = &tcp_class,
};
Copy the code

When the request is made, url_find_protocol finds the handler function of the HTTP protocol by matching the character HTTP

http://1.2.3.4/stream-123.flv
Copy the code

The http_open_cnx_internal function in libavformat/http.c determines the protocol of the next transport layer based on HTTP or HTTPS in the request URL

static int http_open_cnx_internal(URLContext *h, AVDictionary **options) { const char *lower_proto = "tcp"; // By default, TCP is used to transmit if (! strcmp(proto, "https")) { lower_proto = "tls"; Use_proxy = 0; // The application layer uses HTTPS and the transport layer uses TLS. if (port < 0) port = 443; } if (port < 0) port = 80; Ff_url_join (buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); }Copy the code

Is formed by concatenation of ff_URL_JOIN

TCP: / / 116.162.86.92:80Copy the code

Then call the ffurl_open_whitelist function in avio.c again, and then use the url_find_protocol function to find the TCP protocol handler.

Added custom live protocol to FFMPEG

The steps to add a custom Live protocol are as follows:

  1. Create a new live. C file to implement the functions of interest in URLProtocol. The code is as follows
class FFLiveProtocol { public: static int live_open(URLContext *h, const char *url, int flags, AVDictionary **options) { return 0; } static int live_read(URLContext *h, unsigned char *buf, int size) { return 0; } static int live_write(URLContext *h, const unsigned char *buf, int size) { return 0; } static int live_close(URLContext *h) { return 0; }}; URLProtocol g_ff_live_protocol = { .name = "live", .url_open2 = FFLiveProtocol::live_open, .url_read = FFLiveProtocol::live_read, .url_write = FFLiveProtocol::live_write, .url_close = FFLiveProtocol::live_close, .priv_data_size = sizeof(FF_LiveContext), .flags = URL_PROTOCOL_FLAG_NETWORK, .priv_data_class = &live_class, };Copy the code
  1. Enable live in the./configure parameter option

Check out the help first:

# ./configure --help |egrep "enable-protocol"
  --enable-protocol=NAME   enable protocol NAME
Copy the code

Then the new parameters are as follows:

--enable-protocol=live
Copy the code

After completing these two steps, you can change the original URL to

Live: / / 2 / stream - 123. FLVCopy the code

At this point, the protocol has been replaced. The implementation of the live protocol is up to you.