/** * FreeRDP: A Remote Desktop Protocol Implementation * H.264 Bitmap Compression * * Copyright 2015 Marc-André Moreau * Copyright 2014 Mike McDonald * Copyright 2014 erbth * * 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 #include #include #include #include #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 #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 };