x264及H264实现对OpenCV Mat的编解码

36 篇文章 32 订阅

x264及H264实现对OpenCV Mat的编解码

微信公众号:幼儿园的学霸
个人的学习笔记,关于OpenCV,关于机器学习, …。问题或建议,请公众号留言;

之前写的ADAS客户端软件和ADAS程序之间的视频传输采用了cv::imencode和cv::imdecode函数实现编解码,最近偶然间发现可以利用H.264对视频进行编解码,并且效果还不错,特此记录。

目录

x264对Mat进行编码

代码如下.使用时,首先进行编码器的初始化,然后在while循环中调用编码函数实现编码;如果需要进行网络传输,发送编码后的数据即可,在接收端,采用H264进行解码,恢复视频信息。

  • x264_encoder.h
//
// Created by liheng on 19-12-9.
//https://www.cnblogs.com/ziyu-trip/p/7075003.html

#ifndef ADAS_X264_ENCODER_H
#define ADAS_X264_ENCODER_H

#include <stdint.h>
#include "x264.h"
#include "opencv/cv.h"
#include "opencv/highgui.h"

struct x264_encoder{
    x264_param_t    param;
    char            preset[20];
    char            tune[20];
    char            profile[20];
    x264_t*            h;
    x264_picture_t    pic_in;
    x264_picture_t    pic_out;
    long            colorspace;
    x264_nal_t*        nal;
    int             iframe;
    int             iframe_size;
    int                inal;
};

class x264Encoder
{
public:

    x264Encoder();

    x264Encoder(int videoWidth, int videoHeight, int channel, int fps);

    ~x264Encoder();

    /** 创建X264编码器
     * @param[in] videoWidth  视频宽度
     * @param[in] videoHeight 视频高度
     * @param[in] fps 帧率
     * @return 成功返回true, 失败返回false.
     */
    bool Create(int videoWidth, int videoHeight, int channel = 3, int fps = 30);

    /** 编码一帧
     * @param[in] frame 输入的一帧图像
     * @return 返回编码后数据尺寸, 0表示编码失败
     */
    int EncodeOneFrame(const cv::Mat& frame);

    /** 获取编码后的帧数据
     * 说明: EncodeOneFrame 后调用
     * @return 返回裸x264数据
     */
    uchar* GetEncodedFrame() const;

    /** 销毁X264编码器
     */
    void Destory();

    // 编码器是否可用
    bool IsValid() const;

private:

    void Init();

public:
    int m_width;
    int m_height;
    int m_channel;
    int m_fps;

protected:

    int m_widthstep;
    int m_lumaSize;
    int m_chromaSize;

    x264_encoder*  m_encoder;
};

#endif //ADAS_X264_ENCODER_H
  • x264_encoder.cpp
//
// Created by liheng on 19-12-9.
//

#include "x264_encoder.h"
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "opencv2/imgproc.hpp"

#define ENCODER_TUNE   "zerolatency"
#define ENCODER_PROFILE  "baseline"
#define ENCODER_PRESET  "veryfast"//"superfast"
#define ENCODER_COLORSPACE X264_CSP_I420
#define CLEAR(x) (memset((&x),0,sizeof(x)))

x264Encoder::x264Encoder()
{
    Init();
}

x264Encoder::x264Encoder(int videoWidth, int videoHeight, int channel, int fps)
{
    Init();
    Create(videoWidth, videoHeight, channel, fps);
}

x264Encoder::~x264Encoder()
{
    Destory();
}

void x264Encoder::Init()
{
    m_width = 0;
    m_height = 0;
    m_channel = 0;
    m_widthstep = 0;
    m_fps = 30;
    m_lumaSize = 0;
    m_chromaSize = 0;
    m_encoder = NULL;
}

bool x264Encoder::Create(int videoWidth, int videoHeight, int channel, int fps)
{
    int ret;
    int imgSize;

    if (videoWidth <= 0 || videoHeight <= 0 || channel < 0 || fps <= 0){
        printf("wrong input param\n");
        return false;
    }
    m_width = videoWidth;
    m_height = videoHeight;
    m_channel = channel;
    m_fps = fps;
    m_widthstep = videoWidth * channel;
    m_lumaSize = m_width * m_height;
    m_chromaSize = m_lumaSize / 4;
    imgSize = m_lumaSize * channel;

    m_encoder = (x264_encoder *)malloc(sizeof(x264_encoder));
    if (!m_encoder){
        printf("cannot malloc x264_encoder !\n");
        return false;
    }
    CLEAR(*m_encoder);
    m_encoder->iframe = 0;
    m_encoder->iframe_size = 0;

    strcpy(m_encoder->preset, ENCODER_PRESET);
    strcpy(m_encoder->tune, ENCODER_TUNE);

    /*初始化编码器*/
    CLEAR(m_encoder->param);
    x264_param_default(&m_encoder->param);

    ret = x264_param_default_preset(&m_encoder->param, m_encoder->preset, m_encoder->tune);
    if (ret < 0){
        printf("x264_param_default_preset error!\n");
        return false;
    }

    /*cpuFlags 去空缓冲区继续使用不死锁保证*/
    m_encoder->param.i_threads = X264_SYNC_LOOKAHEAD_AUTO;
    /*视频选项*/
    m_encoder->param.i_csp = X264_CSP_I420;
    m_encoder->param.i_width = m_width;    // 要编码的图像的宽度
    m_encoder->param.i_height = m_height;    // 要编码的图像的高度
    m_encoder->param.i_frame_total = 0;    // 要编码的总帧数,不知道用0
    m_encoder->param.i_keyint_max = 10*fps;// 关键帧间隔
    /*流参数*/
    m_encoder->param.i_bframe = 5;
    m_encoder->param.b_open_gop = 0;
    m_encoder->param.i_bframe_pyramid = 0;
    m_encoder->param.i_bframe_adaptive = X264_B_ADAPT_TRELLIS;

    /*log参数,不需要打印编码信息时直接注释掉*/
    m_encoder->param.i_log_level = X264_LOG_NONE;

    m_encoder->param.i_fps_num = fps;//码率分子
    m_encoder->param.i_fps_den = 1;    //码率分母

    m_encoder->param.b_intra_refresh = 1;
    m_encoder->param.b_annexb = 1;
    m_encoder->param.rc.f_rf_constant = 24;
    m_encoder->param.rc.i_rc_method = X264_RC_CRF;
    /

    strcpy(m_encoder->profile, ENCODER_PROFILE);
    ret = x264_param_apply_profile(&m_encoder->param, m_encoder->profile);
    if (ret < 0){
        printf("x264_param_apply_profile error!\n");
        return false;
    }
    /*打开编码器*/
    m_encoder->h = x264_encoder_open(&m_encoder->param);
    m_encoder->colorspace = ENCODER_COLORSPACE;

    /*初始化pic*/
    ret = x264_picture_alloc(&m_encoder->pic_in, m_encoder->colorspace, m_width, m_height);
    if ( ret < 0 ){
        printf("x264_picture_alloc error! ret=%d\n", ret);
        return false;
    }

    m_encoder->pic_in.img.i_csp = m_encoder->colorspace;
    m_encoder->pic_in.img.i_plane = 3;
    m_encoder->pic_in.i_type = X264_TYPE_AUTO;

    m_encoder->inal = 0;
    m_encoder->nal = (x264_nal_t *)calloc(2, sizeof(x264_nal_t));
    if (!m_encoder->nal){
        printf("malloc x264_nal_t error!\n");
        return false;
    }
    CLEAR(*(m_encoder->nal));

    return true;
}

int x264Encoder::EncodeOneFrame(const cv::Mat& frame)
{
    if (frame.empty()){
        return 0;
    }
    cv::Mat bgr(frame), yuv;

    if(1 == frame.channels()){
        cv::cvtColor(frame, bgr, CV_GRAY2BGR);
    }
    cv::cvtColor(bgr, yuv, CV_BGR2YUV_I420);

    memcpy(m_encoder->pic_in.img.plane[0], yuv.data, m_lumaSize);
    memcpy(m_encoder->pic_in.img.plane[1], yuv.data + m_lumaSize, m_chromaSize);
    memcpy(m_encoder->pic_in.img.plane[2], yuv.data + m_lumaSize + m_chromaSize, m_chromaSize);
    m_encoder->pic_in.i_pts = m_encoder->iframe ++;

    m_encoder->iframe_size = x264_encoder_encode(m_encoder->h, &m_encoder->nal, &m_encoder->inal, &m_encoder->pic_in, &m_encoder->pic_out);

    return m_encoder->iframe_size;
}

uchar* x264Encoder::GetEncodedFrame() const
{
    return m_encoder->nal->p_payload;
}

void x264Encoder::Destory()
{
    if (m_encoder){
        if (m_encoder->h){
            x264_encoder_close(m_encoder->h);
            m_encoder->h = NULL;
        }
        free(m_encoder);
        m_encoder = NULL;
    }
}

bool x264Encoder::IsValid() const
{
    return ((m_encoder != NULL) && (m_encoder->h != NULL));
}

H264对Mat进行编码

代码如下,代码来源见代码说明。在原代码基础上进行了微调,以保证视频质量无损。

  • h264encoder.h
#ifndef _AV_H264_H_
#define _AV_H264_H_

/***********************************************************
**	Author:kaychan
**	Data:2019-11-29
**  Mail:1203375695@qq.com
**	Explain:a h264 codec
***********************************************************/

#include <opencv2/opencv.hpp>

#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C" {
#endif
#include <libavutil/time.h>
#include <libavutil/mathematics.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#ifdef __cplusplus
};
#endif

typedef struct AvH264EncConfig_T {

    int width = 320;
    int height = 240;
    int frame_rate = 25;
    //int64_t bit_rate = 320000;
    int gop_size = 250;
    int max_b_frames = 0;
}AvH264EncConfig;

class h264Encoder {

public:
    h264Encoder();
    ~h264Encoder();
    int Init(AvH264EncConfig h264_config);
    AVPacket *encode(const cv::Mat& mat);
    void Destory();
private:
    AVCodec *cdc_;
    AVCodecContext *cdc_ctx_;
    AVFrame *avf_;
    AVPacket *avp_;
    int frame_size_;
    int pts_;
};

#endif
  • h264encoder.cpp
#include "h264encoder.h"

h264Encoder::h264Encoder() {

    cdc_ = NULL;
    cdc_ctx_ = NULL;
    avf_ = NULL;
    avp_ = NULL;
}

h264Encoder::~h264Encoder()
{
    Destory();
}

int h264Encoder::Init(AvH264EncConfig h264_config) {

    pts_ = 0;
    cdc_ = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!cdc_) {

        return -1;
    }
    cdc_ctx_ = avcodec_alloc_context3(cdc_);
    if (!cdc_ctx_) {

        return -1;
    }
    //cdc_ctx_->bit_rate = h264_config.bit_rate;//导致画面模糊
    cdc_ctx_->width = h264_config.width;
    cdc_ctx_->height = h264_config.height;
    cdc_ctx_->time_base = { 1, h264_config.frame_rate };
    cdc_ctx_->framerate = { h264_config.frame_rate, 1 };
    cdc_ctx_->gop_size = h264_config.gop_size;
    cdc_ctx_->max_b_frames = h264_config.max_b_frames;
    cdc_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
    cdc_ctx_->codec_id = AV_CODEC_ID_H264;
    cdc_ctx_->codec_type = AVMEDIA_TYPE_VIDEO;
    //cdc_ctx_->qmin = 10;
    //cdc_ctx_->qmax = 51;
    //cdc_ctx_->qcompress = 0.6;
    AVDictionary *dict = nullptr;
    //av_dict_set(&dict, "preset", "slow", 0);
    av_dict_set(&dict, "preset", "veryfast", 0);
    av_dict_set(&dict, "tune", "zerolatency", 0);
    av_dict_set(&dict, "profile", "main", 0);
    avf_ = av_frame_alloc();
    avp_ = av_packet_alloc();
    if (!avf_ || !avp_) {

        return -1;
    }
    av_init_packet(avp_);

    frame_size_ = cdc_ctx_->width * cdc_ctx_->height;
    avf_->format = cdc_ctx_->pix_fmt;
    avf_->width = cdc_ctx_->width;
    avf_->height = cdc_ctx_->height;
    // alloc memory
    int r = av_frame_get_buffer(avf_, 0);
    if (r < 0) {

        return -1;
    }
    r = av_frame_make_writable(avf_);
    if (r < 0) {

        return -1;
    }
    return avcodec_open2(cdc_ctx_, cdc_, &dict);
}

void h264Encoder::Destory() {

    if(cdc_ctx_) avcodec_free_context(&cdc_ctx_);
    if (avf_) av_frame_free(&avf_);
    if (avp_) av_packet_free(&avp_);
}

AVPacket *h264Encoder::encode(const cv::Mat& mat) {

    if (mat.empty()) return NULL;
    cv::resize(mat, mat, cv::Size(cdc_ctx_->width, cdc_ctx_->height));
    cv::Mat yuv;
    cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
    unsigned char *pdata = yuv.data;
    // fill yuv420
    // yyy yyy yyy yyy
    // uuu
    // vvv
    avf_->data[0] = pdata;
    avf_->data[1] = pdata + frame_size_;
    avf_->data[2] = pdata + frame_size_ * 5 / 4;
    avf_->pts = pts_++;
    int r = avcodec_send_frame(cdc_ctx_, avf_);
    if (r >= 0) {

        r = avcodec_receive_packet(cdc_ctx_, avp_);
        if (r == 0) {

            avp_->stream_index = avf_->pts;
            return avp_;
        }
        if (r == AVERROR(EAGAIN) || r == AVERROR_EOF) {

            return NULL;
        }
    }
    return NULL;
}

H264对x264/H264编码数据进行解码

  • h264decoder.h
#ifndef CH264DECODER_H
#define CH264DECODER_H

#include <string.h>
#include <opencv2/opencv.hpp>
//C++引用C语言的头文件
extern "C"
{
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
}

class CH264Decoder
{
public:
    CH264Decoder();
    ~CH264Decoder();
    /*************************************************
      Function:initial
      Description:初始化
      Input:无
      Output:无
      Return:错误代码
      Others:无
    *************************************************/
    int initial();
    /*************************************************
      Function:decode
      Description:解码
      Input:pDataIn-待解码数据,nInSize-待解码数据长度
      Output:pDataOut-解码后的数据,nWidth-解码后的图像宽度,nHeight-解码后的图像高度
      Return:错误代码
      Others:解码后的数据为RGB16格式
    *************************************************/
    int decode(uint8_t *pDataIn, int nInSize, cv::Mat& res);
    /*************************************************
      Function:unInitial
      Description:销毁
      Input:无
      Output:无
      Return:无
      Others:无
    *************************************************/
    void unInitial();

private:
    int avframe_to_cvmat(AVFrame *frame,cv::Mat& res);
    AVFrame *cvmat2avframe(cv::Mat mat);
private:
    AVCodec *codec;
    AVCodecContext *context;
    AVFrame *frame;
    AVPacket packet;


};

#endif // CH264DECODER_H
  • h264decoder.cpp
#include "h264decoder.h"

CH264Decoder::CH264Decoder()
{
    initial();
}

CH264Decoder::~CH264Decoder()
{
    unInitial();
}




int CH264Decoder::initial()
{
    //avcodec_register_all();//新版本应该不需要这句话
    av_init_packet(&packet);

    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        printf("avcodec_find_encoder failed");
        return -1;
    }

    context = avcodec_alloc_context3(codec);
    if (!context)
    {
        printf("avcodec_alloc_context3 failed");
        return -2;
    }

    context->codec_type = AVMEDIA_TYPE_VIDEO;
    context->pix_fmt = AV_PIX_FMT_YUV420P;

    if (avcodec_open2(context, codec, NULL) < 0)
    {
        printf("avcodec_open2 failed");
        return -3;
    }

    frame = av_frame_alloc();
    if (!frame)
    {
        return -4;
    }

    return 0;
}

void CH264Decoder::unInitial()
{
    avcodec_close(context);
    av_free(context);
    av_frame_free(&frame);
}

int CH264Decoder::decode(uint8_t *pDataIn, int nInSize, cv::Mat& res)
{
//    av_init_packet(&packet);
    packet.size = nInSize;
    packet.data = pDataIn;

    if (packet.size > 0)
    {
        int got_picture=0;
        //int ret= avcodec_decode_video2(context, frame, &got_picture, &packet);
        //新版用法
        int ret = avcodec_send_packet(context, &packet);
        if (ret == 0) got_picture = avcodec_receive_frame(context, frame); //got_picture = 0 success, a frame was returned
        if (ret < 0)
        {
            printf("avcodec_encode_video2 failed");
            return -2;
        }

        if (got_picture==0)//采用avcodec_decode_video2时,此处为if (got_picture)
        {
             avframe_to_cvmat(frame,res);
        }
    }
    else
    {
        printf("no data to decode");
        return -1;
    }

    return 0;
}

int CH264Decoder::avframe_to_cvmat(AVFrame *frame,cv::Mat& res)
{
    int width = frame->width, height = frame->height;
    res.create(height*3/2, width, CV_8UC1);
    memcpy( res.data, frame->data[0], width*height );
    memcpy( res.data + width*height, frame->data[1], width*height/4 );
    memcpy( res.data + width*height*5/4, frame->data[2], width*height/4 );

    //cv::imshow( "yuv_show", res );//yuv格式
    cv::cvtColor( res, res, cv::COLOR_YUV2BGR_I420 );//bgr格式
    //cv::imshow( "bgr_show", bgr );
    return 0;

}
AVFrame * CH264Decoder::cvmat2avframe(cv::Mat mat) {

    // alloc avframe
    AVFrame *avframe = av_frame_alloc();
    if (avframe && !mat.empty()) {

        avframe->format = AV_PIX_FMT_YUV420P;
        avframe->width = mat.cols;
        avframe->height = mat.rows;
        av_frame_get_buffer(avframe, 0);
        av_frame_make_writable(avframe);
        cv::Mat yuv; // convert to yuv420p first
        cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
        // calc frame size
        int frame_size = mat.cols * mat.rows;
        unsigned char *pdata = yuv.data;
        // fill yuv420
        // yyy yyy yyy yyy
        // uuu
        // vvv
        avframe->data[0] = pdata; // fill y
        avframe->data[1] = pdata + frame_size; // fill u
        avframe->data[2] = pdata + frame_size * 5 / 4; // fill v
    }
    return avframe;
}

编码+解码示例

x264编码+H264解码示例

X264EncoderDemo.cpp

//
// Created by liheng on 19-12-9.
//

#include "x264_encoder.h"
#include "h264decoder.h"
#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat frame;
    

    cv::VideoCapture videoCapture("/home/liheng/ADAS_Video/1120/ADAS_Video-20191120-151849.mp4");
    for (int i = 0; i < 10; ++i)
    {
        videoCapture >> frame;
    }

    //x264 encode
    x264Encoder m_x264Encoder;
    m_x264Encoder.Create(1280,720,3,30);
    cv::Mat x264_dst;

    //h264 decode
    CH264Decoder m_h264Decoder;
    m_h264Decoder.initial();

    //cv encode
    int jpeg_quality = 75;
    std::vector<int> params;
    params.push_back(cv::IMWRITE_JPEG_QUALITY);
    params.push_back(jpeg_quality);
    cv::Mat cv_dst;
    
    int nWaitTime =1;
    while (1)
    {
        videoCapture >>frame;
        if( frame.empty() )
            break;

        cv::imshow("src",frame);
        cv::Mat _frame;
        cv::resize(frame,_frame,cv::Size(),0.5,0.5);

        //cv ecode + cv decode
        double timePoint1 = cv::getTickCount();

        std::vector<uchar> jpgSize;
        cv::imencode(".jpg", _frame, jpgSize, params);
        cv_dst = cv::imdecode(jpgSize,cv::IMREAD_COLOR);

        cv::imshow("cv_dst",cv_dst);

        //x264 ecode + h264 decode
        double timePoint2 = cv::getTickCount();

        int size = m_x264Encoder.EncodeOneFrame(frame);
        uchar* data = nullptr;
        data = m_x264Encoder.GetEncodedFrame();

        m_h264Decoder.decode(data,size,x264_dst);
        cv::imshow("x264_dst",x264_dst);
        double timePoint3 = cv::getTickCount();
        //cv::Mat diff = x264_dst - frame;
        //cv::imshow("diff",diff);//查看编解码前后图像是否有差异

        printf("cv::encode size:%d Fps:%.2f, x264 encode size:%d,Fps:%.2f\n",
                jpgSize.size(),cv::getTickFrequency()/(timePoint2-timePoint1),
                size,cv::getTickFrequency()/(timePoint3-timePoint2));





        char chKey = cv::waitKey(nWaitTime);
        //ESC
        if (27 == chKey)break;
        else if (' ' == chKey) nWaitTime = !nWaitTime;
    }


    return 0;
}

上面代码中对比了cv::encode和x264对Mat进行编码,在编码后数据量的大小,和视频质量两个方面的效果对比。从数据量上来看,cv::encode编码后数据量远大于x264编码数据量,即使采用75%的压缩率,cv::encode的数据量仍远高于x264,并且压缩后视频质量不如x264压缩后视频质量。

H264编码+H264解码示例

H264EncoderDemo.cpp

//
// Created by liheng on 19-12-9.
//

#include "h264encoder.h"
#include "h264decoder.h"
#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat frame;
    cv::Mat dst;

    cv::VideoCapture videoCapture("/home/liheng/ADAS_Video/1120/ADAS_Video-20191120-151849.mp4");
    for (int i = 0; i < 10; ++i)
    {
        videoCapture >> frame;
    }

    h264Encoder h264;
    AvH264EncConfig conf;
    conf.width = 1280;
    conf.height = 720;
    conf.gop_size = 10;
    conf.max_b_frames = 0;
    conf.frame_rate = 30;
    h264.Init(conf);


    CH264Decoder m_h264Decoder;
    m_h264Decoder.initial();

    int jpeg_quality = 75;
    std::vector<int> params;
    params.push_back(cv::IMWRITE_JPEG_QUALITY);
    params.push_back(jpeg_quality);

    cv::Mat cvDst;
    int nWaitTime =1;
    while (1)
    {
        videoCapture >>frame;
        if( frame.empty() )
            break;

        cv::imshow("src",frame);
        cv::Mat _frame;
        cv::resize(frame,_frame,cv::Size(),0.5,0.5);

        double timePoint1 = cv::getTickCount();

        std::vector<uchar> jpgSize;
        cv::imencode(".jpg", _frame, jpgSize, params);

        double timePoint2 = cv::getTickCount();

        cvDst = cv::imdecode(jpgSize,cv::IMREAD_COLOR);
        cv::imshow("cvdecode",cvDst);


        // do encode
        AVPacket *pkt = h264.encode(frame);
        int size = pkt->size;
        uchar* data = nullptr;
        data = pkt->data;

        m_h264Decoder.decode(data,size,dst);
        cv::imshow("decode",dst);

        double timePoint3 = cv::getTickCount();


        //cv::Mat diff = dst - frame;
        //cv::imshow("diff",diff);//查看编解码前后图像是否有差异



        printf("cv::encode size:%d Fps:%.2f, h264 encode size:%d,Fps:%.2f\n",
                jpgSize.size(),cv::getTickFrequency()/(timePoint2-timePoint1),
                size,cv::getTickFrequency()/(timePoint3-timePoint2));





        char chKey = cv::waitKey(nWaitTime);
        //ESC
        if (27 == chKey)break;
        else if (' ' == chKey) nWaitTime = !nWaitTime;
    }


    return 0;
}

以上两个.cpp文件对应的CMakeLists.txt文件内容:
CMakeLists.txt

cmake_minimum_required(VERSION 3.12)
project(H264Demo)

set(CMAKE_CXX_STANDARD 11)

if (CMAKE_BUILD_TYPE STREQUAL "")
    message(STATUS "CMAKE_BUILD_TYPE not defined, 'Release' will be used")
    set(CMAKE_BUILD_TYPE "Release")
endif()


find_package( OpenMP REQUIRED)
if(OPENMP_FOUND)
    message("OPENMP FOUND")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

set(OpenCV_DIR /usr/local/opencv3.4.1/share/OpenCV)
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
    message(STATUS "OPENCV found")
else (OpenCV_FOUND)
    message(WARNING "OPENCV is disabled or not found")
    return()
endif()


add_executable(X264EncoderDemo X264EncoderDemo.cpp x264_encoder.cpp h264encoder.cpp h264decoder.cpp)
target_link_libraries(X264EncoderDemo ${OpenCV_LIBRARIES}
        x264
        avformat avdevice  avcodec avutil  avfilter postproc  swresample swscale)

add_executable(H264EncoderDemo H264EncoderDemo.cpp h264decoder.cpp h264encoder.cpp)
target_link_libraries(H264EncoderDemo ${OpenCV_LIBRARIES}
        avformat avdevice  avcodec avutil  avfilter postproc  swresample swscale)

针对x264编码,需要安装libx264库,安装命令为:sudo apt install libx264-dev,H264编解码需要安装ffmpeg库,同样采用apt命令进行安装即可。


我在ADAS程序中将视频发送部分修改为采用x264进行编码后,能够在不缩放视频的情况下进行传输,效果满足使用要求,非常满意。不过,随后,我发现,利用gstreamer-rtsp-sever也能够实现同样的效果,该方式采用rtsp视频流的形式进行视频传输,在接收端,可以直接利用cv::VideoCapture进行视频接收,不用编写额外的解码代码,非常方便,此外,也可以利用VLC media 进行视频接收,此时甚至不用专门编写视频接收代码了;建议有需求的可以尝试一下该方法,代码也是非常简单,此处就不贴出了(主要是感觉我的这个代码并未写完善,有缺陷)。



下面的是我的公众号二维码图片,欢迎关注。
图注:幼儿园的学霸

  • 10
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
要将OpenCV处理的视频流编码为H.264格式,可以使用FFmpeg库进行编码。以下是一些基本步骤: 1. 安装FFmpeg库,并将其包含到项目中。 2. 从OpenCV中读取视频流,并将每一帧存储为Mat对象。 3. 将Mat对象转换为AVFrame对象,使用av_image_fill_arrays()函数填充AVFrame的属性。 4. 创建AVCodecContext对象,并设置编码参数。这些参数包括视频分辨率、帧率、比特率等。 5. 使用avcodec_open2()函数打开编码器。 6. 使用avcodec_encode_video2()函数将每一帧AVFrame编码为H.264格式。 7. 将编码后的数据写入文件或网络流中。 以下是一个简单的例子: ```cpp #include <opencv2/opencv.hpp> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> int main(int argc, char* argv[]) { cv::VideoCapture cap("myvideo.mp4"); // 创建AVCodecContext对象 AVCodecContext* codec_ctx = avcodec_alloc_context3(nullptr); codec_ctx->codec_id = AV_CODEC_ID_H264; codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; codec_ctx->width = cap.get(CV_CAP_PROP_FRAME_WIDTH); codec_ctx->height = cap.get(CV_CAP_PROP_FRAME_HEIGHT); codec_ctx->time_base = { 1, cap.get(CV_CAP_PROP_FPS) }; codec_ctx->bit_rate = 1000000; // 打开编码器 AVCodec* codec = avcodec_find_encoder(codec_ctx->codec_id); avcodec_open2(codec_ctx, codec, nullptr); // 创建AVFrame对象 AVFrame* frame = av_frame_alloc(); frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; av_image_alloc(frame->data, frame->linesize, codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, 32); // 创建AVPacket对象 AVPacket pkt; av_init_packet(&pkt); while (cap.isOpened()) { cv::Mat mat_frame; cap >> mat_frame; // 将Mat对象转换为AVFrame对象 cv::Mat yuv_frame; cv::cvtColor(mat_frame, yuv_frame, CV_BGR2YUV_I420); memcpy(frame->data[0], yuv_frame.data, codec_ctx->width * codec_ctx->height); memcpy(frame->data[1], yuv_frame.data + codec_ctx->width * codec_ctx->height, codec_ctx->width * codec_ctx->height / 4); memcpy(frame->data[2], yuv_frame.data + codec_ctx->width * codec_ctx->height * 5 / 4, codec_ctx->width * codec_ctx->height / 4); // 编码帧 int ret = avcodec_send_frame(codec_ctx, frame); while (ret >= 0) { ret = avcodec_receive_packet(codec_ctx, &pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) return -1; // 将编码后的数据写入文件或网络流中 fwrite(pkt.data, 1, pkt.size, outfile); } } // 清理资源 av_packet_unref(&pkt); av_frame_free(&frame); avcodec_free_context(&codec_ctx); return 0; } ``` 注意:此例子仅用于演示基本的编码过程,实际应用中还需要处理异常情况、添加错误处理等。另外,编码后的H.264数据需要使用相应的解码器进行解码,例如FFmpeg库中的avcodec_decode_video2()函数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值