FFmpeg源码:retry_transfer_wrapper、ffurl_read2、ffurl_write2函数分析

一、retry_transfer_wrapper函数

retry_transfer_wrapper函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/avio.c中:

cpp 复制代码
static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
                                         const uint8_t *cbuf,
                                         int size, int size_min,
                                         int read)
{
    int ret, len;
    int fast_retries = 5;
    int64_t wait_since = 0;

    len = 0;
    while (len < size_min) {
        if (ff_check_interrupt(&h->interrupt_callback))
            return AVERROR_EXIT;
        ret = read ? h->prot->url_read (h, buf + len, size - len):
                     h->prot->url_write(h, cbuf + len, size - len);
        if (ret == AVERROR(EINTR))
            continue;
        if (h->flags & AVIO_FLAG_NONBLOCK)
            return ret;
        if (ret == AVERROR(EAGAIN)) {
            ret = 0;
            if (fast_retries) {
                fast_retries--;
            } else {
                if (h->rw_timeout) {
                    if (!wait_since)
                        wait_since = av_gettime_relative();
                    else if (av_gettime_relative() > wait_since + h->rw_timeout)
                        return AVERROR(EIO);
                }
                av_usleep(1000);
            }
        } else if (ret == AVERROR_EOF)
            return (len > 0) ? len : AVERROR_EOF;
        else if (ret < 0)
            return ret;
        if (ret) {
            fast_retries = FFMAX(fast_retries, 2);
            wait_since = 0;
        }
        len += ret;
    }
    return len;
}

该函数作用是:对本地媒体文件或网络流进行读取或写入。如果是读取,将读上来的数据保存到形参buf指向的缓冲区中;如果是写入,将形参cbuf指向的缓冲区中的数据写入到本地媒体文件或网络流中。

形参h:输入型参数。 指向一个url(Uniform Resource Locator)上下文结构体。

形参buf:如果是要将数据写入到本地媒体文件或网络流中,该值为NULL;如果是要读取本地媒体文件或网络流的数据,buf为输出型参数,指向"保存读上来的数据的缓冲区";

形参cbuf:如果是要读取本地媒体文件或网络流的数据,该值为NULL;如果是要将数据写入到本地媒体文件或网络流中,buf为输入型参数,指向"存放需要写入到文件或网络流中的数据的缓冲区"。

形参size:输入型参数。如果是要读取本地媒体文件或网络流的数据,size为要读取的字节数;如果是要将数据写入到本地媒体文件或网络流中,size为要写入文件的字节数。

形参size_min:输入型参数。值一般等于形参size的值。如果实际读取到或写入到文件的字节数小于size_min,会继续读取或写入,直到不小于size_min为止。

形参read:输入型参数。值为0表示是要将数据写入到本地媒体文件或网络流中;值为1表示是要读取本地媒体文件或网络流的数据。

返回值:返回一个非负数表示成功,此时返回实际读取到或实际写入文件的字节数;返回一个负数表示出错。

retry_transfer_wrapper函数内部的核心是下面语句,通过形参read的值来决定是执行h->prot->url_read还是h->prot->url_write。read的值为1会执行h->prot->url_read,表示是要读取本地媒体文件或网络流的数据;read的值为0时,会执行h->prot->url_write,表示是要将数据写入到本地媒体文件或网络流中:

cpp 复制代码
ret = read ? h->prot->url_read (h, buf + len, size - len):
             h->prot->url_write(h, cbuf + len, size - len);

下面分两种情况讨论:

(一)情况一:形参read的值为1

h->prot->url_read是函数指针。当形参read的值为1时,会执行语句h->prot->url_read (h, buf + len, size - len),调用不同的回调函数。h->prot->url_read指向的回调函数都有"读取"作用:

cpp 复制代码
typedef struct URLProtocol {
//...
    int     (*url_read)( URLContext *h, unsigned char *buf, int size);
//...
}

举几个例子:

1.当执行命令:ffmpeg -i XXX.wav 获取本地媒体文件(比如wav音频文件)的信息时,FFmpeg会调用retry_transfer_wrapper函数。该函数内部执行h->prot->url_read语句时会调用libavformat/file.c中的file_read函数读取本地媒体文件的内容。关于file_read函数的用法可以参考:《FFmpeg源码:file_read、file_write函数分析》。

2.当执行命令:ffmpeg -i "rtsp:XXX" 获取rtsp流的信息时,FFmpeg会调用retry_transfer_wrapper函数。该函数内部执行h->prot->url_read语句时会调用libavformat/tcp.c中的tcp_read函数从TCP连接的另一端接收数据。

3.推流端执行FFmpeg命令:ffmpeg.exe -re -i XXX.mp4 -vcodec copy -acodec copy -f rtp_mpegts rtp://127.0.0.1:6005,拉流端通过:ffmpeg -i rtp://127.0.0.1:6005获取该基于RTP协议的MPEG-2传输流(MPEG-2 Transport Stream)的信息时,拉流端的FFmpeg会调用retry_transfer_wrapper函数。该函数内部执行h->prot->url_read语句时会调用libavformat/rtpproto.c中的rtp_read函数接收UDP数据。

(二)情况二:形参read的值为0

h->prot->url_write是函数指针。当形参read的值为0时,会执行语句h->prot->url_write(h, cbuf + len, size - len),调用不同的回调函数。h->prot->url_write指向的回调函数都有"写入"作用:

cpp 复制代码
typedef struct URLProtocol {
//...
    int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
//...
}

举个例子:

当执行命令:./ffmpeg -ar 44100 -ac 2 -f s16le -acodec pcm_s16le -i XXX.pcm XXX.wav 将某个PCM文件转为WAV格式的音频文件时,FFmpeg会调用retry_transfer_wrapper函数。该函数内部执行h->prot->url_write语句时会调用libavformat/file.c中的file_write函数将数据写入到本地媒体文件(WAV格式的音频文件)。关于file_write函数的用法可以参考:《FFmpeg源码:file_read、file_write函数分析》。

二、ffurl_read2函数

ffurl_read2函数定义在源文件libavformat/avio.c中:

cpp 复制代码
int ffurl_read2(void *urlcontext, uint8_t *buf, int size)
{
    URLContext *h = urlcontext;

    if (!(h->flags & AVIO_FLAG_READ))
        return AVERROR(EIO);
    return retry_transfer_wrapper(h, buf, NULL, size, 1, 1);
}

该函数作用是:对本地媒体文件或网络流进行读取。可以看到该函数内部调用了retry_transfer_wrapper函数。

形参urlcontext:输入型参数。 指向一个url(Uniform Resource Locator)上下文结构体。

形参buf:输出型参数。保存读上来的数据的缓冲区。

形参size:输入型参数。要读取的字节数。

返回值:返回一个非负数表示成功,此时返回实际读取到的字节数;返回一个负数表示出错。

三、ffurl_write2函数

ffurl_write2函数定义在源文件libavformat/avio.c中:

cpp 复制代码
int ffurl_write2(void *urlcontext, const uint8_t *buf, int size)
{
    URLContext *h = urlcontext;

    if (!(h->flags & AVIO_FLAG_WRITE))
        return AVERROR(EIO);
    /* avoid sending too big packets */
    if (h->max_packet_size && size > h->max_packet_size)
        return AVERROR(EIO);

    return retry_transfer_wrapper(h, NULL, buf, size, size, 0);
}

该函数作用是:对本地媒体文件或网络流进行写入。可以看到该函数内部调用了retry_transfer_wrapper函数。

形参urlcontext:输入型参数。 指向一个url(Uniform Resource Locator)上下文结构体。

形参buf:输入型参数。存放需要写入到文件或网络流中的数据的缓冲区。

形参size:输入型参数。要写入文件或网络流的字节数。

返回值:返回一个非负数表示成功,此时返回实际读写入文件或网络流的字节数;返回一个负数表示出错。

相关推荐
runing_an_min17 分钟前
ffmpeg视频滤镜:提取缩略图-framestep
ffmpeg·音视频·framestep
韩曙亮13 小时前
【FFmpeg】FFmpeg 内存结构 ③ ( AVPacket 函数简介 | av_packet_ref 函数 | av_packet_clone 函数 )
ffmpeg·音视频·avpacket·av_packet_clone·av_packet_ref·ffmpeg内存结构
oushaojun217 小时前
ubuntu中使用ffmpeg和nginx推流rtmp视频
nginx·ubuntu·ffmpeg·rtmp
莫固执,朋友18 小时前
网络抓包工具tcpdump 在海思平台上的编译使用
网络·ffmpeg·音视频·tcpdump
lxkj_202418 小时前
修改ffmpeg实现https-flv内容加密
网络协议·https·ffmpeg
cuijiecheng201818 小时前
音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
ffmpeg·音视频
VisionX Lab1 天前
数据脱敏工具:基于 FFmpeg 的视频批量裁剪
python·ffmpeg·音视频
柳鲲鹏2 天前
全网首发:Ubuntu编译跨平台嵌入式支持ffmpeg的OpenCV
linux·ubuntu·ffmpeg
冰山一脚20132 天前
ffplay音频SDL播放处理
ffmpeg
cuijiecheng20182 天前
音视频入门基础:MPEG2-TS专题(7)——FFmpeg源码中,读取出一个transport packet数据的实现
ffmpeg·音视频