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

  • 解码显示

参考

  • 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));
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容