美文网首页工作生活
基于ffmpeg+opencv的h264解码显示及编码

基于ffmpeg+opencv的h264解码显示及编码

作者: 一笔春秋 | 来源:发表于2019-07-04 14:40 被阅读0次
    • 解码显示

    参考

    • decode_video.h
    #ifndef _DECODE_VIDEO_H 
    #define _DECODE_VIDEO_H 
    #include "opencv2/opencv.hpp" 
    extern "C" { 
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h" 
    #include "libswscale/swscale.h" 
    }; 
    typedef struct video_info 
    { 
        AVPacket *packet; 
        AVFrame *pAvFrame; 
        AVCodec *pCodec; 
        AVFormatContext *pFormatCtx; 
        AVCodecContext *pCodecCtx; 
        SwsContext *img_convert_ctx; 
        int videoindex; 
        }video_t; 
    #ifndef G_DECODE_VIDEO_H 
    #define G_DECODE_VIDEO_H  extern 
    #endif 
    G_DECODE_VIDEO_H video_t* video_init(const char* video_filename,int*ret); 
    G_DECODE_VIDEO_H int video_get_frame(video_t* handel,cv::Mat* pCvMat); 
    G_DECODE_VIDEO_H int video_get_alltime(video_t* handel); G_DECODE_VIDEO_H int video_seek_frame(video_t* handel,long time_start); 
    G_DECODE_VIDEO_H int video_uninit(video_t* handel); 
    #endif
    
    • decode_video.cpp
    #include "decode_video.h" 
    G_DECODE_VIDEO_H video_t* video_init(const char* video_filename,int *ret) 
    {
        video_t* video_info = (video_t*)malloc(sizeof(video_t));     
        video_info->packet = NULL;
        video_info->pAvFrame = NULL; 
        video_info->pCodec = NULL; 
        video_info->pFormatCtx = NULL; 
        video_info->pCodecCtx = NULL; 
        video_info->img_convert_ctx = NULL; 
        video_info->videoindex = -1; 
        av_register_all();
        if (avformat_open_input(&(video_info->pFormatCtx),video_filename, NULL, NULL) != 0) 
        { 
            //无法打开文件
            (*ret) = -1; 
            return NULL;
        }
        if (avformat_find_stream_info(video_info->pFormatCtx,NULL) < 0)
        {
            //无法查找到流信息  
            (*ret) = -2; 
            return NULL; 
        } 
        for(int i = 0;i < video_info->pFormatCtx->nb_streams;i++) 
        { 
            if (video_info->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
            { 
                video_info->videoindex = i; 
                break;
            } 
        } 
        if(video_info->videoindex == -1)
        { 
             //无法找到视频流 
             (*ret) = -3; 
             return NULL;
        } 
        video_info->pCodecCtx = video_info->pFormatCtx->streams[video_info->videoindex]->codec; 
        video_info->pCodec = avcodec_find_decoder(video_info->pCodecCtx->codec_id);
        if (video_info->pCodec == NULL) 
        {
             (*ret) = -4;
             return NULL; 
        } 
        if (avcodec_open2(video_info->pCodecCtx,video_info->pCodec, NULL) < 0) 
        { 
            //无法打开解码器 
            (*ret) = -5; 
            return NULL; 
        } 
        video_info->pAvFrame = av_frame_alloc(); 
        int y_size = video_info->pCodecCtx->width * video_info->pCodecCtx->height; 
        video_info->packet = (AVPacket *)av_malloc(sizeof(AVPacket)); 
        av_new_packet(video_info->packet, y_size); 
        (*ret) = 0; 
        return video_info; 
    } 
    void video_getimg(AVCodecContext * pCodecCtx, SwsContext * img_convert_ctx, AVFrame * pFrame,cv::Mat* pCvMat) 
    { 
        if (pCvMat->empty()) 
        { 
            pCvMat->create(cv::Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3); 
        } 
        AVFrame *pFrameRGB = NULL; 
        uint8_t *out_bufferRGB = NULL; 
        pFrameRGB = av_frame_alloc(); 
    
        //给pFrameRGB帧加上分配的内存; 
        int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); 
        out_bufferRGB = new uint8_t[size]; 
        avpicture_fill((AVPicture *)pFrameRGB, out_bufferRGB, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); 
        
        //YUV to RGB 
        sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); 
        memcpy(pCvMat->data, out_bufferRGB, size); 
        delete[] out_bufferRGB; 
        av_free(pFrameRGB); 
    } 
    
    G_DECODE_VIDEO_H int video_get_frame(video_t* handel,cv::Mat* pCvMat) 
    { 
        int result = 0; 
        int pic_got = -1; 
        result = av_read_frame(handel->pFormatCtx,handel->packet); 
        if(result < 0) 
        { 
            //视频播放完成 
            pCvMat = NULL; 
            return -6; 
        } 
        
        //此处需注意,视频播放完成后,并不会输出-6,而是会再进行解码导致解码错误输出-7 
        if (handel->packet->stream_index == handel->videoindex) 
        { 
            int state = avcodec_decode_video2(handel->pCodecCtx, handel->pAvFrame, &pic_got, handel->packet); 
            if (state < 0) 
            { 
                //解码错误 
                pCvMat = NULL; 
                return -7; 
            } 
            if (pic_got) 
            { 
                if (handel->img_convert_ctx == NULL) 
                { 
                    handel->img_convert_ctx = sws_getContext(handel->pCodecCtx->width, handel->pCodecCtx->height,handel->pCodecCtx->pix_fmt, handel->pCodecCtx->width, handel->pCodecCtx->height,AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); 
                } 
                if (pCvMat->empty()) 
                { 
                    pCvMat->create(cv::Size(handel->pCodecCtx->width, handel->pCodecCtx->height), CV_8UC3); 
                } 
                if (handel->img_convert_ctx != NULL) 
                { 
                    video_getimg(handel->pCodecCtx, handel->img_convert_ctx, handel->pAvFrame,pCvMat); 
                }
            } 
        } 
        av_free_packet(handel->packet); 
        return 0; 
    } 
    G_DECODE_VIDEO_H int video_get_alltime(video_t* handel) { 
        int hours, mins, secs, us; 
        if (handel->pFormatCtx->duration != AV_NOPTS_VALUE) 
        { 
            int64_t duration = handel->pFormatCtx->duration + 5000; 
            secs = duration / AV_TIME_BASE; 
            us = duration % AV_TIME_BASE; 
            mins = secs / 60; 
            secs %= 60; 
            hours = mins / 60; 
            mins %= 60; 
            return (hours * 3600 + mins * 60 + secs); 
        } 
        else 
        { 
            return 0; 
        } 
    } 
    
    G_DECODE_VIDEO_H int video_seek_frame(video_t* handel,long time_start) 
    { 
        int64_t seek_pos = 0; 
        if (time_start < 0) 
        {  
            return -1; 
        } 
        seek_pos = time_start * AV_TIME_BASE; 
        if (handel->pFormatCtx->start_time != AV_NOPTS_VALUE) 
        { 
            seek_pos += handel->pFormatCtx->start_time; 
        } 
        if (av_seek_frame(handel->pFormatCtx, -1, seek_pos, AVSEEK_FLAG_ANY) < 0) 
        { 
            return -2; 
        } 
        return 0; 
    } 
    
    G_DECODE_VIDEO_H int video_uninit(video_t* handel) 
    { 
        if(handel != NULL) 
        { 
            av_free_packet(handel->packet); 
            avcodec_close(handel->pCodecCtx); 
            avformat_close_input(&(handel->pFormatCtx)); 
            return 0; 
        } 
        else 
        { 
            return -1; 
        } 
    }
    
    • 编码

    参考

    • x264_encoder.h
    #ifndef _X264_ENCODER_H
    #define _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
    
    • x264_encoder.cpp
    #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"
    #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));
    }
    
    

    相关文章

      网友评论

        本文标题:基于ffmpeg+opencv的h264解码显示及编码

        本文链接:https://www.haomeiwen.com/subject/qoslhctx.html