#include "YfcfPlayer.h"
#include "err_code.hpp"
extern "C"
{
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
}
YfcfPlayer::YfcfPlayer()
{
m_hw_device_ctx = NULL;
m_decoder_ctx = NULL;
mW = mH = 0;
mMaxLen4BufEnc = MAX_BUF_LEN_DEFAULT;
mMaxLen4BufDec =MAX_BUF_LEN_DEFAULT;
mShouldEnd = false;
mLogSt4Dec = mLogSt4Enc = false;
}
YfcfPlayer::~YfcfPlayer()
{
}
static void reader_(void* arg){
YfcfPlayer* p = (YfcfPlayer*)arg;
p->doRead();
}
static void decoder_(void* arg){
YfcfPlayer* p = (YfcfPlayer*)arg;
p->doDecode();
}
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
{
const enum AVPixelFormat *p;
for (p = pix_fmts; *p != -1; p++) {
if (*p == AV_PIX_FMT_VAAPI)
return *p;
}
LOGE(TAG, "Failed to get HW surface format.\n");
return AV_PIX_FMT_NONE;
}
int32_t YfcfPlayer::strtPlay(const string& _url,bool useGpuBuf){
int32_t ret = 0;
mUrl = _url;
mShouldEnd = false;
mSwitch = true;
mSwitch4Dec = true;
mUseGpuBUf = useGpuBuf;
mTidReader = thread(reader_,this);
return ret;
}
int32_t YfcfPlayer::doRead(){
rst:
int32_t ret = 0;
int32_t lVideoIdx = -1;
AVCodec *decoder = NULL;
AVPacket* lAvPack = NULL;
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_VAAPI;
AVDictionary *lDic = NULL;
AVFormatContext *input_ctx = NULL;
std::unique_lock<mutex> lLock(mLock4BufEnc,std::defer_lock);
av_dict_set(&lDic, "protocol_whitelist", "udp,rtp,file", 0);
ret = avformat_open_input(&input_ctx, mUrl.c_str(), NULL, &lDic) ;
if( ret != 0) {
LOGE(TAG, "Cannot open input file %s %d\n", mUrl.c_str(),ret);
ret = -1;
goto fail;
}
if(avformat_find_stream_info(input_ctx, NULL) < 0) {
LOGE(TAG, "Cannot find input stream information.\n");
ret = -2;
goto fail;
}
/* find the video stream information */
lVideoIdx = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, (const AVCodec **)&decoder, 0);
if (lVideoIdx < 0) {
LOGE(TAG,"Cannot find a video stream in the input file\n");
ret = -3;
goto fail;
}
mVdTimebase = input_ctx->streams[lVideoIdx]->time_base;
if(input_ctx->streams[lVideoIdx]->avg_frame_rate.num==0||
input_ctx->streams[lVideoIdx]->avg_frame_rate.den==0){
LOGE(TAG,"invalid fps\n");
ret = -3;
goto fail;
}
mFps = input_ctx->streams[lVideoIdx]->avg_frame_rate.num/input_ctx->streams[lVideoIdx]->avg_frame_rate.den;
for (size_t i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);
if (!config) {
LOGE(TAG, "Decoder %s does not support device type %s.\n",
decoder->name, av_hwdevice_get_type_name(AV_HWDEVICE_TYPE_VAAPI));
ret = -4;
goto fail;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == AV_HWDEVICE_TYPE_VAAPI) {
m_hw_pix_fmt = config->pix_fmt;
break;
}
}
if (!(m_decoder_ctx = avcodec_alloc_context3(decoder))){
LOGE(TAG,"fail to alloc dec ctx");
ret = -5;
goto fail;
}
ret = avcodec_parameters_to_context(m_decoder_ctx, input_ctx->streams[lVideoIdx]->codecpar);
if (ret < 0)
{
LOGE(TAG,"vcodec_parameters_to_context fail %d");
goto fail;
}
//填入回调函数 通过这个函数 编解码器能够知道显卡支持的像素格式
m_decoder_ctx->get_format = get_hw_format;
mW = m_decoder_ctx->width;
mH = m_decoder_ctx->height;
ret = av_hwdevice_ctx_create(&m_hw_device_ctx,AV_HWDEVICE_TYPE_VAAPI,
NULL, NULL, 0);
if (ret< 0) {
LOGE(TAG, "Failed to create specified HW device.\n");
goto fail;
}
//绑定编解码器上下文和硬件设备信息上下文
m_decoder_ctx->hw_device_ctx = av_buffer_ref(m_hw_device_ctx);
//绑定完成后 打开编解码器
if ((ret = avcodec_open2(m_decoder_ctx, decoder, NULL)) < 0) {
LOGE(TAG, "Failed to open codec for stream #%u\n", lVideoIdx);
goto fail;
}
mTidDecoder = thread(decoder_,this);
while (mSwitch)
{
lAvPack = av_packet_alloc();
if(UNLIKELY(lAvPack==NULL)){
LOGE(TAG,"this should never happen");
}
ret = av_read_frame(input_ctx, lAvPack);
if(ret!=0){
av_packet_free(&lAvPack);
LOGW(TAG,"av_read_frame err %d %d %d %d",ret,AVERROR(EINVAL),AVERROR(ENOMEM),AVERROR_EOF);
break;
}
if (lVideoIdx != lAvPack->stream_index){
av_packet_free(&lAvPack);
continue;
}
lLock.lock();
if(mBufEnc.size()>mMaxLen4BufEnc){
if(mLogSt4Enc == false){
LOGW(TAG,"BUF FULL");
mLogSt4Enc = true;
}
mCond4BufEnc.wait(lLock);
}else{
if(mLogSt4Enc == true){
LOGW(TAG,"BUF FULL drained");
mLogSt4Enc = false;
}
}
mBufEnc.push_back(lAvPack);
lLock.unlock();
mCond4BufEnc.notify_one();
}
if(ret != AVERROR_EOF){
mSwitch4Dec = false;
}
mSwitch = false;
mCond4BufEnc.notify_one();
LOGI(TAG,"reader will end");
mTidDecoder.join();
fail:
avcodec_free_context(&m_decoder_ctx);
avformat_close_input(&input_ctx);
av_buffer_unref(&m_hw_device_ctx);
LOGI(TAG,"reader end");
if(!mShouldEnd){
mSwitch4Dec = mSwitch = true;
goto rst;
}
return ret;
}
#include "MiscUtils.h"
int32_t YfcfPlayer::doDecode(){
int32_t ret = 0;
AVPacket* lpAvPack = NULL;
AVFrame* lpAvFrame_dec = NULL;
AVFrame* lpAvFrame_cpu = NULL;
AVFrame* lpAvFrame_tmp = NULL;
std::unique_lock<mutex> lLock(mLock4BufEnc,std::defer_lock);
std::unique_lock<mutex> lLock4Dec(mLock4BufDec,std::defer_lock);
uint8_t *buffer = NULL;
int size = 0;
while (mSwitch4Dec)
{
lLock.lock();
if(mBufEnc.size()==0){
if(mSwitch == false){
lLock.unlock();
break;
}
mCond4BufEnc.wait(lLock);
lLock.unlock();
continue;
}
lpAvPack = mBufEnc.front();
mBufEnc.pop_front();
lLock.unlock();
mCond4BufEnc.notify_one();
uint64_t lastT = 0;
uint64_t curT = 0;
lastT= bag_get_boot_time();
while(true){
ret = avcodec_send_packet(m_decoder_ctx, lpAvPack);
if (ret == AVERROR(EAGAIN)) {
continue;
}else if (ret == 0 )
{
break;
}
LOGE(TAG,"DEC ERR %d %d %d %d",ret,AVERROR(EINVAL),AVERROR(ENOMEM),AVERROR_EOF);
goto err;
}
av_packet_free(&lpAvPack);
lpAvPack = NULL;
lpAvFrame_dec = av_frame_alloc();
if(mUseGpuBUf==false)
lpAvFrame_cpu = av_frame_alloc();
if(lpAvFrame_dec == NULL ){
LOGE(TAG,"NO MEM");
goto err;
}
if(mUseGpuBUf==false && lpAvFrame_cpu == NULL){
LOGE(TAG,"NO MEM");
goto err;
}
ret = avcodec_receive_frame(m_decoder_ctx,lpAvFrame_dec);
if(ret != 0 ){
av_frame_free(&lpAvFrame_dec);
av_frame_free(&lpAvFrame_cpu);
continue;
}
if (lpAvFrame_dec->format == m_hw_pix_fmt) {
if(mUseGpuBUf){
lLock4Dec.lock();
if(mBufDec.size()>mMaxLen4BufDec){
if(mLogSt4Dec == false){
LOGW(TAG,"buf_decoded full %p %d,drop frame",this,mBufDec.size());
mLogSt4Dec = true;
}
av_frame_free(&lpAvFrame_dec);
} else{
mBufDec.push_back(lpAvFrame_dec);
if(mLogSt4Dec == true){
LOGW(TAG,"buf_decoded full %p %d,drained",this,mBufDec.size());
mLogSt4Dec = false;
}
}
lLock4Dec.unlock();
mCond4BufDec.notify_one();
lpAvFrame_dec = NULL;
continue;
}
if ((ret = av_hwframe_transfer_data(lpAvFrame_cpu, lpAvFrame_dec, 0)) < 0) {
LOGE(TAG,"Error transferring the data to system memory");
goto err;
}
lpAvFrame_tmp = lpAvFrame_cpu;
}
else{
LOGE(TAG,"this should never happen");
lpAvFrame_tmp = lpAvFrame_dec;
}
size = av_image_get_buffer_size(AV_PIX_FMT_NV12,lpAvFrame_tmp->width,lpAvFrame_tmp->height, 1);
buffer = (uint8_t*)av_malloc(size);
if(buffer == NULL){
LOGE(TAG, "Can not alloc buffer\n");
goto err;
}
ret = av_image_copy_to_buffer(buffer, size,
(const uint8_t *const *)lpAvFrame_tmp->data,
(const int *)lpAvFrame_tmp->linesize, AV_PIX_FMT_NV12,
lpAvFrame_tmp->width, lpAvFrame_tmp->height, 1);
if (ret < 0)
{
LOGE(TAG, "Can not copy image to buffer\n");
goto err;
}
lLock4Dec.lock();
if(mBufDecoded.size()>mMaxLen4BufDec){
if(mLogSt4Dec == false){
LOGW(TAG,"buf_decoded full %p %d,drop frame",this,mBufDecoded.size());
mLogSt4Dec = true;
}
av_freep(&buffer);
}else{
mBufDecoded.push_back(buffer);
if(mLogSt4Dec == true){
mLogSt4Dec = false;
LOGW(TAG,"buf_decoded full %p %d,drained",this,mBufDecoded.size());
}
}
lLock4Dec.unlock();
mCond4BufDec.notify_one();
buffer = nullptr;
av_frame_free(&lpAvFrame_cpu);
av_frame_free(&lpAvFrame_dec);
curT = bag_get_boot_time();
if(curT-lastT>25000)
LOGI(TAG,"DEC CONSUMED %" PRIu64" %" PRIu64 " %" PRIu64,lastT,curT,curT-lastT );
}
err:
LOGI(TAG,"dec will end");
mSwitch = false;
mCond4BufEnc.notify_one();
lLock.lock();
while (mBufEnc.size()>0)
{
AVPacket* p = mBufEnc.front();
mBufEnc.pop_front();
av_packet_free(&p);
}
lLock.unlock();
lLock4Dec.lock();
if(mUseGpuBUf==false){
while (mBufDecoded.size()>0)
{
uint8_t* p = mBufDecoded.front();
mBufDecoded.pop_front();
av_freep(&p);
}
}else{
while (mBufDec.size()>0)
{
AVFrame* p = mBufDec.front();
mBufDec.pop_front();
av_frame_free(&p);
}
}
lLock4Dec.unlock();
if(lpAvPack)
av_packet_free(&lpAvPack);
if(lpAvFrame_dec)
av_frame_free(&lpAvFrame_dec);
if(buffer){
av_freep(&buffer);
}
LOGI(TAG,"dec end");
return ret;
}
bool YfcfPlayer::getSwitch(){
return mSwitch;
}
void* YfcfPlayer::getOneFrame(){
std::unique_lock<mutex> lLock4Dec(mLock4BufDec,std::defer_lock);
void* f;
lLock4Dec.lock();
if(mUseGpuBUf==false){
if(mBufDecoded.size()==0){
mCond4BufDec.wait(lLock4Dec);
if(mBufDecoded.size()==0){
lLock4Dec.unlock();
return nullptr;
}
}
f = mBufDecoded.front();
mBufDecoded.pop_front();
}else{
if(mBufDec.size()==0){
mCond4BufDec.wait(lLock4Dec);
if(mBufDec.size()==0){
lLock4Dec.unlock();
return nullptr;
}
}
f = mBufDec.front();
mBufDec.pop_front();
}
lLock4Dec.unlock();
return f;
}
void YfcfPlayer::stopPlay(){
mShouldEnd = true;
mSwitch4Dec = false;
mSwitch = false;
mCond4BufEnc.notify_all();
mCond4BufDec.notify_all();
mTidReader.join();
}