static void SetColorDetailsByFFmpeg(VideoFrame *f, AVFrame* frame, AVCodecContext* codec_ctx) { ColorSpace cs = colorSpaceFromFFmpeg(av_frame_get_colorspace(frame)); if (cs == ColorSpace_Unknown) cs = colorSpaceFromFFmpeg(codec_ctx->colorspace); f->setColorSpace(cs); ColorRange cr = colorRangeFromFFmpeg(av_frame_get_color_range(frame)); if (cr == ColorRange_Unknown) { // check yuvj format. TODO: deprecated, check only for old ffmpeg? const AVPixelFormat pixfmt = (AVPixelFormat)frame->format; switch (pixfmt) { //case QTAV_PIX_FMT_C(YUVJ411P): //not in ffmpeg<2 and libav case QTAV_PIX_FMT_C(YUVJ420P): case QTAV_PIX_FMT_C(YUVJ422P): case QTAV_PIX_FMT_C(YUVJ440P): case QTAV_PIX_FMT_C(YUVJ444P): cr = ColorRange_Full; break; default: break; } } if (cr == ColorRange_Unknown) { cr = colorRangeFromFFmpeg(codec_ctx->color_range); if (cr == ColorRange_Unknown) { if (f->format().isXYZ()){ cr = ColorRange_Full; cs = ColorSpace_XYZ; // not here } else if (!f->format().isRGB()) { //qDebug("prefer limited yuv range"); cr = ColorRange_Limited; } } } f->setColorRange(cr); }
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::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::LogFunction(LOGERROR, __FUNCTION__, "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::LogFunction(LOGERROR, __FUNCTION__, "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::LogFunction(LOGERROR, __FUNCTION__, "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 = av_frame_get_color_range(frame); 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::LogFunction(LOGERROR, __FUNCTION__, "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; }