ffmpeg中avformat_new_stream的用法

本文深入解析了FFmpeg中的AVStream结构,介绍了其在音视频处理中的关键作用,特别是与AVCodecParameters和AVRationaltime_base的关系。同时,详细解释了avformat_new_stream函数的使用方法,帮助读者理解如何在AVFormatContext中创建和配置流通道。

大佬总结的

一:介绍
1.1 AVStream
首先了解下 AVStream : http://ffmpeg.org/doxygen/3.1/structAVStream.html

AVStream 即是流通道。例如我们将 H264 和 AAC 码流存储为MP4文件的时候,就需要在 MP4文件中增加两个流通道,一个存储Video:H264,一个存储Audio:AAC。(假设H264和AAC只包含单个流通道)。

AVStream包含很多参数,用于记录通道信息,其中最重要的是 :

AVCodecParameters * codecpar :用于记录编码后的流信息,即通道中存储的流的编码信息。

AVRational time_base :AVStream通道的时间基,时间基是个相当重要的概念。(可参考之后的关于ffmpeg时间的文章)

需要注意的是:现在的 ffmpeg 3.1.4版本已经使用AVCodecParameters * codecpar替换了原先的CodecContext* codec !

1.2 avformat_new_stream
avformat_new_stream 在 AVFormatContext 中创建 Stream 通道。

AVFormatContext :

unsigned int nb_streams;    记录stream通道数目。

AVStream **streams;    存储stream通道。

AVStream :

int index;   在AVFormatContext 中所处的通道索引

在这里插入图片描述
avformat_new_stream之后便在 AVFormatContext 里增加了 AVStream 通道(相关的index已经被设置了)。之后,我们就可以自行设置 AVStream 的一些参数信息。例如 : codec_id , format ,bit_rate ,width , height … …
————————————————
原文链接:https://blog.youkuaiyun.com/u014162133/article/details/82258488

补充

avformat_new_stream(AVFormatContext * s, const AVCodec * c)

AVCodec :s需要通信的视频对应的编码方式。
在已知codec_id的情况下可以通过 codec=avcodec_find_decoder(codec_id);的方式得到。

<think>我们面对的问题是:当使用FFmpegavformat_open_input函数处理无效RTSP URL时,程序会卡住,既不继续执行也不抛出异常。我们需要解决这个问题。 根据引用[2]和[3],我们知道avformat_open_input函数会尝试打开指定的URL(包括RTSP流),并解析流的信息。当URL无效时,函数可能会因为网络超时或等待响应而阻塞。 解决方案思路: 1. 设置超时选项:FFmpeg允许我们为操作设置超时,这样当连接或读取数据超过指定时间时,会中断操作并返回错误。 2. 使用回调函数中断:我们可以注册一个回调函数,在需要的时候(比如用户取消操作)中断连接操作。 3. 多线程处理:将avformat_open_input放在单独的线程中执行,主线程可以控制超时,如果超时则中断该线程。 这里我们主要讨论设置超时选项,因为这是最直接的方法。 根据FFmpeg的文档,我们可以通过设置AVDictionary参数来传递选项给avformat_open_input。对于RTSP流,我们可以设置以下超时选项: - "rtsp_transport": 指定传输协议(如tcp、udp),但这里我们更关心超时。 - "stimeout": 设置套接字超时(以微秒为单位)。例如,设置5秒超时:5000000微秒。 - "timeout": 设置等待连接或读取数据的超时(全局的超时选项,单位微秒)。 注意:不同版本的FFmpeg可能选项名称不同,例如旧版本可能使用"stimeout",而新版本可能使用"timeout"。建议查看最新的文档。 另外,我们还可以设置最大延迟(max_delay)和重试次数等。 具体步骤: 1. 在调用avformat_open_input之前,创建一个AVDictionary并设置超时选项。 2. 调用avformat_open_input时传入这个字典。 3. 检查返回值,如果返回负值(错误),则根据错误码处理。 4. 无论成功与否,都要释放字典。 示例代码: ```c AVFormatContext *fmt_ctx = NULL; AVDictionary *options = NULL; // 设置超时时间为5秒(5000000微秒) av_dict_set(&options, "timeout", "5000000", 0); // 打开输入流 int ret = avformat_open_input(&fmt_ctx, url, NULL, &options); if (ret < 0) { // 处理错误,例如打印错误信息 char errbuf[128]; av_strerror(ret, errbuf, sizeof(errbuf)); fprintf(stderr, "Could not open input: %s\n", errbuf); // 注意:这里fmt_ctx可能被部分初始化,需要调用avformat_close_input清理 if (fmt_ctx) { avformat_close_input(&fmt_ctx); } } else { // 正常处理 // ... avformat_close_input(&fmt_ctx); } // 释放选项字典 av_dict_free(&options); ``` 但是,上述方法可能并不总是有效,因为RTSP连接过程可能涉及多个阶段(如TCP连接、RTSP握手等),而超时设置可能只对某些阶段有效。因此,我们可能需要结合其他方法。 另一种方法是使用回调函数来中断操作。我们可以设置一个中断回调,这样当需要中断时(比如超时或用户取消),我们可以通过回调函数让FFmpeg中断当前操作。 设置中断回调的步骤: 1. 定义一个AVIOInterruptCB结构体,其中包含一个回调函数和一个用户自定义参数(用于传递中断条件)。 2. 将AVFormatContext的interrupt_callback成员设置为这个结构体。 3. 在回调函数中,我们根据条件(例如超时)返回1表示需要中断,返回0表示继续。 注意:avformat_open_input内部会检查这个回调,如果回调返回1,则操作会中断并返回AVERROR_ETIMEDOUT。 但是,avformat_open_input的调用是在设置interrupt_callback之前还是之后?我们必须在打开之前设置。实际上,我们可以先创建AVFormatContext,然后设置interrupt_callback,再调用avformat_open_input。 示例代码: ```c AVFormatContext *fmt_ctx = avformat_alloc_context(); if (!fmt_ctx) { // 处理分配失败 } // 设置中断回调:我们定义一个结构体,其中包含回调函数和用户数据 // 用户数据可以是一个超时时间戳(当前时间+超时时间) int64_t timeout_us = 5000000; // 5秒超时 int64_t start_time = av_gettime(); // 当前时间(微秒) // 定义回调函数 static int interrupt_cb(void *ctx) { int64_t timeout = *(int64_t*)ctx; if (av_gettime() - start_time > timeout) { return 1; // 超时,中断 } return 0; // 继续 } // 设置中断回调 fmt_ctx->interrupt_callback.callback = interrupt_cb; fmt_ctx->interrupt_callback.opaque = &timeout_us; // 然后打开输入 int ret = avformat_open_input(&fmt_ctx, url, NULL, NULL); // 注意:这里我们传入了fmt_ctx,并且已经设置了中断回调,所以打开操作会检查回调 if (ret < 0) { // 处理错误 // ... } // 使用完后释放 avformat_close_input(&fmt_ctx); ``` 但是,注意:在avformat_open_input中,我们传入的是&fmt_ctx,而fmt_ctx已经被分配并设置了中断回调。这样,在打开过程中,FFmpeg会定期检查中断回调。 结合两种方法:我们可以同时设置超时选项和中断回调,以增加可靠性。 针对RTSP,我们还可以尝试指定传输协议,因为有时UDP可能被防火墙阻止导致超时,而TCP则更可靠。设置传输协议: ```c av_dict_set(&options, "rtsp_transport", "tcp", 0); ``` 总结解决方案: 1. 使用超时选项(字典)和中断回调,双重保障超时控制。 2. 指定RTSP传输协议为TCP(如果网络环境允许)。 如果以上方法仍然不能解决问题,我们可能需要考虑在单独的线程中执行avformat_open_input,并在主线程中控制超时,超时后强制终止线程(但这种方法比较暴力,可能导致资源未释放,不推荐)。 因此,优先推荐使用中断回调+选项超时。 最后,注意:在设置超时时间时,需要根据实际网络环境调整,太短可能导致正常网络波动下失败,太长则用户等待过久。 下面是一个综合示例: ```c #include <libavformat/avformat.h> #include <libavutil/time.h> // 中断回调函数 static int interrupt_callback(void *ctx) { int64_t *timeout_us = (int64_t*)ctx; if (av_gettime() - start_time > *timeout_us) { return 1; // 超时中断 } return 0; } int open_rtsp_stream(const char *url) { AVFormatContext *fmt_ctx = NULL; AVDictionary *options = NULL; int64_t timeout_us = 5000000; // 5秒超时 int64_t start_time = av_gettime(); // 记录开始时间 // 分配上下文 fmt_ctx = avformat_alloc_context(); if (!fmt_ctx) { return AVERROR(ENOMEM); } // 设置中断回调 fmt_ctx->interrupt_callback.callback = interrupt_callback; fmt_ctx->interrupt_callback.opaque = &timeout_us; // 设置选项:RTSP使用TCP传输,并设置超时(这里用timeout选项,同时中断回调也起作用) av_dict_set(&options, "rtsp_transport", "tcp", 0); av_dict_set(&options, "stimeout", "5000000", 0); // 也可以试试stimeout int ret = avformat_open_input(&fmt_ctx, url, NULL, &options); av_dict_free(&options); // 无论成功与否,释放选项 if (ret < 0) { avformat_close_input(&fmt_ctx); // 如果打开失败,fmt_ctx可能被部分初始化,需要关闭 return ret; } // 其他操作,如读取流信息等 // ... avformat_close_input(&fmt_ctx); return 0; } ``` 注意:在中断回调中,我们使用了一个全局的start_time?实际上,我们需要将start_time作为回调的用户数据。但上面的示例中,回调函数使用了start_time变量,而该变量是open_rtsp_stream函数的局部变量。我们可以将start_time和timeout_us封装在一个结构体中,然后传递给回调。 修改如下: ```c typedef struct TimeoutContext { int64_t start_time; int64_t timeout_us; } TimeoutContext; static int interrupt_cb(void *ctx) { TimeoutContext *tc = (TimeoutContext*)ctx; if (av_gettime() - tc->start_time > tc->timeout_us) { return 1; } return 0; } // 在函数中: TimeoutContext tc = { av_gettime(), 5000000 }; fmt_ctx->interrupt_callback.opaque = &tc; ``` 这样就更合理了。 如果问题仍然存在,可能需要检查FFmpeg版本,或者查看是否有其他选项可以设置。另外,确保网络环境允许访问RTSP流(防火墙等)。 希望这些方法能解决你的问题。</think>### FFmpeg avformat_open_input 卡住问题解决方案 当使用无效 RTSP URL 调用 `avformat_open_input()` 时,函数可能因网络阻塞而卡住不返回。以下是完整的解决方案: #### 1. **设置超时参数(推荐)** 通过 AVDictionary 设置网络超时参数,这是最有效的解决方案: ```c AVFormatContext *fmt_ctx = NULL; AVDictionary *options = NULL; // 设置超时参数(单位:微秒) av_dict_set(&options, "timeout", "5000000", 0); // 5秒超时 av_dict_set(&options, "stimeout", "3000000", 0); // 3秒套接字超时 av_dict_set(&options, "rtsp_transport", "tcp", 0); // 强制TCP传输 int ret = avformat_open_input(&fmt_ctx, url, NULL, &options); if (ret < 0) { char errbuf[256]; av_strerror(ret, errbuf, sizeof(errbuf)); printf("打开失败: %s\n", errbuf); } av_dict_free(&options); ``` - **关键参数**: - `timeout`:全局超时(推荐值:3-10秒) - `stimeout`:TCP连接超时(推荐值:2-5秒) - `rtsp_transport`:指定TCP避免UDP阻塞[^2] #### 2. **中断回调机制** 设置回调函数强制超时中断: ```c // 定义回调结构 typedef struct { int64_t start_time; int timeout_ms; } TimeoutContext; // 中断回调函数 static int interrupt_cb(void *ctx) { TimeoutContext *tc = (TimeoutContext *)ctx; return (av_gettime() - tc->start_time) > tc->timeout_ms * 1000 ? 1 : 0; } // 使用示例 TimeoutContext tc = { av_gettime(), 5000 }; // 5秒超时 AVFormatContext *fmt_ctx = avformat_alloc_context(); fmt_ctx->interrupt_callback.callback = interrupt_cb; fmt_ctx->interrupt_callback.opaque = &tc; int ret = avformat_open_input(&fmt_ctx, url, NULL, NULL); ``` 此方法在协议层主动检查超时[^3] #### 3. **多线程超时控制** 当上述方法失效时使用线程级超时: ```c #include <pthread.h> void* open_input_thread(void *arg) { // 在此线程内调用avformat_open_input return NULL; } // 主线程控制 pthread_t tid; pthread_create(&tid, NULL, open_input_thread, &url); struct timespec ts = { time(NULL) + 5, 0 }; // 5秒超时 if (pthread_timedjoin_np(tid, NULL, &ts) == ETIMEDOUT) { pthread_cancel(tid); // 强制终止 printf("操作超时!\n"); } ``` #### 4. **错误处理最佳实践 ```c // 检查特定错误码 if (ret == AVERROR_EOF) { // 流结束 } else if (ret == AVERROR(EAGAIN)) { // 资源暂时不可用 } else if (ret == AVERROR(ETIMEDOUT)) { // 超时错误处理 } else if (ret < 0) { // 其他错误 } ``` #### 注意事项 1. **版本兼容性**:FFmpeg 4.0+ 对超时支持更完善 2. **网络诊断**:先用 `ffplay` 测试URL有效性 ```bash ffplay -timeout 5000000 rtsp://example.com/stream ``` 3. **协议优化**:始终指定 `rtsp_transport=tcp` 避免UDP问题 4. **日志调试**:启用FFmpeg日志定位阻塞点 ```c av_log_set_level(AV_LOG_DEBUG); ``` > **根本原因**:无效RTSP URL会导致底层网络调用无限等待,FFmpeg默认不设超时[^4]。通过显式设置超时参数可强制中断阻塞操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值