#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 "../data/CodedData.hpp"
|
#include "../data/FrameData.hpp"
|
|
#include "../../common/gpu/info.h"
|
#include "PsToEs.hpp"
|
|
using namespace logif;
|
|
namespace ffwrapper{
|
FormatIn::FormatIn(bool hw)
|
:ctx_(NULL)
|
,dec_ctx_(NULL)
|
,vs_idx_(-1)
|
,hw_accl_(hw)
|
,io_ctx_(NULL)
|
,read_io_buff_(NULL)
|
,read_io_buff_size_(32768)
|
{}
|
|
FormatIn::~FormatIn()
|
{
|
if(io_ctx_){
|
if(read_io_buff_){
|
// av_free(read_io_buff_);
|
read_io_buff_ = NULL;
|
}
|
avio_context_free(&io_ctx_);
|
io_ctx_ = NULL;
|
}
|
if(ctx_){
|
avformat_close_input(&ctx_);
|
avformat_free_context(ctx_);
|
ctx_ = NULL;
|
if(dec_ctx_){
|
avcodec_close(dec_ctx_);
|
dec_ctx_ = NULL;
|
}
|
}
|
}
|
|
////////////////////////////////////////////////////////////////////////
|
int FormatIn::openWithCustomIO(void *opaque, read_packet fn, AVDictionary **options/*=NULL*/){
|
ctx_ = avformat_alloc_context();
|
if(!ctx_){
|
logIt("open with custom io create format error");
|
return -1;
|
}
|
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");
|
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");
|
return -1;
|
}
|
ctx_->pb = io_ctx_;
|
|
auto err = av_probe_input_buffer(ctx_->pb, &ctx_->iformat, NULL, NULL, 0, read_io_buff_size_);
|
if(err != 0){
|
logIt("open with custom io prob input buffer error:%d", err);
|
logIt("failed:%s", getAVErrorDesc(err).c_str());
|
return -1;
|
}
|
|
return 0;
|
}
|
|
int FormatIn::openGb28181(const char *filename, AVDictionary **options){
|
|
std::string fn = filename;
|
addCamera(fn);
|
|
int ret = openWithCustomIO(NULL, readData, options);
|
if(ret < 0){
|
logIt("do openWithCustomIO failed:%d",ret);
|
}
|
|
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;
|
}
|
|
for (int i = 0; i < ctx_->nb_streams; ++i)
|
{
|
switch(ctx_->streams[i]->codecpar->codec_type){
|
case AVMEDIA_TYPE_VIDEO:
|
vs_idx_ = i;
|
break;
|
|
default:
|
break;
|
}
|
}
|
return true;
|
}
|
|
bool FormatIn::openCodec(const int type, AVDictionary **options){
|
int stream_index = -1;
|
switch(type){
|
case AVMEDIA_TYPE_VIDEO:
|
stream_index = vs_idx_;
|
break;
|
default:
|
break;
|
}
|
if(stream_index < 0){
|
logIt("open input %s codec need correct stream",ctx_->filename);
|
|
return false;
|
}
|
|
AVStream *s = ctx_->streams[stream_index];
|
|
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::getGPU(100);
|
if(idle_gpu < 0){
|
logIt("NO GPU RESOURCE TO DECODE");
|
hw_accl_ = false;
|
continue;
|
}
|
if(codecpar->codec_id == AV_CODEC_ID_H264){
|
dec = avcodec_find_decoder_by_name("h264_cuvid");
|
}else if(codecpar->codec_id == AV_CODEC_ID_H265){
|
dec = avcodec_find_decoder_by_name("hevc_cuvid");
|
}
|
if(!dec){
|
hw_accl_ = false;
|
continue;
|
}else{
|
av_dict_set(&avdic, "gpu", std::to_string(idle_gpu).c_str(), 0);
|
// av_dict_set(&avdic, "gpu", std::to_string(2).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){
|
return ctx_->streams[vs_idx_];
|
}
|
|
AVCodecContext *FormatIn::getCodecContext(int type){
|
return dec_ctx_;
|
}
|
|
bool FormatIn::readPacket(AVPacket &pkt_out, int stream_index){
|
|
bool founded = false;
|
while (!founded){
|
const int ret = av_read_frame(ctx_, &pkt_out);
|
if(ret < 0){
|
logIt("read frame from %s failed:%s",
|
ctx_->filename,getAVErrorDesc(ret).c_str());
|
|
return false;
|
}
|
if(pkt_out.stream_index == stream_index){
|
founded = true;
|
}else{
|
av_free_packet(&pkt_out);
|
av_init_packet(&pkt_out);
|
pkt_out.data = NULL;
|
pkt_out.size = 0;
|
}
|
}
|
pkt_out.stream_index = 0;
|
return true;
|
|
}
|
|
bool FormatIn::readPacket(std::shared_ptr<CodedData> &data, int stream_index){
|
|
AVPacket &pkt(data->getAVPacket());
|
return readPacket(pkt, getStream()->index);
|
}
|
|
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 1;
|
}
|
}
|
return 0;
|
}
|
|
int FormatIn::decode(std::shared_ptr<FrameData> &frame_data,
|
std::shared_ptr<CodedData> &data){
|
|
AVFrame *frame = frame_data->getAVFrame();
|
AVPacket &pkt(data->getAVPacket());
|
|
return decode(frame, pkt);
|
}
|
|
int FormatIn::readFrame(AVFrame* &frame){
|
|
auto data(std::make_shared<CodedData>());
|
if(!readPacket(data)){
|
return -1;
|
}
|
|
AVPacket &pkt(data->getAVPacket());
|
|
return decode(frame, pkt);
|
}
|
|
int FormatIn::readFrame(std::shared_ptr<FrameData> &frame_data){
|
|
AVFrame *frame(frame_data->getAVFrame());
|
|
return readFrame(frame);
|
}
|
|
|
}
|