mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
Implemented FFMPEG based encoder.
This commit is contained in:
@@ -23,11 +23,10 @@
|
||||
#include <freerdp/codec/h264.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/opt.h>
|
||||
|
||||
#define TAG FREERDP_TAG("codec")
|
||||
|
||||
#warning "FFMPEG does not support H264 encoding (GFX H264 server mode). Please review your configuration!"
|
||||
|
||||
/* Fallback support for older libavcodec versions */
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
|
||||
#define AV_CODEC_ID_H264 CODEC_ID_H264
|
||||
@@ -35,28 +34,126 @@
|
||||
|
||||
struct _H264_CONTEXT_LIBAVCODEC
|
||||
{
|
||||
AVCodec* codec;
|
||||
AVCodecContext* codecContext;
|
||||
AVCodec* codecDecoder;
|
||||
AVCodecContext* codecDecoderContext;
|
||||
AVCodec* codecEncoder;
|
||||
AVCodecContext* codecEncoderContext;
|
||||
AVCodecParserContext* codecParser;
|
||||
AVFrame* videoFrame;
|
||||
AVPacket packet;
|
||||
};
|
||||
typedef struct _H264_CONTEXT_LIBAVCODEC H264_CONTEXT_LIBAVCODEC;
|
||||
|
||||
static void libavcodec_destroy_encoder(H264_CONTEXT* h264)
|
||||
{
|
||||
H264_CONTEXT_LIBAVCODEC* sys;
|
||||
|
||||
if (!h264 || !h264->subsystem)
|
||||
return;
|
||||
|
||||
sys = (H264_CONTEXT_LIBAVCODEC*)h264->pSystemData;
|
||||
|
||||
if (sys->codecEncoderContext)
|
||||
{
|
||||
avcodec_close(sys->codecEncoderContext);
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 69, 100)
|
||||
avcodec_free_context(&sys->codecEncoderContext);
|
||||
#else
|
||||
av_free(sys->codecEncoderContext);
|
||||
#endif
|
||||
}
|
||||
|
||||
sys->codecEncoder = NULL;
|
||||
sys->codecEncoderContext = NULL;
|
||||
}
|
||||
|
||||
static BOOL libavcodec_create_encoder(H264_CONTEXT* h264)
|
||||
{
|
||||
BOOL recreate = FALSE;
|
||||
H264_CONTEXT_LIBAVCODEC* sys;
|
||||
|
||||
if (!h264 || !h264->subsystem)
|
||||
return FALSE;
|
||||
|
||||
sys = (H264_CONTEXT_LIBAVCODEC*)h264->pSystemData;
|
||||
recreate = !sys->codecEncoder || !sys->codecEncoderContext;
|
||||
|
||||
if (sys->codecEncoderContext)
|
||||
{
|
||||
if ((sys->codecEncoderContext->width != h264->width) ||
|
||||
(sys->codecEncoderContext->height != h264->height))
|
||||
recreate = TRUE;
|
||||
}
|
||||
|
||||
if (!recreate)
|
||||
return TRUE;
|
||||
|
||||
libavcodec_destroy_encoder(h264);
|
||||
sys->codecEncoder = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||
|
||||
if (!sys->codecEncoder)
|
||||
goto EXCEPTION;
|
||||
|
||||
sys->codecEncoderContext = avcodec_alloc_context3(sys->codecEncoder);
|
||||
|
||||
if (!sys->codecEncoderContext)
|
||||
goto EXCEPTION;
|
||||
|
||||
switch (h264->RateControlMode)
|
||||
{
|
||||
case H264_RATECONTROL_VBR:
|
||||
sys->codecEncoderContext->bit_rate = h264->BitRate;
|
||||
break;
|
||||
|
||||
case H264_RATECONTROL_CQP:
|
||||
/* TODO: sys->codecEncoderContext-> = h264->QP; */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sys->codecEncoderContext->width = h264->width;
|
||||
sys->codecEncoderContext->height = h264->height;
|
||||
sys->codecEncoderContext->delay = 0;
|
||||
sys->codecEncoderContext->framerate = (AVRational)
|
||||
{
|
||||
h264->FrameRate, 1
|
||||
};
|
||||
sys->codecEncoderContext->time_base = (AVRational)
|
||||
{
|
||||
1, h264->FrameRate
|
||||
};
|
||||
sys->codecEncoderContext->gop_size = 0;
|
||||
sys->codecEncoderContext->max_b_frames = 0;
|
||||
sys->codecEncoderContext->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
av_opt_set(sys->codecEncoderContext->priv_data, "preset", "fast", 0);
|
||||
av_opt_set(sys->codecEncoderContext->priv_data, "vprofile", "baseline", 0);
|
||||
av_opt_set(sys->codecEncoderContext->priv_data, "tune", "zerolatency", 0);
|
||||
|
||||
if (avcodec_open2(sys->codecEncoderContext, sys->codecEncoder, NULL) < 0)
|
||||
goto EXCEPTION;
|
||||
|
||||
return TRUE;
|
||||
EXCEPTION:
|
||||
libavcodec_destroy_encoder(h264);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int libavcodec_decompress(H264_CONTEXT* h264, const BYTE* pSrcData,
|
||||
UINT32 SrcSize)
|
||||
{
|
||||
int status;
|
||||
int gotFrame = 0;
|
||||
AVPacket packet;
|
||||
H264_CONTEXT_LIBAVCODEC* sys = (H264_CONTEXT_LIBAVCODEC*) h264->pSystemData;
|
||||
BYTE** pYUVData = h264->pYUVData;
|
||||
UINT32* iStride = h264->iStride;
|
||||
av_init_packet(&packet);
|
||||
packet.data = (BYTE*)pSrcData;
|
||||
packet.size = SrcSize;
|
||||
av_init_packet(&sys->packet);
|
||||
sys->packet.data = (BYTE*)pSrcData;
|
||||
sys->packet.size = SrcSize;
|
||||
/* avcodec_decode_video2 is deprecated with libavcodec 57.48.101 */
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
|
||||
status = avcodec_send_packet(sys->codecContext, &packet);
|
||||
status = avcodec_send_packet(sys->codecDecoderContext, &sys->packet);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
@@ -66,14 +163,14 @@ static int libavcodec_decompress(H264_CONTEXT* h264, const BYTE* pSrcData,
|
||||
|
||||
do
|
||||
{
|
||||
status = avcodec_receive_frame(sys->codecContext, sys->videoFrame);
|
||||
status = avcodec_receive_frame(sys->codecDecoderContext, sys->videoFrame);
|
||||
}
|
||||
while (status == AVERROR(EAGAIN));
|
||||
|
||||
gotFrame = (status == 0);
|
||||
#else
|
||||
status = avcodec_decode_video2(sys->codecContext, sys->videoFrame, &gotFrame,
|
||||
&packet);
|
||||
status = avcodec_decode_video2(sys->codecDecoderContext, sys->videoFrame, &gotFrame,
|
||||
&sys->packet);
|
||||
#endif
|
||||
|
||||
if (status < 0)
|
||||
@@ -110,7 +207,77 @@ static int libavcodec_decompress(H264_CONTEXT* h264, const BYTE* pSrcData,
|
||||
|
||||
static int libavcodec_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
|
||||
{
|
||||
return -1;
|
||||
int status;
|
||||
int gotFrame = 0;
|
||||
H264_CONTEXT_LIBAVCODEC* sys = (H264_CONTEXT_LIBAVCODEC*) h264->pSystemData;
|
||||
|
||||
if (!libavcodec_create_encoder(h264))
|
||||
return -1;
|
||||
|
||||
av_free(sys->packet.data);
|
||||
av_init_packet(&sys->packet);
|
||||
sys->packet.data = NULL;
|
||||
sys->packet.size = 0;
|
||||
sys->videoFrame->format = sys->codecEncoderContext->pix_fmt;
|
||||
sys->videoFrame->width = sys->codecEncoderContext->width;
|
||||
sys->videoFrame->height = sys->codecEncoderContext->height;
|
||||
sys->videoFrame->colorspace = AVCOL_SPC_BT709;
|
||||
sys->videoFrame->data[0] = h264->pYUVData[0];
|
||||
sys->videoFrame->data[1] = h264->pYUVData[1];
|
||||
sys->videoFrame->data[2] = h264->pYUVData[2];
|
||||
sys->videoFrame->linesize[0] = h264->iStride[0];
|
||||
sys->videoFrame->linesize[1] = h264->iStride[1];
|
||||
sys->videoFrame->linesize[2] = h264->iStride[2];
|
||||
sys->videoFrame->pts++;
|
||||
sys->videoFrame->pict_type = AV_PICTURE_TYPE_I;
|
||||
/* avcodec_encode_video2 is deprecated with libavcodec 57.48.101 */
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
|
||||
status = avcodec_send_frame(sys->codecEncoderContext, sys->videoFrame);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to encode video frame (%s [%d])",
|
||||
av_err2str(status), status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = avcodec_receive_packet(sys->codecEncoderContext,
|
||||
&sys->packet);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to encode video frame (%s [%d])",
|
||||
av_err2str(status), status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gotFrame = (status == 0);
|
||||
#else
|
||||
|
||||
do
|
||||
{
|
||||
status = avcodec_encode_video2(sys->codecDecoderContext,
|
||||
&sys->packet,
|
||||
sys->videoFrame, &gotFrame);
|
||||
}
|
||||
while ((status >= 0) && (gotFrame != 0));
|
||||
|
||||
#endif
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to encode video frame (%s [%d])",
|
||||
av_err2str(status), status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ppDstData = sys->packet.data;
|
||||
*pDstSize = sys->packet.size;
|
||||
|
||||
if (!gotFrame)
|
||||
return -2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void libavcodec_uninit(H264_CONTEXT* h264)
|
||||
@@ -130,20 +297,20 @@ static void libavcodec_uninit(H264_CONTEXT* h264)
|
||||
}
|
||||
|
||||
if (sys->codecParser)
|
||||
{
|
||||
av_parser_close(sys->codecParser);
|
||||
}
|
||||
|
||||
if (sys->codecContext)
|
||||
if (sys->codecDecoderContext)
|
||||
{
|
||||
avcodec_close(sys->codecContext);
|
||||
avcodec_close(sys->codecDecoderContext);
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 69, 100)
|
||||
avcodec_free_context(&sys->codecContext);
|
||||
avcodec_free_context(&sys->codecDecoderContext);
|
||||
#else
|
||||
av_free(sys->codecContext);
|
||||
av_free(sys->codecDecoderContext);
|
||||
#endif
|
||||
}
|
||||
|
||||
libavcodec_destroy_encoder(h264);
|
||||
|
||||
free(sys);
|
||||
h264->pSystemData = NULL;
|
||||
}
|
||||
@@ -160,39 +327,43 @@ static BOOL libavcodec_init(H264_CONTEXT* h264)
|
||||
|
||||
h264->pSystemData = (void*) sys;
|
||||
avcodec_register_all();
|
||||
sys->codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
|
||||
if (!sys->codec)
|
||||
if (!h264->Compressor)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to find libav H.264 codec");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
sys->codecDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
|
||||
sys->codecContext = avcodec_alloc_context3(sys->codec);
|
||||
if (!sys->codecDecoder)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to find libav H.264 codec");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
|
||||
if (!sys->codecContext)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate libav codec context");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
sys->codecDecoderContext = avcodec_alloc_context3(sys->codecDecoder);
|
||||
|
||||
if (sys->codec->capabilities & CODEC_CAP_TRUNCATED)
|
||||
{
|
||||
sys->codecContext->flags |= CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
if (!sys->codecDecoderContext)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate libav codec context");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
|
||||
if (avcodec_open2(sys->codecContext, sys->codec, NULL) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to open libav codec");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
if (sys->codecDecoder->capabilities & CODEC_CAP_TRUNCATED)
|
||||
{
|
||||
sys->codecDecoderContext->flags |= CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
|
||||
sys->codecParser = av_parser_init(AV_CODEC_ID_H264);
|
||||
if (avcodec_open2(sys->codecDecoderContext, sys->codecDecoder, NULL) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to open libav codec");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
|
||||
if (!sys->codecParser)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to initialize libav parser");
|
||||
goto EXCEPTION;
|
||||
sys->codecParser = av_parser_init(AV_CODEC_ID_H264);
|
||||
|
||||
if (!sys->codecParser)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to initialize libav parser");
|
||||
goto EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 18, 102)
|
||||
@@ -207,6 +378,8 @@ static BOOL libavcodec_init(H264_CONTEXT* h264)
|
||||
goto EXCEPTION;
|
||||
}
|
||||
|
||||
sys->videoFrame->pts = 0;
|
||||
|
||||
return TRUE;
|
||||
EXCEPTION:
|
||||
libavcodec_uninit(h264);
|
||||
|
||||
Reference in New Issue
Block a user