RV1126解码—ffmpeg_read_thread线程的讲解
fmpeg_read_thread线程的用处
解码这一块,在第一篇的时候就看到了,有三个线程,这篇先介绍一个线程的作用是怎么样的。
ffmpeg_read_thread线程的主要作用是读取每一帧流媒体文件,这个流媒体文件可以是复合流文件。如:TS文件、MP4文件等等,也可以包括网络流媒体文件:如RTMP、RTSP等等。然后抽取每一帧视频帧插入到队列里面。所以就是作用就是读取每一帧视频帧入队。
下面代码要看一下。
第一步:avformat_open_input() 打开输入的流媒体文件
#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1
//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
int ret;
//线程分离
pthread_detach(pthread_self());
//定义出一个大管家
AVFormatContext* ctx = nullptr;
//该函数用于打开多媒体数据并且获得一些相关的信息
ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0);
if(ret < 0)
{
printf("复合文件或者网络文件打开失败\n");
return 0;
}
printf("==> 流媒体文件输入成功!\n");
}
第二步:avformat_find_stream_info() :可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。
#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1
//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
int ret;
//线程分离
pthread_detach(pthread_self());
//定义出一个大管家
AVFormatContext* ctx = nullptr;
//该函数用于打开多媒体数据并且获得一些相关的信息
ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0);
if(ret < 0)
{
printf("复合文件或者网络文件打开失败\n");
return 0;
}
printf("==> 流媒体文件输入成功!\n");
// 可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据
avformat_find_stream_info(m_pInFmtCtx, nullptr);
printf("===========Input Information==========\n");
// 通过av_find_best_stream()函数来获取流的索引 “AVMEDIA_TYPE_VIDEO”找视频的索引
int nVideo_indx = av_find_best_stream(m_pInFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (nVideo_indx < 0)
{
//todo......
}
printf("==> av_find_best_stream!\n");
}
第一步和第二步就相当于初始化工作已经完成了,接下来就是这个线程最重要的一个函数“av_read_frame()”,
先来说一下这个“av_read_frame()”,函数的一些底层的逻辑,看看是怎么工作的,
假设下图是H264的一个连续的视频码流,视频源源不断通摄像头那边传输过来,除非停止。
假设我把这些连续的码流整体塞给解码器,和下图一样,加密器能否正常解析出来,因为这些码流最终都要送到解码器里面解析的,但是,一下子全部给它能否工作?答案是不行的,在解码器里面是没有办法解析一个整体的数据,整体的数据就是左边的这个东西。因为解码器没有办法提取每一帧数据。
所以我们要想它解码器能正常工作,我们要在码流中提取一帧一帧数据,送到解码器这里,那怎么提取,就是与“00 00 00 01”为切割符,是一帧数据的起始符。
所以这也也就是av_read_frame的作用,就是提取视频里中的每一帧数据,这个时候解码器就可以工作了。
av_read_frame:这个API的具体作用是读取码流中提取一帧完整的视频帧或者音频帧。对于编解码器来说,开发者需要获取一帧完整的数据帧才能够对其进行数据的编解码
#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1
//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
int ret;
//线程分离
pthread_detach(pthread_self());
//定义出一个大管家
AVFormatContext* ctx = nullptr;
//该函数用于打开多媒体数据并且获得一些相关的信息
ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0);
if(ret < 0)
{
printf("复合文件或者网络文件打开失败\n");
return 0;
}
printf("==> 流媒体文件输入成功!\n");
// 可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据
avformat_find_stream_info(m_pInFmtCtx, nullptr);
printf("===========Input Information==========\n");
// 通过av_find_best_stream()函数来获取流的索引 “AVMEDIA_TYPE_VIDEO”找视频的索引
int nVideo_indx = av_find_best_stream(m_pInFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (nVideo_indx < 0)
{
//todo......
}
printf("==> av_find_best_stream!\n");
AVPacket *packet = av_packet_alloc();
// 通过av_read_frame获取每一帧视频帧
while (av_read_frame(m_pInFmtCtx, packet) >= 0)
{
if (packet->stream_index == nVideo_indx)//找到这个索引看看是视频的还是音频
{
video_data_packet_t *video_data_packet = (video_data_packet_t
*)malloc(sizeof(video_data_packet_t));
memcpy(video_data_packet->buffer, packet->data, packet->size);
video_data_packet->video_frame_size = packet->size;
//入队
video_queue->putVideoPacketQueue(video_data_packet);
}
}
return NULL;
}