#include "FormatIn.hpp"
|
|
#include <stdexcept>
|
#include <unistd.h>
|
|
extern "C"{
|
#include <libavformat/avformat.h>
|
#include <libavcodec/avcodec.h>
|
#include <libavutil/pixdesc.h>
|
#include <libavutil/opt.h>
|
#include <libavutil/avassert.h>
|
#include <libavutil/imgutils.h>
|
#include <libswscale/swscale.h>
|
}
|
|
#include "../log/log.hpp"
|
#include "../configure/conf.hpp"
|
|
#include "../property/VideoProp.hpp"
|
|
#include "../../common/gpu/info.h"
|
|
using namespace logif;
|
|
namespace ffwrapper{
|
FormatIn::FormatIn(bool hw)
|
:ctx_(NULL)
|
,dec_ctx_(NULL)
|
,vs_idx_(-1)
|
,as_idx_(-1)
|
,hw_accl_(hw)
|
,io_ctx_(NULL)
|
,read_io_buff_(NULL)
|
,read_io_buff_size_(32768)
|
,handle_gb28181(NULL)
|
,fps_(25.0)
|
{}
|
|
FormatIn::~FormatIn()
|
{
|
logIt("free format in");
|
if(dec_ctx_){
|
avcodec_close(dec_ctx_);
|
avcodec_free_context(&dec_ctx_);
|
dec_ctx_ = NULL;
|
}
|
|
if(ctx_){
|
avformat_close_input(&ctx_);
|
ctx_ = NULL;
|
}
|
|
if (handle_gb28181){
|
delete handle_gb28181;
|
}
|
|
if(io_ctx_){
|
av_freep(&io_ctx_->buffer);
|
av_freep(&io_ctx_);
|
io_ctx_ = NULL;
|
}
|
|
}
|
|
////////////////////////////////////////////////////////////////////////
|
int FormatIn::openWithCustomIO(void *opaque, read_packet fn, AVDictionary **options/*=NULL*/){
|
read_io_buff_ = (uint8_t*)av_malloc(read_io_buff_size_);
|
if(!read_io_buff_){
|
logIt("open with custom io alloc read io buff error\n");
|
return -1;
|
}
|
|
io_ctx_ = avio_alloc_context(read_io_buff_, read_io_buff_size_, 0, opaque, fn, NULL, NULL);//opaque
|
if(!io_ctx_){
|
logIt("open with custom io create custom avio error\n");
|
return -1;
|
}
|
|
ctx_ = avformat_alloc_context();
|
if(!ctx_){
|
logIt("open with custom io create format error\n");
|
return -1;
|
}
|
|
ctx_->pb = io_ctx_;
|
|
// auto err = av_probe_input_buffer(ctx_->pb, &ctx_->iformat, NULL, NULL, 0, 0);
|
// if(err != 0){
|
// logIt("open with custom io prob input buffer error:%d err: %s\n", err, getAVErrorDesc(err).c_str());
|
// return -1;
|
// }
|
|
return 0;
|
}
|
|
int FormatIn::openGb28181(const char *filename, AVDictionary **options){
|
|
std::string fn = filename;
|
//GB28181API gb28181(fn);
|
if (handle_gb28181){
|
delete handle_gb28181;
|
}
|
handle_gb28181 = new GB28181API;
|
if(handle_gb28181->addCamera(fn) == -1){
|
logIt("do addCamera Error\n");
|
return -1;
|
}
|
|
int ret = openWithCustomIO(handle_gb28181, handle_gb28181->readData, options);
|
if(ret < 0){
|
logIt("do openWithCustomIO failed:%d",ret);
|
}else{
|
ret = avformat_open_input(&ctx_, "", NULL, options);
|
}
|
|
// if(ret < 0){
|
// logIt("open %s failed:%s",filename,
|
// getAVErrorDesc(ret).c_str());
|
// }
|
|
return ret;
|
}
|
|
/////////////////////////////////////////////////////////////////////////
|
int FormatIn::open(const char *filename, AVDictionary **options){
|
|
const int ret = avformat_open_input(&ctx_, filename, NULL, options);
|
// if(ret < 0){
|
// logIt("open %s failed:%s",filename,
|
// getAVErrorDesc(ret).c_str());
|
// }
|
|
return ret;
|
}
|
|
bool FormatIn::findStreamInfo(AVDictionary **options){
|
|
const int ret = avformat_find_stream_info(ctx_, options);
|
if(ret < 0){
|
logIt("find %s stream info failed:%s",
|
ctx_->filename,getAVErrorDesc(ret).c_str());
|
|
return false;
|
}
|
|
// logIt("there are %d stream", ctx_->nb_streams);
|
|
for (int i = 0; i < ctx_->nb_streams; ++i)
|
{
|
auto type = ctx_->streams[i]->codecpar->codec_type;
|
logIt("there are %d stream, stream %d, type %d", ctx_->nb_streams, i, type);
|
|
if (type == AVMEDIA_TYPE_VIDEO){
|
vs_idx_ = i;
|
|
auto in = ctx_->streams[i];
|
if(in->r_frame_rate.num >=1 && in->r_frame_rate.den >= 1){
|
fps_ = av_q2d(in->r_frame_rate);
|
}else if(in->avg_frame_rate.num >=1 && in->avg_frame_rate.den >= 1){
|
fps_ = av_q2d(in->avg_frame_rate);
|
}
|
logIt("in stream video fps %f, time_base: %d : %d, size: %dx%d", fps_, in->time_base.num, in->time_base.den, in->codecpar->width, in->codecpar->height);
|
}
|
if (type == AVMEDIA_TYPE_AUDIO){
|
auto in = ctx_->streams[i];
|
logIt("in stream audio %d time_base: %d : %d", in->codecpar->codec_id, in->time_base.num, in->time_base.den);
|
if (in->codecpar->codec_id == AV_CODEC_ID_AAC)
|
as_idx_ = i;
|
else
|
logIt("record not support audio codec: %d", in->codecpar->codec_id);
|
}
|
}
|
|
return true;
|
}
|
|
const bool FormatIn::IsHEVC()const{
|
return ctx_->streams[vs_idx_]->codecpar->codec_id == AV_CODEC_ID_HEVC;
|
}
|
|
bool FormatIn::openCodec(AVDictionary **options){
|
if (vs_idx_ == -1) return false;
|
|
AVStream *s = ctx_->streams[vs_idx_];
|
|
AVCodecParameters *codecpar = s->codecpar;
|
AVCodec *dec = NULL;
|
|
bool flag = false;
|
AVDictionary *avdic = NULL;
|
|
int idle_gpu = -1;
|
|
for (int i = 0; i < 2; ++i)
|
{
|
if(hw_accl_){
|
// idle_gpu = gpu::getGPUPrior(300, 1024, 0);
|
idle_gpu = gpu::getGPU(300);
|
if(idle_gpu < 0){
|
logIt("NO GPU RESOURCE TO DECODE");
|
hw_accl_ = false;
|
continue;
|
}
|
|
std::string codec_name(avcodec_get_name(codecpar->codec_id));
|
codec_name += "_cuvid";
|
dec = avcodec_find_decoder_by_name(codec_name.c_str());
|
|
if(!dec){
|
hw_accl_ = false;
|
continue;
|
}else{
|
av_dict_set(&avdic, "gpu", std::to_string(idle_gpu).c_str(), 0);
|
}
|
}else{
|
dec = avcodec_find_decoder(codecpar->codec_id);
|
}
|
if(dec){
|
if(avdic){
|
options = &avdic;
|
logIt("DECODE USE GPU %d", idle_gpu);
|
}
|
flag = allocCodec(dec, s, options);
|
if(avdic){
|
av_dict_free(&avdic);
|
}
|
if(flag){
|
logIt("use decoder %s\n", dec->name);
|
break;
|
}else{
|
av_free(dec_ctx_);
|
dec_ctx_ = NULL;
|
hw_accl_ = false;
|
}
|
}
|
}
|
|
return flag;
|
}
|
|
bool FormatIn::allocCodec(AVCodec *dec, AVStream *s, AVDictionary **options){
|
|
AVCodecParameters *codecpar = s->codecpar;
|
|
dec_ctx_ = avcodec_alloc_context3(dec);
|
if (!dec_ctx_){
|
logIt("avcodec_alloc_context3 error");
|
return false;
|
}
|
int ret = avcodec_parameters_to_context(dec_ctx_, codecpar);
|
if(ret < 0){
|
logIt("avcodec_parameters_to_context error : %s", getAVErrorDesc(ret).c_str());
|
return false;
|
}
|
av_codec_set_pkt_timebase(dec_ctx_, s->time_base);
|
|
dec_ctx_->framerate = av_guess_frame_rate(ctx_, s, NULL);
|
|
dec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
ret = avcodec_open2(dec_ctx_,dec, options);
|
if(ret < 0){
|
logIt("open input %s codec failed:%s",
|
ctx_->filename,getAVErrorDesc(ret).c_str());
|
return false;
|
}
|
return true;
|
}
|
|
AVStream *FormatIn::getStream(int type/*=-1*/){
|
if (type == -1){
|
return ctx_->streams[vs_idx_];
|
}
|
|
if (vs_idx_ > -1 && type == ctx_->streams[vs_idx_]->codecpar->codec_type)
|
return ctx_->streams[vs_idx_];
|
if (as_idx_ > -1 && type == ctx_->streams[as_idx_]->codecpar->codec_type)
|
return ctx_->streams[as_idx_];
|
|
return NULL;
|
}
|
|
AVCodecContext *FormatIn::getCodecContext(int type){
|
return dec_ctx_;
|
}
|
|
bool FormatIn::isVideoPkt(AVPacket *pkt){
|
if (pkt->stream_index == vs_idx_){
|
return true;
|
}
|
return false;
|
}
|
|
bool FormatIn::isAudioPkt(AVPacket *pkt){
|
if (pkt->stream_index == as_idx_){
|
return true;
|
}
|
return false;
|
}
|
|
bool FormatIn::notVideoAudio(AVPacket *pkt){
|
return !isVideoPkt(pkt) && !isAudioPkt(pkt);
|
}
|
|
int FormatIn::readPacket(AVPacket *pkt_out){
|
|
auto flag = av_read_frame(ctx_, pkt_out);
|
|
return flag;
|
}
|
|
int FormatIn::decode(AVFrame* frame, AVPacket *pkt){
|
|
AVStream *in = getStream();
|
|
av_packet_rescale_ts(pkt, in->time_base, in->codec->time_base);
|
int ret = avcodec_send_packet(dec_ctx_, pkt);
|
if(ret < 0){
|
logIt("avcodec_send_packet error : %s", getAVErrorDesc(ret).c_str());
|
return -1;
|
}
|
while (ret >= 0) {
|
ret = avcodec_receive_frame(dec_ctx_, frame);
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
break;
|
} else if (ret < 0) {
|
logIt("decode frame failed : %s", getAVErrorDesc(ret).c_str());
|
return -1;
|
}else{
|
return 0;
|
}
|
}
|
return -2;
|
}
|
}
|