ffmpeg与opencv联合的解码图像并显示问题。

该段代码来自:(2条消息) 3、FFMPEG拉流转OpenCV并推流& Opencv 读视频转FFMPEG并推流_sxj731533730的博客-CSDN博客 

该作者的其他文章也十分具有工程的借鉴意义,不知道作者本人是何方圣神?

笔者抄袭了该段代码,添加了一些注释并修改了一些不合适的地方。

ffmpeg使用起来不是很顺畅,也许是该库开放了太多api,给予了开发者更多的操作空间,对于希望开发更上层应用的编程人员来讲是件好事!

复制代码
#include <stdio.h>
#include <string>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

extern "C"
{

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/imgutils.h"

};

using namespace std;

int main(int argc, char *argv[]) {

    const char *path = "cv2_curve.mp4";//不能识别UTF8和其他汉语,请尽量使用全英文
    
    AVFormatContext *pFormat = NULL;//输出上下文,用于联系数据和输出的中间结构体。
    int ret = avformat_open_input(&pFormat, path, NULL, NULL);
    if (ret < 0) {
        perror("avformat_open_input");
        avformat_free_context(pFormat);
        return -1;
    }
    // av_dict_free(&optional);
    printf("avformat_open_input successfully\n");
    ret = avformat_find_stream_info(pFormat, NULL);
    if (ret < 0) {
        perror("avformat_find_stream_info\n");
        return -1;
    }
    printf("avformat_find_stream_info successfully\n");
    int time = pFormat->duration;
    int mbittime = (time / 100000) / 60;
    int mminttime = (time / 100000) % 60;
    printf("video time: %d'm %d's\n", mbittime, mminttime);
    av_dump_format(pFormat, 0, path, 0);
    int videoindex = -1;
    for (int i = 0; i < pFormat->nb_streams; i++) {
        if (pFormat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoindex = i;
            break;
        }
    }
    if (videoindex == -1) {
        printf("don't find video stream\n");
        return -1;
    }
    AVCodecParameters *codecParameters = pFormat->streams[videoindex]->codecpar;


    printf("video width %d\n", codecParameters->width);
    printf("video height %d\n", codecParameters->height);
    const AVCodec *pCodec = avcodec_find_decoder((AVCodecID)27);//设置解码器 解码协议及格式
    AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec); //为解码器上下文分配内存空间
    avcodec_parameters_to_context(pCodecCtx, codecParameters); //解码器相关参数拷贝到上下文
    ret = avcodec_open2(pCodecCtx, pCodec, NULL);//用解码器初始化上下文
    /*if (ret < 0) {//打开解码器
        printf("Could not open codec.\n");
        return -1;
    }*/
    AVFrame *pFrame = av_frame_alloc();
    pFrame->width = codecParameters->width;
    pFrame->height = codecParameters->height;
    pFrame->format = AV_PIX_FMT_YUV420P;
    ret = av_frame_get_buffer(pFrame, 1);
    if (ret < 0) {
        printf("av_frame_get_buffer error\n");
        return -1;
    }
    AVFrame *pFrameRGB = av_frame_alloc();
    pFrameRGB->width = codecParameters->width;
    pFrameRGB->height = codecParameters->height;
    pFrameRGB->format = AV_PIX_FMT_RGB24;
    ret = av_frame_get_buffer(pFrameRGB, 1);
    if (ret < 0) {
        printf("av_frame_get_buffer error\n");
        return -1;
    }


    int picture_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, codecParameters->width, codecParameters->height,
        1);//计算这个格式的图片,需要多少字节来存储
    uint8_t *out_buff = (uint8_t *)av_malloc(picture_size * sizeof(uint8_t));
    av_image_fill_arrays(pFrame->data, pFrame->linesize, out_buff, AV_PIX_FMT_YUV420P, codecParameters->width,
        codecParameters->height, 1);
    //这个函数 是缓存转换格式,可以不用 以为上面已经设置了AV_PIX_FMT_YUV420P
    SwsContext *img_convert_ctx = sws_getContext(codecParameters->width, codecParameters->height, AV_PIX_FMT_YUV420P,
        codecParameters->width, codecParameters->height, AV_PIX_FMT_RGB24, 4,
        NULL, NULL, NULL);
    AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));


    while (av_read_frame(pFormat, packet) >= 0) {
        if (packet->stream_index == videoindex) {
            ret = avcodec_send_packet(pCodecCtx, packet);//提供原始数据包给解码器作为输入
            if (ret < 0) {
                printf("avcodec_send_packet error\n");
                continue;
            }
            av_packet_unref(packet);//刷新一下packet
            int got_picture = avcodec_receive_frame(pCodecCtx, pFrame);//输出
            if (got_picture < 0) {
                printf("avcodec_receive_frame error\n");
                continue;
            }

            sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
                codecParameters->height,
                pFrameRGB->data, pFrameRGB->linesize);


            cv::Mat mRGB(cv::Size(codecParameters->width, codecParameters->height), CV_8UC3);
            mRGB.data = (unsigned char *)pFrameRGB->data[0];
            cv::Mat mBGR;
            cv::cvtColor(mRGB, mBGR, cv::COLOR_RGB2BGR);

            cv::imshow("demo", mRGB);
            cv::waitKey(10);
        }


    }
    av_frame_free(&pFrame);
    av_frame_free(&pFrameRGB);
    avformat_free_context(pFormat);
    return 0;
}
复制代码

笔者认为有以下几点需要注意:

1.AVFormatContext记述了输入文件、数据流信息等等重要的信息。AVFormatContext下的孙成员codecpar是比较重要的,该成员记述了编码协议、图像长宽高等等重要的东西。

2.sws_scale这个操作类似于python中的reshape函数,因为Mat的data数据是逐行的,所以必须要squeeze一下数据。

 

posted @   澳大利亚树袋熊  阅读(137)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示