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:
- Add 127.0.0.1 before the original URL and use the local proxy
- 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:
- 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
- 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.