#include "FormatIn.hpp"
|
|
#include <stdexcept>
|
#include <unistd.h>
|
#include <stdlib.h>
|
#include <time.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)
|
,prop_(NULL)
|
,io_ctx_(NULL)
|
,read_io_buff_(NULL)
|
,read_io_buff_size_(32768)
|
,fps_(25.0)
|
{}
|
|
FormatIn::FormatIn(const VideoProp &prop, bool hw/*=true*/)
|
:FormatIn(hw)
|
{
|
prop_ = new VideoProp;
|
*prop_ = prop;
|
}
|
|
FormatIn::~FormatIn()
|
{
|
logIt("free format in");
|
if (prop_) delete prop_;
|
|
if(dec_ctx_){
|
avcodec_close(dec_ctx_);
|
avcodec_free_context(&dec_ctx_);
|
dec_ctx_ = NULL;
|
}
|
|
if(ctx_){
|
avformat_close_input(&ctx_);
|
ctx_ = NULL;
|
}
|
|
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_;
|
|
return 0;
|
}
|
|
int FormatIn::open(const char *filename, AVDictionary **options){
|
|
int ret = avformat_open_input(&ctx_, filename, NULL, options);
|
|
ret = avformat_find_stream_info(ctx_, options);
|
if(ret < 0){
|
logIt("find %s stream info failed:%s",
|
ctx_->filename,getAVErrorDesc(ret).c_str());
|
return ret;
|
}
|
|
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 0;
|
}
|
|
const bool FormatIn::IsHEVC()const{
|
return ctx_->streams[vs_idx_]->codecpar->codec_id == AV_CODEC_ID_HEVC;
|
}
|
|
const bool FormatIn::IsAVC1()const{
|
if (IsHEVC()) return false;
|
|
char p[100] = {0};
|
char *sub = av_fourcc_make_string(p, ctx_->streams[vs_idx_]->codecpar->codec_tag);
|
const int ret = strcmp(sub, "avc1");
|
if (ret == 0) return true;
|
return false;
|
}
|
|
bool FormatIn::openCodec(AVDictionary **options){
|
if (dec_ctx_) return true;
|
if (vs_idx_ == -1) return false;
|
|
AVStream *s = ctx_->streams[vs_idx_];
|
AVCodecParameters *codecpar = s->codecpar;
|
|
bool flag = false;
|
|
AVCodecID codec_id = codecpar->codec_id;
|
|
AVCodec *dec = avcodec_find_decoder(codec_id);
|
|
if(dec){
|
flag = allocCodec(dec, s, options);
|
if(*options){
|
av_dict_free(options);
|
}
|
if(!flag){
|
av_free(dec_ctx_);
|
dec_ctx_ = NULL;
|
}
|
|
logIt("use decoder %s\n", dec->name);
|
}
|
|
return flag;
|
}
|
|
bool FormatIn::allocCodec(AVCodec *dec, AVStream *s, AVDictionary **options){
|
|
AVCodecParameters *codecpar = NULL;
|
if(s) codecpar = s->codecpar;
|
|
dec_ctx_ = avcodec_alloc_context3(dec);
|
if (!dec_ctx_){
|
logIt("avcodec_alloc_context3 error");
|
return false;
|
}
|
int ret = 0;
|
if(s && codecpar) {
|
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 (vs_idx_ < 0 || !ctx_ || ctx_->nb_streams == 0 || !ctx_->streams) return NULL;
|
|
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);
|
if (flag < 0)
|
logIt("======>> av_read_frame error %s", getAVErrorDesc(flag).c_str());
|
return flag;
|
}
|
|
int FormatIn::decode(AVFrame* frame, AVPacket *pkt){
|
|
AVStream *in = getStream();
|
if (in){
|
av_packet_rescale_ts(pkt, in->time_base, in->codec->time_base);
|
}
|
int ret = avcodec_send_packet(dec_ctx_, pkt);
|
if(ret < 0){
|
logIt("pkt size %d avcodec_send_packet error : %s", pkt->size, 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;
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
constexpr int bs = 8192;
|
FormatInGB::FormatInGB()
|
:FormatIn()
|
,gb28181_(NULL)
|
,parser_ctx_(NULL)
|
,buffer_(NULL)
|
,buffer_size_(bs)
|
{
|
buffer_ = (unsigned char*)malloc(buffer_size_);
|
}
|
|
FormatInGB::FormatInGB(const VideoProp &prop)
|
:FormatInGB()
|
{
|
prop_ = new VideoProp;
|
*prop_ = prop;
|
}
|
|
FormatInGB::~FormatInGB(){
|
if (parser_ctx_){
|
av_parser_close(parser_ctx_);
|
}
|
if (gb28181_)delete gb28181_;
|
if (buffer_) free(buffer_);
|
|
for(auto &pkt : q_pkt_){
|
av_packet_free(&pkt);
|
}
|
q_pkt_.clear();
|
}
|
|
static enum AVCodecID codecMap(const int c){
|
switch (c) {
|
case E_VIDEO_STREAM_H264:// = 0,
|
return AV_CODEC_ID_H264;
|
case E_VIDEO_STREAM_MPEG2:// = 1, // MPEG4
|
// return AV_CODEC_ID_MPEG2VIDEO;
|
case E_VIDEO_STREAM_MPEG4:// = 2, // MPEG4
|
return AV_CODEC_ID_MPEG4;
|
case E_VIDEO_STREAM_SVAC:// = 3, // SVAC
|
return AV_CODEC_ID_NONE;
|
case E_VIDEO_STREAM_3GP:// = 4, // 3GP
|
return AV_CODEC_ID_NONE; // audio
|
case E_VIDEO_STREAM_H265:// = 5, //H265
|
return AV_CODEC_ID_HEVC;
|
default:
|
break;
|
}
|
return AV_CODEC_ID_NONE;
|
}
|
|
int FormatInGB::open(const char *filename, AVDictionary **options){
|
if (gb28181_){
|
delete gb28181_;
|
}
|
|
gb28181_ = new GB28181API;
|
std::string fn = filename;
|
|
if(gb28181_->addCamera(fn) < 0){
|
delete gb28181_;
|
gb28181_ = NULL;
|
logIt("do addCamera Error\n");
|
return -1;
|
}
|
for(int i = 0; i < 6; i++){
|
if (gb28181_->getDataType() >= 0){
|
AVCodecID id = codecMap(gb28181_->getDataType());
|
logIt("======>>codec name %s\n", avcodec_get_name(id));
|
parser_ctx_ = av_parser_init(id);
|
if (parser_ctx_) parser_ctx_->flags |= PARSER_FLAG_USE_CODEC_TS;
|
AVCodec* dec = avcodec_find_decoder(id);
|
allocCodec(dec, NULL, NULL);
|
break;
|
}
|
usleep(1000000);
|
}
|
return 0;
|
}
|
|
int FormatInGB::readPacket(AVPacket *pkt_out){
|
if (!q_pkt_.empty()){
|
auto pkt = q_pkt_.front();
|
q_pkt_.pop_front();
|
av_packet_ref(pkt_out, pkt);
|
av_packet_free(&pkt);
|
return 0;
|
}
|
|
if (gb28181_->getDataType() < 0){
|
logIt("======>> readPacket can't recv gb28181 stream");
|
return 1;
|
}
|
if (!parser_ctx_){
|
AVCodecID id = codecMap(gb28181_->getDataType());
|
parser_ctx_ = av_parser_init(id);
|
AVCodec* dec = avcodec_find_decoder(id);
|
allocCodec(dec, NULL, NULL);
|
}
|
if (parser_ctx_ && dec_ctx_){
|
|
int try_run = 0;
|
AVPacket* pkt = av_packet_alloc();
|
bool got_pkt = false;
|
|
while (true) {
|
int data_size = gb28181_->readData(gb28181_, buffer_, buffer_size_);
|
// printf("======>> data_size %d pos %d\n", data_size, buffer_pos_);
|
if (data_size == 0){
|
try_run ++;
|
if (try_run > 12){
|
av_packet_free(&pkt);
|
logIt("gb28181_ readData %d failed, return -1", try_run);
|
return -1;
|
}
|
continue;
|
}
|
try_run = 0;
|
unsigned char* data = buffer_;
|
while (data_size > 0) {
|
int ret = av_parser_parse2(parser_ctx_, dec_ctx_,
|
&pkt->data, &pkt->size, data, data_size,
|
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
|
|
// logIt("======>> av_parser_parse2 ret %d pkt size %d", ret, pkt->size);
|
if (ret < 0) {
|
av_packet_free(&pkt);
|
logIt("======>> av_parser_parse2 error %d %s", ret, getAVErrorDesc(ret).c_str());
|
return ret;
|
}
|
|
data += ret;
|
data_size -= ret;
|
|
if (pkt->size){
|
if(fps_ == 0 && dec_ctx_->framerate.den > 0 && dec_ctx_->framerate.num > 0){
|
fps_ = dec_ctx_->framerate.num/dec_ctx_->framerate.den;
|
if (fps_ == 0) fps_ = 24;
|
}
|
if (parser_ctx_->key_frame == 1){
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
}
|
got_pkt = true;
|
AVPacket* tmpkt = av_packet_alloc();
|
av_packet_ref(tmpkt, pkt);
|
q_pkt_.push_back(tmpkt);
|
}
|
}
|
if (got_pkt) {
|
av_packet_free(&pkt);
|
auto tmpkt = q_pkt_.front();
|
q_pkt_.pop_front();
|
av_packet_ref(pkt_out, tmpkt);
|
av_packet_free(&tmpkt);
|
return 0;
|
}
|
}
|
}
|
return -1;
|
}
|
|
const bool FormatInGB::IsHEVC()const{
|
if (!gb28181_) return false;
|
return codecMap(gb28181_->getDataType()) == AV_CODEC_ID_HEVC;
|
}
|
|
const bool FormatInGB::IsAVC1()const{
|
return false;
|
}
|
|
bool FormatInGB::isVideoPkt(AVPacket *pkt) {
|
return true;
|
}
|
bool FormatInGB::isAudioPkt(AVPacket *pkt) {
|
return false;
|
}
|
|
}
|