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:输入型参数。要写入文件或网络流的字节数。

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

相关推荐
hunandede2 小时前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三:将AVFrame转换成AVPacket。视频编码,AVPacket 重要函数,结构体成员学习
c++·ffmpeg·音视频
橘子味的茶二2 小时前
SDL读取PCM音频
ffmpeg·音视频·pcm
EasyCVR19 小时前
EHOME视频平台EasyCVR视频融合平台使用OBS进行RTMP推流,WebRTC播放出现抖动、卡顿如何解决?
人工智能·算法·ffmpeg·音视频·webrtc·监控视频接入
简鹿办公20 小时前
使用 FFmpeg 进行音视频转换的相关命令行参数解释
ffmpeg·简鹿视频格式转换器·ffmpeg视频转换
EasyCVR1 天前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
runing_an_min1 天前
ffmpeg 视频滤镜:屏蔽边框杂色- fillborders
ffmpeg·音视频·fillborders
岁月小龙1 天前
如何让ffmpeg运行时从当前目录加载库,而不是从/lib64
ffmpeg·origin·ffprobe·rpath
行者记3 天前
ffmpeg命令——从wireshark包中的rtp包中分离h264
测试工具·ffmpeg·wireshark
EasyCVR3 天前
国标GB28181视频平台EasyCVR私有化视频平台工地防盗视频监控系统方案
运维·科技·ffmpeg·音视频·1024程序员节·监控视频接入
hypoqqq3 天前
使用ffmpeg播放rtsp视频流
ffmpeg