在我之前写的一篇文章《SkeyeRTSPLive传统视频监控互联网+实现利器解决方案》中提到RTSP转RTMP的转流过程,简化流程就是通过SkeyeRTSPClient拉RTSP流,获取音视频编码数据,然后再通过SkeyeRTMP推出去,流程非常简单;然后再实际开发过程中,我们发现其实这个过程并没有想象中那么简单;首先,RTSP协议支持多种音视频编码格式,如音频支持AAC,G711,G726,等,视频支持H264,H625,MJPEG, MPEG等等各种格式,而SkeyeRTMPPusher推流只支持H264(已扩展支持H265)格式,这时,音频我们可以通过SkeyeAACEncoder将音频转码成AAC格式,而视频我们可以通过SkeyeVideoDecoder解码成原始数据,然后再通过SkeyeVideoEncoder将原始数据转码成RTMP推送指定的格式,本文,我们将重点讲述SkeyeVideoDecoder的软解码流程。
1. SkeyeVideoDecoder软解码接口声明如下:
#ifndef __SKEYE_DECODER_API_H__
#define __FF_DECODER_API_H__
#include
#include
#include
#include
#define SKEYEDECODER_API __declspec(dllexport)
//=======================================================
//Decoder
#ifndef DECODER_H264
#define DECODER_H264 0x1C //28
#endif
#ifndef DECODER_MPEG4
#define DECODER_MPEG4 0x0D //13
#endif
#ifndef DECODER_MPEG2
#define DECODER_MPEG2 0x02 //2
#endif
#ifndef DECODER_MJPEG
#define DECODER_MJPEG 0x08 //8
#endif
#ifndef DECODER_MP3
#define DECODER_MP3 0x15001 //86017
#endif
#ifndef DECODER_AAC
#define DECODER_AAC 0x15002 //86018
#endif
//=======================================================
//输出格式
#ifndef OUTPUT_PIX_FMT_YUV420P
#define OUTPUT_PIX_FMT_YUV420P 0
#endif
#ifndef OUTPUT_PIX_FMT_YUYV422
#define OUTPUT_PIX_FMT_YUYV422 1
#endif
#ifndef OUTPUT_PIX_FMT_RGB565LE
#define OUTPUT_PIX_FMT_RGB565LE 44
#endif
#ifndef OUTPUT_PIX_FMT_RGBA
#define OUTPUT_PIX_FMT_RGBA 28
#endif
//=======================================================
//图像处理
//=======================================================
typedef enum __VIDEO_FILTER_TYPE
{
VIDEO_ROTATION_90_0 = 0, //顺时针旋转90度
VIDEO_ROTATION_90_1, //逆时针旋转90度
VIDEO_ROTATION_90_0_FLIP, //顺时针旋转90度,再水平翻转
VIDEO_ROTATION_90_1_FLIP, //逆时针旋转90度,再垂直翻转
VIDEO_TEXT,
}VIDEO_FILTER_TYPE;
//=======================================================
typedef void *SKEYEDEC_HANDLE;
//=======================================================
extern "C"
{
int SKEYEDECODER_API SKEYEDECODER_Init(SKEYEDEC_HANDLE *_handle);
int SKEYEDECODER_API SKEYEDECODER_Deinit(SKEYEDEC_HANDLE *_handle);
int SKEYEDECODER_API SKEYEDECODER_SetVideoDecoderParam(SKEYEDEC_HANDLE _handle, int _width, int _height, int _decoder, int _outformat);
int SKEYEDECODER_API SKEYEDECODER_SetAudioDecoderParam(SKEYEDEC_HANDLE _handle, unsigned char _channel, unsigned int _sample_rate, unsigned int _decoder);
int SKEYEDECODER_API SKEYEDECODER_GetVideoDecoderInfo(SKEYEDEC_HANDLE _handle, int *_decoder, int *_width, int *_height);
int SKEYEDECODER_API SKEYEDECODER_DecodeVideo(SKEYEDEC_HANDLE _handle, char *pInBuf, int inputSize, char **pOutBuf, int dstW, int dstH);
//desc: 解码后的数据,直接送到指定的内存中
int SKEYEDECODER_API SKEYEDECODER_DecodeVideo2Buf(SKEYEDEC_HANDLE _handle, char *_inbuf, int _bufsize, void *_outbuf[8], int _pitch);
int SKEYEDECODER_API SKEYEDECODER_DecodeVideo3(SKEYEDEC_HANDLE _handle, char *_inbuf, int _bufsize, void *yuvbuf, int dstW, int dstH);
int SKEYEDECODER_API SKEYEDECODER_DecodeVideoPacket(SKEYEDEC_HANDLE _handle, char *pCodecCtx, unsigned char *avPacket, char **_outbuf);
int SKEYEDECODER_API SKEYEDECODER_DecodeAudio(SKEYEDEC_HANDLE _handle, char *pInBuf, int inputSize, char *pOutBuf, int *outSize);
int SKEYEDECODER_API SKEYEDECODER_DecodeAudioPacket(SKEYEDEC_HANDLE _handle, char *pCodecCtx, unsigned char *avPacket, char *pOutBuf, int *outSize);
};
#endif
2. 软解码通过ffmpeg解码实现流程
和网上大多数的ffmpeg解码示例调用相似。软解码实现分四步走,详细流程如下:
-
第一步,全局注册ffmpeg编解码器
avcodec_register_all();/*注册所有的编码解码器*/ av_register_all();// //注册所有可解码类型
- 第二步,初始化视频解码参数
int InitVideoDecoder(int _width, int _height, int _videoCodec, int _outformat)
{
if (NULL != decoderObj.pVideoCodecCtx) return -1; //or call DeinitVideoDecoder();
AVCodec *pAVCodec;
pAVCodec = avcodec_find_decoder((AVCodecID)_videoCodec);
if (NULL == pAVCodec) return -1;
decoderObj.pVideoCodecCtx = avcodec_alloc_context3(pAVCodec);
decoderObj.pVideoCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
decoderObj.pVideoCodecCtx->width = _width;
decoderObj.pVideoCodecCtx->height = _height;
//decoderObj.pVideoCodecCtx->thread_count = 2;
//decoderObj.pVideoCodecCtx->active_thread_type = decoderObj.pVideoCodecCtx->thread_type = FF_THREAD_FRAME;
int ret = avcodec_open2(decoderObj.pVideoCodecCtx, pAVCodec, NULL);
if (ret
- 第三步,直接解码视频帧并输出指定色彩格式
int DecodeVideo(char *_inbuf, int _bufsize, char **_outbuf, int dstW, int dstH)
{
if (NULL == _inbuf) return -1;
if (1 > _bufsize) return -1;
//if (NULL == decoderObj.pSws_ctx) return -2;
if (NULL == decoderObj.mVideoFrame420) decoderObj.mVideoFrame420 = av_frame_alloc();
decoderObj.avVidPacket.size = _bufsize;
decoderObj.avVidPacket.data = (uint8_t*)_inbuf;
int frameFinished = 0;
int nDecode = avcodec_decode_video2(decoderObj.pVideoCodecCtx, decoderObj.mVideoFrame420, &frameFinished, &decoderObj.avVidPacket);//(uint8_t*)pInBuffer, inputSize);
if (nDecode data, decoderObj.mVideoFrame420->linesize, 0, decoderObj.pVideoCodecCtx->height,
decoderObj.pAvFrameYuv->data, decoderObj.pAvFrameYuv->linesize);
//sws_freeContext(decoderObj.pSws_ctx);
//decoderObj.pSws_ctx = NULL;
*_outbuf = (char*)decoderObj.pAvFrameYuv->data[0];
return 0;
}
- 第四步,停止解码后,销毁解码器申请的资源
void DeinitVideoDecoder()
{
if (NULL != decoderObj.mVideoFrame420)
{
av_frame_free(&decoderObj.mVideoFrame420);
decoderObj.mVideoFrame420 = NULL;
}
if (NULL != decoderObj.pBuffYuv420)
{
av_free(decoderObj.pBuffYuv420);
decoderObj.pBuffYuv420 = NULL;
}
if (NULL != decoderObj.pAvFrameSws)
{
av_frame_free(&decoderObj.pAvFrameSws);
decoderObj.pAvFrameSws = NULL;
}
if (NULL != decoderObj.pAvFrameYuv)
{
av_frame_free(&decoderObj.pAvFrameYuv);
decoderObj.pAvFrameYuv = NULL;
}
if (NULL != decoderObj.pBuffYuv)
{
av_free(decoderObj.pBuffYuv);
decoderObj.pBuffYuv = NULL;
}
if (NULL != decoderObj.pSws_ctx)
{
sws_freeContext(decoderObj.pSws_ctx);
decoderObj.pSws_ctx = NULL;
}
if (NULL != decoderObj.pVideoCodecCtx)
{
avcodec_close(decoderObj.pVideoCodecCtx);
av_free(decoderObj.pVideoCodecCtx);
decoderObj.pVideoCodecCtx = NULL;
}
}
有任何技术问题,欢迎大家和我技术交流:
295222688@qq.com
大家也可以加入SKEYEPlayer流媒体播放器 QQ群进行讨论:
102644504
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net