ffmpeg解码h264流数据并用opencv显示

    最近做的一个项目,需要播放服务端传过来的h264裸流数据,于是做了一个简易的播放器,采用ffmpeg解码h264并用opencv显示图像。实现原理很简单,首先接收到一个完整的h264帧之后传给ffmpeg的AVPacket,然后调用avcodec_send_packet()和avcodec_receive_frame()解码,得到一个AVFrame,然后调用sws_scale()把解码后的yuv图像转换成opencv能使用的RGB图像,然后每解码转换一帧数据就调用一次cv::imshow()显示图像,具体实现代码如下:

//H264ToCVShow.h
#ifndef _H264TOCVSHOW_H
#define _H264TOCVSHOW_H

//#define __STDC_CONSTANT_MACROS
#include <stdio.h>
// Opencv
extern "C" {
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>

#include <thread>
#include <string>

class H264ToCVShow {

public :

    H264ToCVShow(std::string winName);
    ~H264ToCVShow(); 
    void init();
    void decode(unsigned char *inputbuf, size_t size);
    void start();
    void play();
    cv::Mat getMat();

private:
    
    const AVCodec *codec;
    AVCodecContext *c = nullptr;
    int frame_count;
    AVFrame *frame;
    AVPacket avpkt;
    struct SwsContext *img_convert_ctx;
    cv::Mat pCvMat;
    bool matReady;
    std::thread *play_th = nullptr;
    std::string windowName;
};

#endif 
//H264ToCVShow.cpp
#include "H264ToCVShow.h"
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"swscale.lib")
#pragma comment(lib,"opencv_world320d.lib")


void H264ToCVShow::init() {

    matReady = false;
    av_init_packet(&avpkt);

    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }

    frame_count = 0;

}


void H264ToCVShow::decode(unsigned char *inputbuf, size_t size){

    avpkt.size = size;
    if(avpkt.size == 0)
        return;

    avpkt.data = inputbuf;
	int ret = avcodec_send_packet(c, &avpkt);
	if (ret != 0)
	{
		printf("avcodec_send_packet error\n");
	}
	ret = avcodec_receive_frame(c, frame);
	while (true)
	{
		if (ret == 0)
		{
			int width = frame->width;
			int height = frame->height;

			// Allocate the opencv mat and store its stride in a 1-element array
			if (pCvMat.rows != height || pCvMat.cols != width || pCvMat.type() != CV_8UC3) pCvMat = cv::Mat(height, width, CV_8UC3);
			int cvLinesizes[1];
			cvLinesizes[0] = pCvMat.step1();

			// Convert the colour format and write directly to the opencv matrix
			SwsContext* conversion = sws_getContext(width, height, (AVPixelFormat)frame->format, width, height, AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
			sws_scale(conversion, frame->data, frame->linesize, 0, height, &pCvMat.data, cvLinesizes);
			sws_freeContext(conversion);
			matReady = true;
			ret = avcodec_receive_frame(c, frame);
			if (ret != 0) {
				matReady = false;
				break;
			}
		}
		else {
			matReady = false;
			break;
		}
	}
}

void H264ToCVShow::play() {
    while(true)
    {
        if(matReady){   
            cv::imshow(windowName,pCvMat);
            cv::waitKey(1);
        }
    }
}

void H264ToCVShow::start()
{
    play_th = new std::thread(&H264ToCVShow::play,this);
    //play_th->join();
}

H264ToCVShow::H264ToCVShow(std::string winName) {
    this->windowName = winName;
    init();
}
H264ToCVShow::~H264ToCVShow() {
    play_th->join();
    delete play_th;
    play_th = nullptr;
}
cv::Mat H264ToCVShow::getMat() {
    if(matReady){
        return pCvMat;
    }
    else{
        return cv::Mat();
    }
}



  • 0
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用FFmpeg解码H.264视频的示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> extern "C" { #include <libavcodec/avcodec.h> } int main(int argc, char **argv) { AVCodec *codec = NULL; AVCodecContext *codec_ctx = NULL; AVFrame *frame = NULL; AVPacket packet; int ret, i, video_stream_index; int got_frame = 0; int frame_count = 0; int video_width, video_height; struct timeval start_time, end_time; if (argc < 2) { printf("Usage: %s <input_file>\n", argv[0]); return -1; } avcodec_register_all(); // 打开文件并读取视频信息 AVFormatContext *format_ctx = NULL; if (avformat_open_input(&format_ctx, argv[1], NULL, NULL) != 0) { printf("Couldn't open input file\n"); return -1; } if (avformat_find_stream_info(format_ctx, NULL) < 0) { printf("Couldn't find stream information\n"); return -1; } // 查找视频,并初始化解码器 for (i = 0; i < format_ctx->nb_streams; i++) { if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; codec_ctx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codec_ctx, format_ctx->streams[i]->codecpar); codec = avcodec_find_decoder(codec_ctx->codec_id); if (!codec) { printf("Unsupported codec\n"); return -1; } if (avcodec_open2(codec_ctx, codec, NULL) < 0) { printf("Could not open codec\n"); return -1; } video_width = codec_ctx->width; video_height = codec_ctx->height; break; } } // 初始化AVFrame并分配内存 frame = av_frame_alloc(); if (!frame) { printf("Could not allocate frame\n"); return -1; } gettimeofday(&start_time, NULL); // 读取视频解码 while (av_read_frame(format_ctx, &packet) >= 0) { if (packet.stream_index == video_stream_index) { ret = avcodec_decode_video2(codec_ctx, frame, &got_frame, &packet); if (ret < 0) { printf("Error decoding video frame\n"); return -1; } if (got_frame) { printf("Decoded frame %d\n", frame_count++); // 在这里可以处理解码后的帧,例如渲染到屏幕上 } } av_packet_unref(&packet); } gettimeofday(&end_time, NULL); double elapsed_time = (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_usec - start_time.tv_usec) / 1000000.0; printf("Decoded %d frames in %f seconds (average fps: %f)\n", frame_count, elapsed_time, frame_count / elapsed_time); avcodec_free_context(&codec_ctx); avformat_close_input(&format_ctx); av_frame_free(&frame); return 0; } ``` 这段代码打开指定文件并读取视频信息,查找视频并初始化解码器,然后循环读取视频解码每个视频帧。在解码每个帧后,您可以将其渲染到屏幕上或进行其他处理。最后,它将打印解码帧的数量和解码时间,然后释放所有资源。 请注意,为了简化代码,这个示例忽略了错误处理和内存释放。在实际应用中,您需要确保正确地处理和释放所有资源,以避免内存泄漏和其他问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值