/** * FreeRDP: A Remote Desktop Protocol Implementation * Digital Sound Processing - FFMPEG backend * * Copyright 2018 Armin Novak * Copyright 2018 Thincast Technologies GmbH * * 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 #if defined(SWRESAMPLE_FOUND) #include #elif defined(AVRESAMPLE_FOUND) #include #else #error "libswresample or libavresample required" #endif #include "dsp.h" #include "dsp_ffmpeg.h" #define TAG FREERDP_TAG("dsp.ffmpeg") struct S_FREERDP_DSP_CONTEXT { FREERDP_DSP_COMMON_CONTEXT common; BOOL isOpen; UINT32 bufferedSamples; enum AVCodecID id; const AVCodec* codec; AVCodecContext* context; AVFrame* frame; AVFrame* resampled; AVFrame* buffered; AVPacket* packet; #if defined(SWRESAMPLE_FOUND) SwrContext* rcontext; #else AVAudioResampleContext* rcontext; #endif }; static BOOL ffmpeg_codec_is_filtered(enum AVCodecID id, WINPR_ATTR_UNUSED BOOL encoder) { switch (id) { #if !defined(WITH_DSP_EXPERIMENTAL) case AV_CODEC_ID_ADPCM_IMA_OKI: case AV_CODEC_ID_MP3: case AV_CODEC_ID_ADPCM_MS: case AV_CODEC_ID_G723_1: case AV_CODEC_ID_GSM_MS: case AV_CODEC_ID_PCM_ALAW: case AV_CODEC_ID_PCM_MULAW: return TRUE; #endif case AV_CODEC_ID_NONE: return TRUE; case AV_CODEC_ID_AAC: case AV_CODEC_ID_AAC_LATM: return FALSE; default: return FALSE; } } static enum AVCodecID ffmpeg_get_avcodec(const AUDIO_FORMAT* WINPR_RESTRICT format) { if (!format) return AV_CODEC_ID_NONE; switch (format->wFormatTag) { case WAVE_FORMAT_UNKNOWN: return AV_CODEC_ID_NONE; case WAVE_FORMAT_PCM: switch (format->wBitsPerSample) { case 16: return AV_CODEC_ID_PCM_U16LE; case 8: return AV_CODEC_ID_PCM_U8; default: return AV_CODEC_ID_NONE; } case WAVE_FORMAT_DVI_ADPCM: return AV_CODEC_ID_ADPCM_IMA_OKI; case WAVE_FORMAT_ADPCM: return AV_CODEC_ID_ADPCM_MS; case WAVE_FORMAT_ALAW: return AV_CODEC_ID_PCM_ALAW; case WAVE_FORMAT_MULAW: return AV_CODEC_ID_PCM_MULAW; case WAVE_FORMAT_GSM610: return AV_CODEC_ID_GSM_MS; case WAVE_FORMAT_MSG723: return AV_CODEC_ID_G723_1; case WAVE_FORMAT_AAC_MS: return AV_CODEC_ID_AAC; case WAVE_FORMAT_OPUS: return AV_CODEC_ID_OPUS; default: return AV_CODEC_ID_NONE; } } static enum AVSampleFormat ffmpeg_sample_format(const AUDIO_FORMAT* WINPR_RESTRICT format) { switch (format->wFormatTag) { case WAVE_FORMAT_PCM: switch (format->wBitsPerSample) { case 8: return AV_SAMPLE_FMT_U8; case 16: return AV_SAMPLE_FMT_S16; default: return AV_SAMPLE_FMT_NONE; } case WAVE_FORMAT_DVI_ADPCM: case WAVE_FORMAT_ADPCM: return AV_SAMPLE_FMT_S16P; case WAVE_FORMAT_MPEGLAYER3: case WAVE_FORMAT_AAC_MS: return AV_SAMPLE_FMT_FLTP; case WAVE_FORMAT_OPUS: return AV_SAMPLE_FMT_S16; case WAVE_FORMAT_MSG723: case WAVE_FORMAT_GSM610: return AV_SAMPLE_FMT_S16P; case WAVE_FORMAT_ALAW: return AV_SAMPLE_FMT_S16; default: return AV_SAMPLE_FMT_NONE; } } static void ffmpeg_close_context(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) { if (context) { if (context->context) avcodec_free_context(&context->context); if (context->frame) av_frame_free(&context->frame); if (context->resampled) av_frame_free(&context->resampled); if (context->buffered) av_frame_free(&context->buffered); if (context->packet) av_packet_free(&context->packet); if (context->rcontext) { #if defined(SWRESAMPLE_FOUND) swr_free(&context->rcontext); #else avresample_free(&context->rcontext); #endif } context->id = AV_CODEC_ID_NONE; context->codec = nullptr; context->isOpen = FALSE; context->context = nullptr; context->frame = nullptr; context->resampled = nullptr; context->packet = nullptr; context->rcontext = nullptr; } } static void ffmpeg_setup_resample_frame(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, const AUDIO_FORMAT* WINPR_RESTRICT format) { if (context->resampled->buf[0] != nullptr) av_frame_unref(context->resampled); if (context->common.encoder) { context->resampled->format = context->context->sample_fmt; context->resampled->sample_rate = context->context->sample_rate; } else { context->resampled->format = AV_SAMPLE_FMT_S16; WINPR_ASSERT(format->nSamplesPerSec <= INT_MAX); context->resampled->sample_rate = (int)format->nSamplesPerSec; } #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) av_channel_layout_default(&context->resampled->ch_layout, format->nChannels); #else const int64_t layout = av_get_default_channel_layout(format->nChannels); context->resampled->channel_layout = layout; context->resampled->channels = format->nChannels; #endif } static BOOL ffmpeg_open_context(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) { int ret = 0; if (!context || context->isOpen) return FALSE; const AUDIO_FORMAT* format = &context->common.format; if (!format) return FALSE; context->id = ffmpeg_get_avcodec(format); if (ffmpeg_codec_is_filtered(context->id, context->common.encoder)) goto fail; if (context->common.encoder) context->codec = avcodec_find_encoder(context->id); else context->codec = avcodec_find_decoder(context->id); if (!context->codec) goto fail; context->context = avcodec_alloc_context3(context->codec); if (!context->context) goto fail; switch (context->id) { /* We need support for multichannel and sample rates != 8000 */ case AV_CODEC_ID_GSM_MS: context->context->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL; break; case AV_CODEC_ID_AAC: #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) context->context->profile = FF_PROFILE_AAC_MAIN; #elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(62, 11, 100) context->context->profile = AV_PROFILE_AAC_MAIN; #else context->context->profile = AV_PROFILE_AAC_LOW; #endif break; default: break; } context->context->max_b_frames = 1; context->context->delay = 0; #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) av_channel_layout_default(&context->context->ch_layout, format->nChannels); #else context->context->channels = format->nChannels; const int64_t layout = av_get_default_channel_layout(format->nChannels); context->context->channel_layout = layout; #endif context->context->sample_rate = (int)format->nSamplesPerSec; context->context->block_align = format->nBlockAlign; context->context->bit_rate = format->nAvgBytesPerSec * 8LL; context->context->sample_fmt = ffmpeg_sample_format(format); context->context->time_base = av_make_q(1, context->context->sample_rate); if ((ret = avcodec_open2(context->context, context->codec, nullptr)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error avcodec_open2 %s [%d]", err, ret); goto fail; } context->packet = av_packet_alloc(); if (!context->packet) goto fail; context->frame = av_frame_alloc(); if (!context->frame) goto fail; context->resampled = av_frame_alloc(); if (!context->resampled) goto fail; context->buffered = av_frame_alloc(); if (!context->buffered) goto fail; #if defined(SWRESAMPLE_FOUND) context->rcontext = swr_alloc(); #else context->rcontext = avresample_alloc_context(); #endif if (!context->rcontext) goto fail; #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) av_channel_layout_default(&context->frame->ch_layout, format->nChannels); #else context->frame->channel_layout = layout; context->frame->channels = format->nChannels; #endif WINPR_ASSERT(format->nSamplesPerSec <= INT_MAX); context->frame->sample_rate = (int)format->nSamplesPerSec; context->frame->format = AV_SAMPLE_FMT_S16; ffmpeg_setup_resample_frame(context, format); if (context->context->frame_size > 0) { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) ret = av_channel_layout_copy(&context->buffered->ch_layout, &context->resampled->ch_layout); if (ret != 0) goto fail; #else context->buffered->channel_layout = context->resampled->channel_layout; context->buffered->channels = context->resampled->channels; #endif context->buffered->format = context->resampled->format; context->buffered->nb_samples = context->context->frame_size; ret = av_frame_get_buffer(context->buffered, 1); if (ret < 0) goto fail; } context->isOpen = TRUE; return TRUE; fail: ffmpeg_close_context(context); return FALSE; } #if defined(SWRESAMPLE_FOUND) static BOOL ffmpeg_resample_frame(SwrContext* WINPR_RESTRICT context, AVFrame* WINPR_RESTRICT in, AVFrame* WINPR_RESTRICT out) { int ret = 0; if (!swr_is_initialized(context)) { if ((ret = swr_config_frame(context, out, in)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } if ((ret = (swr_init(context))) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } } if ((ret = swr_convert_frame(context, out, in)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } return TRUE; } #else static BOOL ffmpeg_resample_frame(AVAudioResampleContext* WINPR_RESTRICT context, AVFrame* WINPR_RESTRICT in, AVFrame* WINPR_RESTRICT out) { int ret; if (!avresample_is_open(context)) { if ((ret = avresample_config(context, out, in)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } if ((ret = (avresample_open(context))) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } } if ((ret = avresample_convert_frame(context, out, in)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } return TRUE; } #endif static BOOL ffmpeg_encode_frame(AVCodecContext* WINPR_RESTRICT context, AVFrame* WINPR_RESTRICT in, AVPacket* WINPR_RESTRICT packet, wStream* WINPR_RESTRICT out) { if (in->format == AV_SAMPLE_FMT_FLTP) { uint8_t** pp = in->extended_data; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 28, 100) const int nr_channels = in->channels; #else const int nr_channels = in->ch_layout.nb_channels; #endif for (int y = 0; y < nr_channels; y++) { float* data = (float*)pp[y]; for (int x = 0; x < in->nb_samples; x++) { const float val1 = data[x]; if (isnan(val1)) data[x] = 0.0f; else if (isinf(val1)) { if (val1 < 0.0f) data[x] = -1.0f; else data[x] = 1.0f; } } } } /* send the packet with the compressed data to the encoder */ int ret = avcodec_send_frame(context, in); if (ret < 0) { const char* err = av_err2str(ret); // Ignore errors: AAC encoder sometimes returns -22 // The log message from ffmpeg is '[aac @ 0x7f140db753c0] Input contains (near) NaN/+-Inf' if (ret == AVERROR(EINVAL)) { WLog_DBG(TAG, "Error submitting the packet to the encoder %s [%d], ignoring", err, ret); return TRUE; } WLog_ERR(TAG, "Error submitting the packet to the encoder %s [%d]", err, ret); return FALSE; } /* read all the output frames (in general there may be any number of them */ while (TRUE) { ret = avcodec_receive_packet(context, packet); if ((ret == AVERROR(EAGAIN)) || (ret == AVERROR_EOF)) break; if (ret < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during encoding %s [%d]", err, ret); return FALSE; } WINPR_ASSERT(packet->size >= 0); if (!Stream_EnsureRemainingCapacity(out, (size_t)packet->size)) return FALSE; Stream_Write(out, packet->data, (size_t)packet->size); av_packet_unref(packet); } return TRUE; } static BOOL ffmpeg_fill_frame(AVFrame* WINPR_RESTRICT frame, const AUDIO_FORMAT* WINPR_RESTRICT inputFormat, const BYTE* WINPR_RESTRICT data, size_t size) { int ret = 0; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 28, 100) frame->channels = inputFormat->nChannels; frame->channel_layout = av_get_default_channel_layout(frame->channels); #else av_channel_layout_default(&frame->ch_layout, inputFormat->nChannels); #endif WINPR_ASSERT(inputFormat->nSamplesPerSec <= INT_MAX); frame->sample_rate = (int)inputFormat->nSamplesPerSec; frame->format = ffmpeg_sample_format(inputFormat); const int bpp = av_get_bytes_per_sample(WINPR_ASSERTING_INT_CAST(enum AVSampleFormat, frame->format)); WINPR_ASSERT(bpp >= 0); WINPR_ASSERT(size <= INT_MAX); const size_t nb_samples = size / inputFormat->nChannels / (size_t)bpp; frame->nb_samples = (int)nb_samples; if ((ret = avcodec_fill_audio_frame( frame, inputFormat->nChannels, WINPR_ASSERTING_INT_CAST(enum AVSampleFormat, frame->format), data, (int)size, 1)) < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during audio frame fill %s [%d]", err, ret); return FALSE; } return TRUE; } #if defined(SWRESAMPLE_FOUND) static BOOL ffmpeg_decode(AVCodecContext* WINPR_RESTRICT dec_ctx, AVPacket* WINPR_RESTRICT pkt, AVFrame* WINPR_RESTRICT frame, SwrContext* WINPR_RESTRICT resampleContext, AVFrame* WINPR_RESTRICT resampled, wStream* WINPR_RESTRICT out) #else static BOOL ffmpeg_decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, AVAudioResampleContext* resampleContext, AVFrame* resampled, wStream* out) #endif { /* send the packet with the compressed data to the decoder */ int ret = avcodec_send_packet(dec_ctx, pkt); if (ret < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error submitting the packet to the decoder %s [%d]", err, ret); return FALSE; } /* read all the output frames (in general there may be any number of them */ while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; if (ret < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during decoding %s [%d]", err, ret); return FALSE; } #if defined(SWRESAMPLE_FOUND) if (!swr_is_initialized(resampleContext)) { if ((ret = swr_config_frame(resampleContext, resampled, frame)) < 0) { #else if (!avresample_is_open(resampleContext)) { if ((ret = avresample_config(resampleContext, resampled, frame)) < 0) { #endif const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } #if defined(SWRESAMPLE_FOUND) ret = (swr_init(resampleContext)); #else ret = (avresample_open(resampleContext)); #endif if (ret < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } } #if defined(SWRESAMPLE_FOUND) ret = swr_convert_frame(resampleContext, resampled, frame); #else ret = avresample_convert_frame(resampleContext, resampled, frame); #endif if (ret < 0) { const char* err = av_err2str(ret); WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret); return FALSE; } { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) WINPR_ASSERT(resampled->ch_layout.nb_channels >= 0); const size_t nrchannels = (size_t)resampled->ch_layout.nb_channels; #else const size_t nrchannels = resampled->channels; #endif WINPR_ASSERT(resampled->nb_samples >= 0); const size_t data_size = nrchannels * (size_t)resampled->nb_samples * 2ull; if (!Stream_EnsureRemainingCapacity(out, data_size)) return FALSE; Stream_Write(out, resampled->data[0], data_size); } } return TRUE; } BOOL freerdp_dsp_ffmpeg_supports_format(const AUDIO_FORMAT* WINPR_RESTRICT format, BOOL encode) { enum AVCodecID id = ffmpeg_get_avcodec(format); if (ffmpeg_codec_is_filtered(id, encode)) return FALSE; if (encode) return avcodec_find_encoder(id) != nullptr; else return avcodec_find_decoder(id) != nullptr; } FREERDP_DSP_CONTEXT* freerdp_dsp_ffmpeg_context_new(BOOL encode) { FREERDP_DSP_CONTEXT* context = nullptr; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) avcodec_register_all(); #endif context = calloc(1, sizeof(FREERDP_DSP_CONTEXT)); if (!context) goto fail; if (!freerdp_dsp_common_context_init(&context->common, encode)) goto fail; return context; fail: WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC freerdp_dsp_ffmpeg_context_free(context); WINPR_PRAGMA_DIAG_POP return nullptr; } void freerdp_dsp_ffmpeg_context_free(FREERDP_DSP_CONTEXT* context) { if (context) { ffmpeg_close_context(context); freerdp_dsp_common_context_uninit(&context->common); free(context); } } BOOL freerdp_dsp_ffmpeg_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, const AUDIO_FORMAT* WINPR_RESTRICT targetFormat) { if (!context || !targetFormat) return FALSE; ffmpeg_close_context(context); context->common.format = *targetFormat; return ffmpeg_open_context(context); } static BOOL freerdp_dsp_channel_mix(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, const BYTE* WINPR_RESTRICT src, size_t size, const AUDIO_FORMAT* WINPR_RESTRICT srcFormat, const BYTE** WINPR_RESTRICT data, size_t* WINPR_RESTRICT length, AUDIO_FORMAT* WINPR_RESTRICT dstFormat) { UINT32 bpp = 0; size_t samples = 0; if (!context || !data || !length || !dstFormat) return FALSE; if (srcFormat->wFormatTag != WAVE_FORMAT_PCM) return FALSE; bpp = srcFormat->wBitsPerSample > 8 ? 2 : 1; samples = size / bpp / srcFormat->nChannels; *dstFormat = *srcFormat; if (context->common.format.nChannels == srcFormat->nChannels) { *data = src; *length = size; return TRUE; } Stream_ResetPosition(context->common.channelmix); /* Destination has more channels than source */ if (context->common.format.nChannels > srcFormat->nChannels) { switch (srcFormat->nChannels) { case 1: if (!Stream_EnsureCapacity(context->common.channelmix, size * 2)) return FALSE; for (size_t x = 0; x < samples; x++) { for (size_t y = 0; y < bpp; y++) Stream_Write_UINT8(context->common.channelmix, src[x * bpp + y]); for (size_t y = 0; y < bpp; y++) Stream_Write_UINT8(context->common.channelmix, src[x * bpp + y]); } Stream_SealLength(context->common.channelmix); *data = Stream_Buffer(context->common.channelmix); *length = Stream_Length(context->common.channelmix); dstFormat->nChannels = 2; return TRUE; case 2: /* We only support stereo, so we can not handle this case. */ default: /* Unsupported number of channels */ WLog_WARN(TAG, "[%s] unsupported source channel count %" PRIu16, __func__, srcFormat->nChannels); return FALSE; } } /* Destination has less channels than source */ switch (srcFormat->nChannels) { case 2: if (!Stream_EnsureCapacity(context->common.channelmix, size / 2)) return FALSE; /* Simply drop second channel. * TODO: Calculate average */ for (size_t x = 0; x < samples; x++) { for (size_t y = 0; y < bpp; y++) Stream_Write_UINT8(context->common.channelmix, src[2 * x * bpp + y]); } Stream_SealLength(context->common.channelmix); *data = Stream_Buffer(context->common.channelmix); *length = Stream_Length(context->common.channelmix); dstFormat->nChannels = 1; return TRUE; case 1: /* Invalid, do we want to use a 0 channel sound? */ default: /* Unsupported number of channels */ WLog_WARN(TAG, "[%s] unsupported channel count %" PRIu16, __func__, srcFormat->nChannels); return FALSE; } return FALSE; } BOOL freerdp_dsp_ffmpeg_encode(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, const AUDIO_FORMAT* WINPR_RESTRICT format, const BYTE* WINPR_RESTRICT sdata, size_t length, wStream* WINPR_RESTRICT out) { AUDIO_FORMAT fmt = WINPR_C_ARRAY_INIT; if (!context || !format || !sdata || !out || !context->common.encoder) return FALSE; /* https://github.com/FreeRDP/FreeRDP/issues/7607 * * we get noisy data with channel transformation, so do it ourselves. */ const BYTE* data = nullptr; if (!freerdp_dsp_channel_mix(context, sdata, length, format, &data, &length, &fmt)) return FALSE; /* Create input frame */ if (!ffmpeg_fill_frame(context->frame, format, data, length)) return FALSE; ffmpeg_setup_resample_frame(context, format); /* Resample to desired format. */ if (!ffmpeg_resample_frame(context->rcontext, context->frame, context->resampled)) return FALSE; if (context->context->frame_size <= 0) { return ffmpeg_encode_frame(context->context, context->resampled, context->packet, out); } else { int copied = 0; int rest = context->resampled->nb_samples; do { int inSamples = rest; if ((inSamples < 0) || (context->bufferedSamples > (UINT32)(INT_MAX - inSamples))) return FALSE; if (inSamples + (int)context->bufferedSamples > context->context->frame_size) inSamples = context->context->frame_size - (int)context->bufferedSamples; #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) const int nrchannels = context->context->ch_layout.nb_channels; #else const int nrchannels = context->context->channels; #endif const int rc = av_samples_copy(context->buffered->extended_data, context->resampled->extended_data, (int)context->bufferedSamples, copied, inSamples, nrchannels, context->context->sample_fmt); if (rc < 0) return FALSE; rest -= inSamples; copied += inSamples; context->bufferedSamples += (UINT32)inSamples; if (context->context->frame_size <= (int)context->bufferedSamples) { /* Encode in desired format. */ if (!ffmpeg_encode_frame(context->context, context->buffered, context->packet, out)) return FALSE; context->bufferedSamples = 0; } } while (rest > 0); return TRUE; } } BOOL freerdp_dsp_ffmpeg_decode(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, const AUDIO_FORMAT* WINPR_RESTRICT srcFormat, const BYTE* WINPR_RESTRICT data, size_t length, wStream* WINPR_RESTRICT out) { if (!context || !srcFormat || !data || !out || context->common.encoder) return FALSE; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100) av_init_packet(context->packet); #endif context->packet->data = WINPR_CAST_CONST_PTR_AWAY(data, uint8_t*); WINPR_ASSERT(length <= INT_MAX); context->packet->size = (int)length; return ffmpeg_decode(context->context, context->packet, context->frame, context->rcontext, context->resampled, out); }