基于ffmpeg+opencv的h264解码显示及编码
2019-07-04 本文已影响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));
}