2017-06-14 17:04:54 +03:00
|
|
|
/**
|
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
|
|
* H.264 Bitmap Compression
|
|
|
|
*
|
|
|
|
* Copyright 2015 Marc-André Moreau <marcandre.moreau@gmail.com>
|
|
|
|
* Copyright 2014 Mike McDonald <Mike.McDonald@software.dell.com>
|
|
|
|
* Copyright 2014 erbth <t.erbesdobler@team103.com>
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
#include <winpr/wlog.h>
|
|
|
|
#include <freerdp/log.h>
|
|
|
|
#include <freerdp/codec/h264.h>
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
|
|
#include <libavutil/avutil.h>
|
|
|
|
|
|
|
|
#define TAG FREERDP_TAG("codec")
|
|
|
|
|
2017-07-17 10:00:57 +03:00
|
|
|
#warning "FFMPEG does not support H264 encoding (GFX H264 server mode). Please review your configuration!"
|
|
|
|
|
2017-06-14 17:04:54 +03:00
|
|
|
/* Fallback support for older libavcodec versions */
|
|
|
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
|
|
|
|
#define AV_CODEC_ID_H264 CODEC_ID_H264
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct _H264_CONTEXT_LIBAVCODEC
|
|
|
|
{
|
|
|
|
AVCodec* codec;
|
|
|
|
AVCodecContext* codecContext;
|
|
|
|
AVCodecParserContext* codecParser;
|
|
|
|
AVFrame* videoFrame;
|
|
|
|
};
|
|
|
|
typedef struct _H264_CONTEXT_LIBAVCODEC H264_CONTEXT_LIBAVCODEC;
|
|
|
|
|
|
|
|
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;
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to decode video frame (status=%d)", status);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
status = avcodec_receive_frame(sys->codecContext, sys->videoFrame);
|
|
|
|
}
|
|
|
|
while (status == AVERROR(EAGAIN));
|
|
|
|
|
|
|
|
gotFrame = (status == 0);
|
|
|
|
#else
|
|
|
|
status = avcodec_decode_video2(sys->codecContext, sys->videoFrame, &gotFrame,
|
|
|
|
&packet);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to decode video frame (status=%d)", status);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
WLog_INFO(TAG,
|
|
|
|
"libavcodec_decompress: frame decoded (status=%d, gotFrame=%d, width=%d, height=%d, Y=[%p,%d], U=[%p,%d], V=[%p,%d])",
|
|
|
|
status, gotFrame, sys->videoFrame->width, sys->videoFrame->height,
|
|
|
|
(void*) sys->videoFrame->data[0], sys->videoFrame->linesize[0],
|
|
|
|
(void*) sys->videoFrame->data[1], sys->videoFrame->linesize[1],
|
|
|
|
(void*) sys->videoFrame->data[2], sys->videoFrame->linesize[2]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (gotFrame)
|
|
|
|
{
|
|
|
|
pYUVData[0] = sys->videoFrame->data[0];
|
|
|
|
pYUVData[1] = sys->videoFrame->data[1];
|
|
|
|
pYUVData[2] = sys->videoFrame->data[2];
|
|
|
|
iStride[0] = sys->videoFrame->linesize[0];
|
|
|
|
iStride[1] = sys->videoFrame->linesize[1];
|
|
|
|
iStride[2] = sys->videoFrame->linesize[2];
|
|
|
|
h264->width = sys->videoFrame->width;
|
|
|
|
h264->height = sys->videoFrame->height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -2;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int libavcodec_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void libavcodec_uninit(H264_CONTEXT* h264)
|
|
|
|
{
|
|
|
|
H264_CONTEXT_LIBAVCODEC* sys = (H264_CONTEXT_LIBAVCODEC*) h264->pSystemData;
|
|
|
|
|
|
|
|
if (!sys)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sys->videoFrame)
|
|
|
|
{
|
|
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 18, 102)
|
|
|
|
av_frame_free(&sys->videoFrame);
|
|
|
|
#else
|
|
|
|
av_free(sys->videoFrame);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sys->codecParser)
|
|
|
|
{
|
|
|
|
av_parser_close(sys->codecParser);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sys->codecContext)
|
|
|
|
{
|
|
|
|
avcodec_close(sys->codecContext);
|
|
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 69, 100)
|
|
|
|
avcodec_free_context(&sys->codecContext);
|
|
|
|
#else
|
|
|
|
av_free(sys->codecContext);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
free(sys);
|
|
|
|
h264->pSystemData = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL libavcodec_init(H264_CONTEXT* h264)
|
|
|
|
{
|
|
|
|
H264_CONTEXT_LIBAVCODEC* sys;
|
|
|
|
sys = (H264_CONTEXT_LIBAVCODEC*) calloc(1, sizeof(H264_CONTEXT_LIBAVCODEC));
|
|
|
|
|
|
|
|
if (!sys)
|
|
|
|
{
|
|
|
|
goto EXCEPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
h264->pSystemData = (void*) sys;
|
|
|
|
avcodec_register_all();
|
|
|
|
sys->codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
|
|
|
|
|
|
|
if (!sys->codec)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to find libav H.264 codec");
|
|
|
|
goto EXCEPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
sys->codecContext = avcodec_alloc_context3(sys->codec);
|
|
|
|
|
|
|
|
if (!sys->codecContext)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to allocate libav codec context");
|
|
|
|
goto EXCEPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sys->codec->capabilities & CODEC_CAP_TRUNCATED)
|
|
|
|
{
|
|
|
|
sys->codecContext->flags |= CODEC_FLAG_TRUNCATED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avcodec_open2(sys->codecContext, sys->codec, NULL) < 0)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to open libav codec");
|
|
|
|
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)
|
|
|
|
sys->videoFrame = av_frame_alloc();
|
|
|
|
#else
|
|
|
|
sys->videoFrame = avcodec_alloc_frame();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!sys->videoFrame)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to allocate libav frame");
|
|
|
|
goto EXCEPTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
EXCEPTION:
|
|
|
|
libavcodec_uninit(h264);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
H264_CONTEXT_SUBSYSTEM g_Subsystem_libavcodec =
|
|
|
|
{
|
|
|
|
"libavcodec",
|
|
|
|
libavcodec_init,
|
|
|
|
libavcodec_uninit,
|
|
|
|
libavcodec_decompress,
|
|
|
|
libavcodec_compress
|
|
|
|
};
|