bool CFFmpegImage::DecodeFrame(AVFrame* frame, unsigned int width, unsigned int height, unsigned int pitch, unsigned char * const pixels) { if (pixels == nullptr) { CLog::Log(LOGERROR, "%s - No valid buffer pointer (nullptr) passed", __FUNCTION__); return false; } AVFrame* pictureRGB = av_frame_alloc(); if (!pictureRGB) { CLog::LogF(LOGERROR, "AVFrame could not be allocated"); return false; } // we align on 16 as the input provided by the Texture also aligns the buffer size to 16 int size = av_image_fill_arrays(pictureRGB->data, pictureRGB->linesize, NULL, AV_PIX_FMT_RGB32, width, height, 16); if (size < 0) { CLog::LogF(LOGERROR, "Could not allocate AVFrame member with %i x %i pixes", width, height); av_frame_free(&pictureRGB); return false; } bool needsCopy = false; int pixelsSize = pitch * height; bool aligned = (((uintptr_t)(const void *)(pixels)) % (32) == 0); if (!aligned) CLog::Log(LOGDEBUG, "Alignment of external buffer is not suitable for ffmpeg intrinsics - please fix your malloc"); if (aligned && size == pixelsSize && (int)pitch == pictureRGB->linesize[0]) { // We can use the pixels buffer directly pictureRGB->data[0] = pixels; } else { // We need an extra buffer and copy it manually afterwards pictureRGB->format = AV_PIX_FMT_RGB32; pictureRGB->width = width; pictureRGB->height = height; // we copy the data manually later so give a chance to intrinsics (e.g. mmx, neon) if (av_frame_get_buffer(pictureRGB, 32) < 0) { CLog::LogF(LOGERROR, "Could not allocate temp buffer of size %i bytes", size); av_frame_free(&pictureRGB); return false; } needsCopy = true; } // Especially jpeg formats are full range this we need to take care here // Input Formats like RGBA are handled correctly automatically AVColorRange range = frame->color_range; AVPixelFormat pixFormat = ConvertFormats(frame); // assumption quadratic maximums e.g. 2048x2048 float ratio = m_width / (float)m_height; unsigned int nHeight = m_originalHeight; unsigned int nWidth = m_originalWidth; if (nHeight > height) { nHeight = height; nWidth = (unsigned int)(nHeight * ratio + 0.5f); } if (nWidth > width) { nWidth = width; nHeight = (unsigned int)(nWidth / ratio + 0.5f); } struct SwsContext* context = sws_getContext(m_originalWidth, m_originalHeight, pixFormat, nWidth, nHeight, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); if (range == AVCOL_RANGE_JPEG) { int* inv_table = nullptr; int* table = nullptr; int srcRange, dstRange, brightness, contrast, saturation; sws_getColorspaceDetails(context, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation); srcRange = 1; sws_setColorspaceDetails(context, inv_table, srcRange, table, dstRange, brightness, contrast, saturation); } sws_scale(context, frame->data, frame->linesize, 0, m_originalHeight, pictureRGB->data, pictureRGB->linesize); sws_freeContext(context); if (needsCopy) { int minPitch = std::min((int)pitch, pictureRGB->linesize[0]); if (minPitch < 0) { CLog::LogF(LOGERROR, "negative pitch or height"); av_frame_free(&pictureRGB); return false; } const unsigned char *src = pictureRGB->data[0]; unsigned char* dst = pixels; for (unsigned int y = 0; y < nHeight; y++) { memcpy(dst, src, minPitch); src += pictureRGB->linesize[0]; dst += pitch; } av_frame_free(&pictureRGB); } else { // we only lended the data so don't get it deleted pictureRGB->data[0] = nullptr; av_frame_free(&pictureRGB); } // update width and height original dimensions are kept m_height = nHeight; m_width = nWidth; return true; }
bool CFFmpegImage::Decode(unsigned char * const pixels, unsigned int width, unsigned int height, unsigned int pitch, unsigned int format) { if (m_width == 0 || m_height == 0 || format != XB_FMT_A8R8G8B8) return false; if (!m_pFrame || !m_pFrame->data[0]) { CLog::LogFunction(LOGERROR, __FUNCTION__, "AVFrame member not allocated"); return false; } AVPicture* pictureRGB = static_cast<AVPicture*>(av_mallocz(sizeof(AVPicture))); if (!pictureRGB) { CLog::LogFunction(LOGERROR, __FUNCTION__, "AVPicture could not be allocated"); return false; } int size = avpicture_fill(pictureRGB, NULL, AV_PIX_FMT_RGB32, width, height); if (size < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "Could not allocate AVPicture member with %i x %i pixes", width, height); av_free(pictureRGB); return false; } bool needsCopy = false; int pixelsSize = pitch * height; if (size == pixelsSize && (int) pitch == pictureRGB->linesize[0]) { // We can use the pixels buffer directly pictureRGB->data[0] = pixels; } else { // We need an extra buffer and copy it manually afterwards if (avpicture_alloc(pictureRGB, AV_PIX_FMT_RGB32, width, height) < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "Could not allocate temp buffer of size %i bytes", size); av_free(pictureRGB); return false; } needsCopy = true; } // Especially jpeg formats are full range this we need to take care here // Input Formats like RGBA are handled correctly automatically AVColorRange range = av_frame_get_color_range(m_pFrame); AVPixelFormat pixFormat = ConvertFormats(m_pFrame); // assumption quadratic maximums e.g. 2048x2048 float ratio = m_width / (float) m_height; unsigned int nHeight = m_originalHeight; unsigned int nWidth = m_originalWidth; if (nHeight > height) { nHeight = height; nWidth = (unsigned int) (nHeight * ratio + 0.5f); } if (nWidth > width) { nWidth = width; nHeight = (unsigned int) (nWidth / ratio + 0.5f); } struct SwsContext* context = sws_getContext(m_originalWidth, m_originalHeight, pixFormat, nWidth, nHeight, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); if (range == AVCOL_RANGE_JPEG) { int* inv_table = nullptr; int* table = nullptr; int srcRange, dstRange, brightness, contrast, saturation; sws_getColorspaceDetails(context, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation); srcRange = 1; sws_setColorspaceDetails(context, inv_table, srcRange, table, dstRange, brightness, contrast, saturation); } sws_scale(context, m_pFrame->data, m_pFrame->linesize, 0, m_originalHeight, pictureRGB->data, pictureRGB->linesize); sws_freeContext(context); if (needsCopy) { int minPitch = std::min((int)pitch, pictureRGB->linesize[0]); if (minPitch < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "negative pitch or height"); av_free(pictureRGB); return false; } const unsigned char *src = pictureRGB->data[0]; unsigned char* dst = pixels; for (unsigned int y = 0; y < nHeight; y++) { memcpy(dst, src, minPitch); src += pictureRGB->linesize[0]; dst += pitch; } avpicture_free(pictureRGB); } pictureRGB->data[0] = nullptr; avpicture_free(pictureRGB); // update width and height original dimensions are kept m_height = nHeight; m_width = nWidth; return true; }
bool CFFmpegImage::Decode(unsigned char * const pixels, unsigned int width, unsigned int height, unsigned int pitch, unsigned int format) { if (m_width == 0 || m_height == 0 || format != XB_FMT_A8R8G8B8) return false; if (!m_pFrame || !m_pFrame->data) { CLog::LogFunction(LOGERROR, __FUNCTION__, "AVFrame member not allocated"); return false; } AVPicture* pictureRGB = static_cast<AVPicture*>(av_mallocz(sizeof(AVPicture))); int size = avpicture_fill(pictureRGB, NULL, PIX_FMT_RGB32, width, height); if (size < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "Could not allocate AVPicture member with %i x %i pixes", width, height); av_free(pictureRGB); return false; } bool needsCopy = false; int pixelsSize = pitch * height; if (size == pixelsSize && pitch == pictureRGB->linesize[0]) { // We can use the pixels buffer directly pictureRGB->data[0] = pixels; } else { // We need an extra buffer and copy it manually afterwards if (avpicture_alloc(pictureRGB, PIX_FMT_RGB32, width, height) < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "Could not allocate temp buffer of size %i bytes", size); av_free(pictureRGB); return false; } needsCopy = true; } AVPixelFormat pixFormat = ConvertFormats(m_pFrame); struct SwsContext* context = sws_getContext(m_width, m_height, pixFormat, width, height, PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); sws_scale(context, m_pFrame->data, m_pFrame->linesize, 0, m_height, pictureRGB->data, pictureRGB->linesize); sws_freeContext(context); if (needsCopy) { int minPitch = std::min((int)pitch, pictureRGB->linesize[0]); int minHeight = std::min((int)height, (size / pictureRGB->linesize[0])); if (minPitch < 0 || minHeight < 0) { CLog::LogFunction(LOGERROR, __FUNCTION__, "negative pitch or height"); av_free(pictureRGB); return false; } const unsigned char *src = pictureRGB->data[0]; unsigned char* dst = pixels; for (int y = 0; y < minHeight; y++) { memcpy(dst, src, minPitch); src += pictureRGB->linesize[0]; dst += pitch; } avpicture_free(pictureRGB); } pictureRGB->data[0] = nullptr; avpicture_free(pictureRGB); m_originalHeight = m_height = height; m_originalWidth = m_width = width; return true; }