欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt+FFmpeg播放RTSP H264视频流(2)- FFmpeg自定义类封装

程序员文章站 2022-07-14 21:37:52
...

FFmpeg操作类封装

在上一篇博客Qt+FFmpeg播放RTSP H264视频流(1)- 在Qt项目加入FFmpeg库中完成了FFmpeg库的加入qt项目的过程,本篇完成FFmepg库的封装,将播放rtsp视频流用到的相关FFmpeg接口进行简单封装得到我们自己的MyFFmpeg类。

FFmpeg播放RTSP流程分析

FFmpeg播放RTSP流程如下图所示:
Qt+FFmpeg播放RTSP H264视频流(2)- FFmpeg自定义类封装

MyFFmpegSetUrl(QString playUrl) 设置RTSP播放路径

MyFFmpegSetUrl接口用来设置待播放视频路径;

void MyFFmpeg::MyFFmpegSetUrl(QString rtspUrl)
{
    m_rtspUrl = rtspUrl;
}
  • 1
  • 2
  • 3
  • 4

MyFFmpegInit() 初始化

MyFFmpegInit接口主要执行一系列FFmepg初始化、资源分配、视频流打开等工作

int MyFFmpeg::MyFFmpegInit()
{
    int i;
    int ret = -1;
// 获取视频播放URL
QByteArray byteRtspUrl =m_rtspUrl.toLocal8Bit();
char *pRtspUrl = byteRtspUrl.data();

// 初始化所有组件,调用该函数后,才能使用复用器和编解码器
av_register_all();

// 初始化网络库
avformat_network_init();

// 分配AVFormatContext,它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体,
// 具体可参考https://blog.csdn.net/leixiaohua1020/article/details/14214705
m_AVFormatContext = avformat_alloc_context();

// 设置参数
AVDictionary *options = NULL;
// 设置传输协议为TCP协议
av_dict_set(&options, "rtsp_transport", "tcp", 0);

// 设置TCP连接最大延时时间
av_dict_set(&options, "max_delay", "100", 0);

// 设置“buffer_size”缓存容量
av_dict_set(&options, "buffer_size", "1024000", 0);

// 设置avformat_open_input超时时间为3秒
av_dict_set(&options, "stimeout", "3000000", 0);

// 打开网络流或文件流
ret = avformat_open_input(&m_AVFormatContext, pRtspUrl, NULL, &options);
if (ret != 0)
{
    qDebug("Couldn't open input stream, ret=%d\n", ret);
    return -1;
}

// 读取流数据包并获取流的相关信息
if (avformat_find_stream_info(m_AVFormatContext, NULL) < 0)
{
    qDebug("Couldn't find stream information.\n");
    return -1;
}

// 确定流格式是否为视频
for (i = 0; i < m_AVFormatContext->nb_streams; i++)
{
    if (m_AVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        m_videoIndex = i;
        break;
    }
}

if (m_videoIndex == -1)
{
    qDebug("Didn't find a video stream.\n");
    return -1;
}

m_AVCodecContext = m_AVFormatContext->streams[m_videoIndex]->codec;

// 根据编码器的ID号查找对应的解码器
m_AVCodec = avcodec_find_decoder(m_AVCodecContext->codec_id);
if (NULL == m_AVCodec)
{
    qDebug("avcodec_find_decoder AV_CODEC_ID_H264 fail!\n");
    return -1;
}

// 配置编码器上下文的参数
m_AVCodecContext->bit_rate = 0;         //码率
m_AVCodecContext->time_base.den = 25;   // 下面2行设置帧率,每秒/25帧
m_AVCodecContext->time_base.num = 1;
m_AVCodecContext->frame_number = 1;     //每包一个视频帧

// Initialize the AVCodecContext to use the given AVCodec
if (avcodec_open2(m_AVCodecContext, m_AVCodec, NULL) < 0)
{
    qDebug("avcodec_open2 fail");
    return -1;
}

// alloc AVFrame
m_AVFrame = av_frame_alloc();
m_AVFrameRGB = av_frame_alloc();

// 图像色彩空间转换、分辨率缩放、前后图像滤波处理
m_SwsContext = sws_getContext(m_AVCodecContext->width, m_AVCodecContext->height,
        m_AVCodecContext->pix_fmt, m_AVCodecContext->width, m_AVCodecContext->height,
        AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

int bytes = avpicture_get_size(AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);
m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));

// 将分配的内存空间给m_AVFrameRGB使用
avpicture_fill((AVPicture *)m_AVFrameRGB, m_OutBuffer, AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);

// 为AVPacket分别内存空间
int packSize = m_AVCodecContext->width * m_AVCodecContext->height;
m_AVPacket = (AVPacket *)malloc(sizeof(AVPacket));
av_new_packet(m_AVPacket, packSize);

qDebug("============== MyFFmpegInit ok! ====================== ");

return 0;

}

MyFFmpegDestroy() 逆初始化

对MyFFmpegInit接口中申请的资源进行释放操作

void MyFFmpeg::MyFFmpegDestroy()
{
    av_free(m_OutBuffer);
    av_free(m_AVFrameRGB);
    av_frame_free(&m_AVFrame);
    av_frame_free(&m_AVFrameRGB);
    sws_freeContext(m_SwsContext);
    av_free_packet(m_AVPacket);
    free(m_AVPacket);
    avcodec_close(m_AVCodecContext);
    avformat_close_input(&m_AVFormatContext);
    avformat_free_context(m_AVFormatContext);
    avformat_network_deinit();
}

MyFFmpegReadFrame() 读取视频帧

MyFFmpegReadFrame没调用一次,尝试读取一帧视频帧数据,读取成功后对视频帧进行解码、缩放、格式转等操作转换成QImage,再通过MyFFmpegSigGetOneFrame()信号将QImage发送出去;

int MyFFmpeg::MyFFmpepReadFrame()
{
    int ret = -1;
    int getPicture = 0;
// 获取下一帧数据
ret = av_read_frame(m_AVFormatContext, m_AVPacket);
if (ret < 0)
{
    qDebug("av_read_frame fail!\n");
    return -1;
}

if (m_AVPacket->stream_index != m_videoIndex)
{
    av_free_packet(m_AVPacket);
    return 0;
}

//  解码m_AVPacket,Decode the video frame of size avpkt->size from avpkt->data into picture
ret = avcodec_decode_video2(m_AVCodecContext, m_AVFrame, &getPicture, m_AVPacket);
if (ret < 0)
{
    qDebug("avcodec_decode_video2 fail!\n");
    av_free_packet(m_AVPacket);
    return 0;
}

// got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.
// 判断是否已有视频帧被解码了
if (getPicture)
{
    // 对解码视频帧进行缩放、格式转换等操作
    sws_scale(m_SwsContext, (uint8_t const * const *)m_AVFrame->data,
             m_AVFrame->linesize, 0, m_AVCodecContext->height,
             m_AVFrameRGB->data, m_AVFrameRGB->linesize);

    // 转换到QImage
    QImage tmmImage((uchar *)m_OutBuffer, m_AVCodecContext->width, m_AVCodecContext->height, QImage::Format_RGB32);
    QImage image = tmmImage.copy();

    // 发送QImage
    emit MyFFmpegSigGetOneFrame(image);
}

// 释放资源
av_free_packet(m_AVPacket);

return 0;

}

MyFFmpegSigGetOneFrame(QImage img) 发送单帧数据信号

发送QImage数据的信号函数

MyFFmpeg类测试

在FFmpegLibTestFrm类中增加一个测试接口MyFFmpegTest

void FFmpegLibTestFrm::MyFFmpegTest()
{
    MyFFmpeg *ffmpeg = new MyFFmpeg();
QString rtspUrl = "rtsp://192.168.88.168:554/0";
ffmpeg->MyFFmpegSetUrl(rtspUrl);
if (ffmpeg->MyFFmpegInit() == 0) {
    int i;
    for (i = 0; i < 5; i++) {
        ffmpeg->MyFFmpepReadFrame();
    }
} else {
    qDebug("MyFFmpegInit fail!\n");
}

delete ffmpeg;

}

在FFmpegLibTestFrm构造函数中调用一次MyFFmpegTest接口,控制台输入结果如下
Qt+FFmpeg播放RTSP H264视频流(2)- FFmpeg自定义类封装

参考资源

AVPacket详解: https://blog.csdn.net/with_dream/article/details/84928280
AVCodecContext参数:https://blog.csdn.net/chance_yin/article/details/16335625
avcodec_open2(): https://blog.csdn.net/leixiaohua1020/article/details/44117891

FFmpeg操作类封装

在上一篇博客Qt+FFmpeg播放RTSP H264视频流(1)- 在Qt项目加入FFmpeg库中完成了FFmpeg库的加入qt项目的过程,本篇完成FFmepg库的封装,将播放rtsp视频流用到的相关FFmpeg接口进行简单封装得到我们自己的MyFFmpeg类。

FFmpeg播放RTSP流程分析

FFmpeg播放RTSP流程如下图所示:
Qt+FFmpeg播放RTSP H264视频流(2)- FFmpeg自定义类封装

MyFFmpegSetUrl(QString playUrl) 设置RTSP播放路径

MyFFmpegSetUrl接口用来设置待播放视频路径;

void MyFFmpeg::MyFFmpegSetUrl(QString rtspUrl)
{
    m_rtspUrl = rtspUrl;
}
  • 1
  • 2
  • 3
  • 4

MyFFmpegInit() 初始化

MyFFmpegInit接口主要执行一系列FFmepg初始化、资源分配、视频流打开等工作

int MyFFmpeg::MyFFmpegInit()
{
    int i;
    int ret = -1;
// 获取视频播放URL
QByteArray byteRtspUrl =m_rtspUrl.toLocal8Bit();
char *pRtspUrl = byteRtspUrl.data();

// 初始化所有组件,调用该函数后,才能使用复用器和编解码器
av_register_all();

// 初始化网络库
avformat_network_init();

// 分配AVFormatContext,它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体,
// 具体可参考https://blog.csdn.net/leixiaohua1020/article/details/14214705
m_AVFormatContext = avformat_alloc_context();

// 设置参数
AVDictionary *options = NULL;
// 设置传输协议为TCP协议
av_dict_set(&options, "rtsp_transport", "tcp", 0);

// 设置TCP连接最大延时时间
av_dict_set(&options, "max_delay", "100", 0);

// 设置“buffer_size”缓存容量
av_dict_set(&options, "buffer_size", "1024000", 0);

// 设置avformat_open_input超时时间为3秒
av_dict_set(&options, "stimeout", "3000000", 0);

// 打开网络流或文件流
ret = avformat_open_input(&m_AVFormatContext, pRtspUrl, NULL, &options);
if (ret != 0)
{
    qDebug("Couldn't open input stream, ret=%d\n", ret);
    return -1;
}

// 读取流数据包并获取流的相关信息
if (avformat_find_stream_info(m_AVFormatContext, NULL) < 0)
{
    qDebug("Couldn't find stream information.\n");
    return -1;
}

// 确定流格式是否为视频
for (i = 0; i < m_AVFormatContext->nb_streams; i++)
{
    if (m_AVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        m_videoIndex = i;
        break;
    }
}

if (m_videoIndex == -1)
{
    qDebug("Didn't find a video stream.\n");
    return -1;
}

m_AVCodecContext = m_AVFormatContext->streams[m_videoIndex]->codec;

// 根据编码器的ID号查找对应的解码器
m_AVCodec = avcodec_find_decoder(m_AVCodecContext->codec_id);
if (NULL == m_AVCodec)
{
    qDebug("avcodec_find_decoder AV_CODEC_ID_H264 fail!\n");
    return -1;
}

// 配置编码器上下文的参数
m_AVCodecContext->bit_rate = 0;         //码率
m_AVCodecContext->time_base.den = 25;   // 下面2行设置帧率,每秒/25帧
m_AVCodecContext->time_base.num = 1;
m_AVCodecContext->frame_number = 1;     //每包一个视频帧

// Initialize the AVCodecContext to use the given AVCodec
if (avcodec_open2(m_AVCodecContext, m_AVCodec, NULL) < 0)
{
    qDebug("avcodec_open2 fail");
    return -1;
}

// alloc AVFrame
m_AVFrame = av_frame_alloc();
m_AVFrameRGB = av_frame_alloc();

// 图像色彩空间转换、分辨率缩放、前后图像滤波处理
m_SwsContext = sws_getContext(m_AVCodecContext->width, m_AVCodecContext->height,
        m_AVCodecContext->pix_fmt, m_AVCodecContext->width, m_AVCodecContext->height,
        AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

int bytes = avpicture_get_size(AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);
m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));

// 将分配的内存空间给m_AVFrameRGB使用
avpicture_fill((AVPicture *)m_AVFrameRGB, m_OutBuffer, AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);

// 为AVPacket分别内存空间
int packSize = m_AVCodecContext->width * m_AVCodecContext->height;
m_AVPacket = (AVPacket *)malloc(sizeof(AVPacket));
av_new_packet(m_AVPacket, packSize);

qDebug("============== MyFFmpegInit ok! ====================== ");

return 0;
相关标签: Qt ffmpeg