在android下调用第三方开源库ffmpeg,把手机预览数据流(AV_PIX_FMT_NV12)编码成AV_CODEC_ID_H264格式的视频流。
写的一个简单的c++ demo如下
Android.mk里添加如下引用
LOCAL_C_INCLUDES += $(LOCAL_PATH)/ext/ffmpeg/default/3.1.2/include/android/
LOCAL_SHARED_LIBRARIES += libffmpeg
my_h264.h
#ifndef __MY_H264_H__
#define __MY_H264_H__
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
}
#endif
#include
#include "my_mutex.h"
typedef enum{
QUALITY_VERY_HIGHT,
QUALITY_HIGHT,
QUALITY_MEAIUM,
QUALITY_LOW,
QUALITY_VERYLOW
}eEncoderQuality;
class MyH264
{
public:
MyH264();
virtual ~MyH264();
int reset();
int open(AVPixelFormat nAVPixelFormat, int FrameWidth, int FrameHeight, int FrameRate, eEncoderQuality EncoderQuality);
int close();
int record(unsigned char* BufY);
int setData(AVPacket* pPacket);
int getData(uint8_t** Data, int* Size);
private:
AVFormatContext* mpAVFormatContext;
AVCodecContext* mpAVCodecContext;
AVStream* mpAVStream;
AVFrame* mpAVFrame;
uint8_t* mFrameInData;
int mFrameInSize;
uint8_t* mFrameOutData;
int mFrameOutSize;
double mFrameInterval;
unsigned int mFrameIndex;
int mDataReadyFlag;
Mutex mLock;
};
#endif//__MY_H264_H__
my_h264.cpp
#include "my_h264.h"
#include "my_log.h"
#define TIME_BASE_DEN (500)
#define OUT_BUF_SIZE_MAX (1000000)
MyH264::MyH264()
{
MY_LOGE("+");
reset();
MY_LOGE("-");
}
MyH264::~MyH264()
{
MY_LOGE("+");
close();
MY_LOGE("-");
}
int MyH264::reset()
{
MY_LOGE("+");
mpAVFormatContext = NULL;
mpAVCodecContext = NULL;
mpAVStream = NULL;
mpAVFrame = NULL;
mFrameInData = NULL;
mFrameOutData = NULL;
mFrameOutSize = 0;
mFrameInterval = 0.0;
mFrameIndex = 0;
mDataReadyFlag = 0;
MY_LOGE("-");
return 0;
}
int MyH264::open(AVPixelFormat nAVPixelFormat, int FrameWidth, int FrameHeight, int FrameRate, eEncoderQuality EncoderQuality)
{
Mutex::Autolock lock(mLock);
MY_LOGE("+");
reset();
int ret = 0;
mFrameInterval = (1.0 / (double)FrameRate);
char strEncoderParam[5][64];
memset(strEncoderParam, 0, sizeof(strEncoderParam));
switch (EncoderQuality)
{
case QUALITY_VERYLOW:
strcpy(strEncoderParam[0], "30");
strcpy(strEncoderParam[1], "4.1");
strcpy(strEncoderParam[2], "superfast");
strcpy(strEncoderParam[3], "zerolatency");
strcpy(strEncoderParam[4], "baseline");
break;
case QUALITY_LOW:
strcpy(strEncoderParam[0], "25");
strcpy(strEncoderParam[1], "4.1");
strcpy(strEncoderParam[2], "superfast");
strcpy(strEncoderParam[3], "zerolatency");
strcpy(strEncoderParam[4], "baseline");
break;
case QUALITY_MEAIUM:
strcpy(strEncoderParam[0], "22");
strcpy(strEncoderParam[1], "4.1");
strcpy(strEncoderParam[2], "superfast");
strcpy(strEncoderParam[3], "zerolatency");
strcpy(strEncoderParam[4], "baseline");
break;
case QUALITY_HIGHT:
strcpy(strEncoderParam[0], "20");
strcpy(strEncoderParam[1], "4.1");
strcpy(strEncoderParam[2], "medium");
strcpy(strEncoderParam[3], "zerolatency");
strcpy(strEncoderParam[4], "main");
break;
case QUALITY_VERY_HIGHT:
strcpy(strEncoderParam[0], "17");
strcpy(strEncoderParam[1], "5.1");
strcpy(strEncoderParam[2], "slow");
strcpy(strEncoderParam[3], "zerolatency");
strcpy(strEncoderParam[4], "high");
break;
default:
break;
}
avcodec_register_all();
av_register_all();
mpAVFormatContext = avformat_alloc_context();
if (!mpAVFormatContext)
{
MY_LOGE("avformat_alloc_context error");
close();
return -1;
}
AVCodec* pAVCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!pAVCodec)
{
MY_LOGE("avcodec_find_encoder error");
close();
return -1;
}
mpAVStream = avformat_new_stream(mpAVFormatContext, pAVCodec);
if (!mpAVStream) {
MY_LOGE("avformat_new_stream error");
close();
return -1;
}
mpAVStream->avg_frame_rate.num = TIME_BASE_DEN;
mpAVStream->avg_frame_rate.den = 1;
mpAVCodecContext = mpAVStream->codec;
mpAVCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
mpAVCodecContext->codec_id = AV_CODEC_ID_H264;
mpAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
mpAVCodecContext->bit_rate = 400000;
mpAVCodecContext->width = FrameWidth;
mpAVCodecContext->height = FrameHeight;
mpAVCodecContext->time_base.den = TIME_BASE_DEN;//mFrameRate;
mpAVCodecContext->time_base.num = 1;
mpAVCodecContext->gop_size = 12;
mpAVCodecContext->pix_fmt = nAVPixelFormat;
mpAVCodecContext->qmin = 10;
mpAVCodecContext->qmax = 51;
mpAVCodecContext->max_b_frames = 3;
av_opt_set(mpAVCodecContext->priv_data, "crf", strEncoderParam[0], 0);
av_opt_set(mpAVCodecContext->priv_data, "level", strEncoderParam[1], 0);
av_opt_set(mpAVCodecContext->priv_data, "preset", strEncoderParam[2], 0);
av_opt_set(mpAVCodecContext->priv_data, "tune", strEncoderParam[3], 0);
AVDictionary *pAVDictionary = NULL;
av_dict_set(&pAVDictionary, "profile", strEncoderParam[4], 0);
ret = avcodec_open2(mpAVCodecContext, pAVCodec, &pAVDictionary);
if (ret < 0)
{
MY_LOGE("avcodec_open2 error");
av_dict_free(&pAVDictionary);
close();
return -1;
}
if (ret < 0)
{
MY_LOGE("avformat_write_header error");
close();
return -1;
}
mpAVFrame = av_frame_alloc();
if(!mpAVFrame)
{
MY_LOGE("av_frame_alloc error");
close();
return -1;
}
mpAVFrame->pts = 0;
mFrameInSize = avpicture_get_size(mpAVCodecContext->pix_fmt, mpAVCodecContext->width, mpAVCodecContext->height);
MY_LOGE("mFrameInSize=%d", mFrameInSize);
mFrameInData = new uint8_t[mFrameInSize];
if(!mFrameInData)
{
MY_LOGE("new mFrameInData error");
close();
return -1;
}
mFrameOutData = new uint8_t[OUT_BUF_SIZE_MAX];
if(!mFrameOutData)
{
MY_LOGE("new mFrameOutData error");
close();
return -1;
}
MY_LOGE("-");
return 0;
}
int MyH264::close()
{
Mutex::Autolock lock(mLock);
MY_LOGE("+");
if(mpAVCodecContext)
{
avcodec_close(mpAVCodecContext);
mpAVCodecContext = NULL;
}
if (mpAVFormatContext)
{
avio_close(mpAVFormatContext->pb);
avformat_free_context(mpAVFormatContext);
mpAVFormatContext = NULL;
}
if (mpAVFrame)
{
av_frame_free(&mpAVFrame);
mpAVFrame = NULL;
}
if (mFrameInData)
{
delete [] mFrameInData;
mFrameInData = NULL;
}
if (mFrameOutData)
{
delete [] mFrameOutData;
mFrameOutData = NULL;
}
reset();
MY_LOGE("-");
return 0;
}
int MyH264::record(unsigned char* BufY)
{
Mutex::Autolock lock(mLock);
double TimeStemp = mFrameInterval * (double)mFrameIndex;
unsigned int nPtsIndex = (int)(TimeStemp * TIME_BASE_DEN + 0.5);
MY_LOGE("+ mFrameIndex=%d TimeStemp=%lf , nPtsIndex=%d", mFrameIndex, TimeStemp, nPtsIndex);
memset(mFrameInData, 0, mFrameInSize);
memcpy(mFrameInData, BufY, mFrameInSize);
int ret = 0;
ret = av_image_fill_arrays(mpAVFrame->data, mpAVFrame->linesize, mFrameInData, mpAVCodecContext->pix_fmt, mpAVCodecContext->width, mpAVCodecContext->height, 1);
if (ret < 0)
{
MY_LOGE("av_image_fill_arrays error");
close();
return -1;
}
AVPacket Packet = { 0 };
av_init_packet(&Packet);
mpAVFrame->pict_type = AV_PICTURE_TYPE_NONE;
mpAVFrame->pts = nPtsIndex * av_rescale_q(1, mpAVCodecContext->time_base, mpAVStream->time_base);
int GotPacket = 0;
ret = avcodec_encode_video2(mpAVCodecContext, &Packet, mpAVFrame, &GotPacket);
if (ret < 0)
{
MY_LOGE("avcodec_encode_video2 error");
av_packet_unref(&Packet);
close();
return -1;
}
//MY_LOGE("GotPacket=%d Packet.size=%d", GotPacket, Packet.size);
if (!ret && GotPacket && Packet.size)
{
Packet.stream_index = mpAVStream->index;
setData(&Packet);
}
av_packet_unref(&Packet);
mFrameIndex++;
MY_LOGE("-");
return 0;
}
int MyH264::setData(AVPacket* pPacket)
{
MY_LOGE("+ mFrameIndex=%d pPacket->flags=%d", mFrameIndex, pPacket->flags);
memset(mFrameOutData, 0, OUT_BUF_SIZE_MAX);
if (pPacket->flags == AV_PKT_FLAG_KEY)
{
memcpy(mFrameOutData, mpAVCodecContext->extradata, mpAVCodecContext->extradata_size);
memcpy((mFrameOutData + mpAVCodecContext->extradata_size), pPacket->data, pPacket->size);
mFrameOutSize = mpAVCodecContext->extradata_size + pPacket->size;
}
else
{
memcpy(mFrameOutData, pPacket->data, pPacket->size);
mFrameOutSize = pPacket->size;
}
mDataReadyFlag = 1;
return 0;
MY_LOGE("-");
}
int MyH264::getData(uint8_t** Data, int* Size)
{
Mutex::Autolock lock(mLock);
MY_LOGE("+ mDataReadyFlag=%d mFrameOutSize=%d", mDataReadyFlag, mFrameOutSize);
if (mDataReadyFlag == 0)
{
MY_LOGE("data no ready");
return -1;
}
*Size = mFrameOutSize;
*Data = mFrameOutData;
mDataReadyFlag = 0;
MY_LOGE("-");
return 0;
}