extern "C" {
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/pixdesc.h"
#include "libavutil/mem.h"
}
using namespace std;
typedef enum {
SLPiecesErrorNone,
SLPiecesErrorOpenFile,
SLPiecesErrorStreamInfoNotFound,
SLPiecesErrorCodecNotFound,
SLPiecesErrorOpenCodec,
SLPiecesErrorAllocateFrame,
SLPiecesErroSetupScaler,
SLPiecesErroReSampler,
SLPiecesErroUnsupported,
} SLPiecesError;
class SLRtspDecoder{
public:
SLRtspDecoder();
~SLRtspDecoder();
public:
void regsterWindow(CAEAGLLayer *layer);
int starPlayVideo(std::string &path);
void stopPlayVideo();
private:
SLPiecesError avformatOpenInput(std::string &path);
SLPiecesError openVideoStream();
void closeVideoStream();
SLPiecesError openAVCodec(int videoStream);
int readDataFromNetwork(AVPacket &avPacket);
vector<int> collectStreams(AVFormatContext *formatCtx, enum AVMediaType codecType);
void avStreamFPSTimeBase(AVStream *st, float defaultTimeBase, float *pFPS, float *pTimeBase);
private:
bool isHaveLayer;
float _fps;
float _videoTimeBase;
GLRender mGLRender;
CAEAGLLayer *mViewWindow;
private:
int _videoStream;
int _artworkStream;
bool _isFirstIFrame;
vector<int> _videoStreams;
AVFormatContext *_formatCtx;
AVFrame *_videoFrame;
AVCodecContext *_videoCodecCtx;
};
#include "SLRtspDecoder.hpp"
SLRtspDecoder::SLRtspDecoder()
{
}
SLRtspDecoder::~SLRtspDecoder()
{
}
void SLRtspDecoder::regsterWindow(CAEAGLLayer *layer)
{
mViewWindow = layer;
}
int SLRtspDecoder::starPlayVideo(std::string &path)
{
//Step 1: Register all file formats and codec library.
av_register_all();
//Step 2: Open the network video stream.
avformat_network_init();
_isFirstIFrame = false;
//Step 3: AVFormat open input and get parameters(_formatCtx).
SLPiecesError errCode = this->avformatOpenInput(path);
if (errCode != SLPiecesErrorNone) {
return -1;
}
//Step4:Set avcodec with video stream.
SLPiecesError videoErr = this->openVideoStream();
if (videoErr != SLPiecesErrorNone) {
return -1;
}
AVPacket avPacket;
while (true){
this->readDataFromNetwork(avPacket);
}
return 0;
}
int SLRtspDecoder::readDataFromNetwork(AVPacket &avPacket)
{
int nRet = av_read_frame(_formatCtx, &avPacket);
if(nRet < 0){
clock_t now = clock();
while(clock() - now < 100);
return -1;
}
if (avPacket.stream_index ==_videoStream) {
//Return the number of bytes used: got_picture_ptr Zero if no frame could be decompressed,.
int gotFrame = 0;
int lenUseable = avcodec_decode_video2(_videoCodecCtx, _videoFrame, &gotFrame, &avPacket);
if (lenUseable <= 0 || gotFrame == 0) {
printf(0, "--->decode video error, skip packet.");
return -1;
}
//Find the first key frame.
if(!_isFirstIFrame && _videoFrame->key_frame){
_isFirstIFrame = true;
return -1;
}
if (_videoCodecCtx->width > 0 && _videoCodecCtx->height > 0 && !isHaveLayer) {
isHaveLayer = true;
mGLRender.setGLSurface(_videoCodecCtx->width, _videoCodecCtx->height, mViewWindow);
}
if(_videoCodecCtx->width > 0 && _videoCodecCtx->height > 0){
mGLRender.nativeGLRender(_videoFrame->data[0],_videoFrame->data[1],_videoFrame->data[2]);
}
}
av_free_packet(&avPacket);
return 0;
}
SLPiecesError SLRtspDecoder::avformatOpenInput(std::string &path)
{
//Allocate an AVFormatContext.
AVFormatContext *formatCtx = avformat_alloc_context();
if (!formatCtx){
return SLPiecesErrorOpenFile;
}
//Open an input stream and read the header. The codecs are not opened.
AVDictionary * opts = NULL;
int net = avformat_open_input(&formatCtx, path.c_str(), NULL, &opts);
if (net < 0) {
if (formatCtx){
avformat_close_input(&formatCtx);
avformat_free_context(formatCtx);
}
return SLPiecesErrorOpenFile;
}
//Read packets of a media file to get stream information.
if (avformat_find_stream_info(formatCtx, NULL) < 0) {
avformat_close_input(&formatCtx);
return SLPiecesErrorStreamInfoNotFound;
}
//Print detailed information about the input or output format.
av_dump_format(formatCtx, 0, "1", false);
_formatCtx = formatCtx;
return SLPiecesErrorNone;
}
SLPiecesError SLRtspDecoder::openVideoStream()
{
_videoStream = -1;
_artworkStream = -1;
SLPiecesError errCode = SLPiecesErrorCodecNotFound;
_videoStreams = collectStreams(_formatCtx, AVMEDIA_TYPE_VIDEO);
for (auto iter = _videoStreams.begin(); iter != _videoStreams.end(); iter ++) {
const int iStream = *iter;
if (0 == (_formatCtx->streams[iStream]->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
errCode = this->openAVCodec(iStream);
if (errCode == SLPiecesErrorNone){
break;
}
} else {
_artworkStream = iStream;
}
}
return errCode;
}
vector<int> SLRtspDecoder::collectStreams(AVFormatContext *formatCtx, enum AVMediaType codecType)
{
vector<int> streamVector;
for (int i = 0; i < formatCtx->nb_streams; ++i){
if (codecType == formatCtx->streams[i]->codec->codec_type){
streamVector.push_back(i);
}
}
return streamVector;
}
SLPiecesError SLRtspDecoder::openAVCodec(int videoStream)
{
// Find a registered decoder with a matching codec ID.
AVCodecContext *codecCtx = _formatCtx->streams[videoStream]->codec;
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
if (!codec){
return SLPiecesErrorCodecNotFound;
}
// Initialize the AVCodecContext to use the given AVCodec.
if (avcodec_open2(codecCtx, codec, NULL) < 0){
return SLPiecesErrorOpenCodec;
}
// Allocate an AVFrame and set its fields to default values.
_videoFrame = av_frame_alloc();
if (!_videoFrame) {
avcodec_close(codecCtx);
return SLPiecesErrorAllocateFrame;
}
// Calc fps
_videoStream = videoStream;
_videoCodecCtx = codecCtx;
AVStream *streamData = _formatCtx->streams[_videoStream];
avStreamFPSTimeBase(streamData, 0.04, &_fps, &_videoTimeBase);
return SLPiecesErrorNone;
}
void SLRtspDecoder::avStreamFPSTimeBase(AVStream *st, float defaultTimeBase, float *pFPS, float *pTimeBase)
{
float fps, timebase;
if (st->time_base.den && st->time_base.num){
timebase = av_q2d(st->time_base);
}else if(st->codec->time_base.den && st->codec->time_base.num){
timebase = av_q2d(st->codec->time_base);
}else{
timebase = defaultTimeBase;
}
if (st->codec->ticks_per_frame != 1) {
timebase *= st->codec->ticks_per_frame;
}
if (st->avg_frame_rate.den && st->avg_frame_rate.num){
fps = av_q2d(st->avg_frame_rate);
}else if (st->r_frame_rate.den && st->r_frame_rate.num){
fps = av_q2d(st->r_frame_rate);
}else{
fps = 1.0 / timebase;
}
if (pFPS){
*pFPS = fps;
}
if (pTimeBase){
*pTimeBase = timebase;
}
}
void SLRtspDecoder::stopPlayVideo(){
this->closeVideoStream();
if (_formatCtx) {
avformat_close_input(&_formatCtx);
_formatCtx = NULL;
}
}
void SLRtspDecoder::closeVideoStream()
{
_videoStream = -1;
if (_videoFrame) {
//av_free(_videoFrame);
_videoFrame = NULL;
}
if (_videoCodecCtx) {
avcodec_close(_videoCodecCtx);
_videoCodecCtx = NULL;
}
}