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

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

相关推荐
superconvert12 小时前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
cuijiecheng201818 小时前
音视频入门基础:AAC专题(8)——FFmpeg源码中计算AAC裸流AVStream的time_base的实现
ffmpeg·音视频·aac
0点51 胜18 小时前
[ffmpeg] 视频格式转换
ffmpeg
Jerry 二河小鱼1 天前
在Linux中安装FFmpeg
linux·运维·服务器·ffmpeg
0点51 胜2 天前
[ffmpeg] 音视频编码
ffmpeg·音视频
0点51 胜2 天前
[ffmpeg]音频格式转换
开发语言·c++·ffmpeg
PlumCarefree3 天前
基于鸿蒙API10的RTSP播放器(五:拖动底部视频滑轨实现跳转)
华为·ffmpeg·音视频
LuckyInn3 天前
从安装ffmpeg开始,把一个视频按照每秒30帧fps剪切为图片
ffmpeg·音视频
cuijiecheng20183 天前
FFmpeg源码:skip_bits、skip_bits1、show_bits函数分析
ffmpeg
__Destiny__3 天前
视频格式转为mp4(使用ffmpeg)
ffmpeg·视频编解码