CFFmpegImage::~CFFmpegImage() { av_frame_free(&m_pFrame); // someone could have forgotten to call us CleanupLocalOutputBuffer(); if (m_fctx) { avcodec_free_context(&m_codec_ctx); avformat_close_input(&m_fctx); } if (m_ioctx) FreeIOCtx(&m_ioctx); m_buf.data = nullptr; m_buf.pos = 0; m_buf.size = 0; }
CFFmpegImage::~CFFmpegImage() { av_frame_free(&m_pFrame); // someone could have forgotten to call us CleanupLocalOutputBuffer(); if (m_ioctx) FreeIOCtx(&m_ioctx); if (m_fctx) { for (unsigned int i = 0; i < m_fctx->nb_streams; i++) { avcodec_close(m_fctx->streams[i]->codec); } avformat_close_input(&m_fctx); } m_buf.data = nullptr; m_buf.pos = 0; m_buf.size = 0; }
void CFFmpegImage::ReleaseThumbnailBuffer() { CleanupLocalOutputBuffer(); }
bool CFFmpegImage::CreateThumbnailFromSurface(unsigned char* bufferin, unsigned int width, unsigned int height, unsigned int format, unsigned int pitch, const std::string& destFile, unsigned char* &bufferout, unsigned int &bufferoutSize) { // It seems XB_FMT_A8R8G8B8 mean RGBA and not ARGB if (format != XB_FMT_A8R8G8B8) { CLog::Log(LOGERROR, "Supplied format: %d is not supported.", format); return false; } bool jpg_output = false; if (m_strMimeType == "image/jpeg" || m_strMimeType == "image/jpg") jpg_output = true; else if (m_strMimeType == "image/png") jpg_output = false; else { CLog::Log(LOGERROR, "Output Format is not supported: %s is not supported.", destFile.c_str()); return false; } ThumbDataManagement tdm; tdm.codec = avcodec_find_encoder(jpg_output ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG); if (!tdm.codec) { CLog::Log(LOGERROR, "Your are missing a working encoder for format: %d", jpg_output ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG); return false; } tdm.avOutctx = avcodec_alloc_context3(tdm.codec); if (!tdm.avOutctx) { CLog::Log(LOGERROR, "Could not allocate context for thumbnail: %s", destFile.c_str()); return false; } tdm.avOutctx->height = height; tdm.avOutctx->width = width; tdm.avOutctx->time_base.num = 1; tdm.avOutctx->time_base.den = 1; tdm.avOutctx->pix_fmt = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA; tdm.avOutctx->flags = CODEC_FLAG_QSCALE; tdm.avOutctx->mb_lmin = tdm.avOutctx->qmin * FF_QP2LAMBDA; tdm.avOutctx->mb_lmax = tdm.avOutctx->qmax * FF_QP2LAMBDA; tdm.avOutctx->global_quality = tdm.avOutctx->qmin * FF_QP2LAMBDA; unsigned int internalBufOutSize = 0; int size = avpicture_get_size(tdm.avOutctx->pix_fmt, tdm.avOutctx->width, tdm.avOutctx->height); if (size < 0) { CLog::Log(LOGERROR, "Could not compute picture size for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } internalBufOutSize = (unsigned int) size; m_outputBuffer = (uint8_t*) av_malloc(internalBufOutSize); if (!m_outputBuffer) { CLog::Log(LOGERROR, "Could not generate allocate memory for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } tdm.intermediateBuffer = (uint8_t*) av_malloc(internalBufOutSize); if (!tdm.intermediateBuffer) { CLog::Log(LOGERROR, "Could not allocate memory for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } if (avcodec_open2(tdm.avOutctx, tdm.codec, NULL) < 0) { CLog::Log(LOGERROR, "Could not open avcodec context thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } tdm.frame_input = av_frame_alloc(); if (!tdm.frame_input) { CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } // convert the RGB32 frame to AV_PIX_FMT_YUV420P - we use this later on as AV_PIX_FMT_YUVJ420P tdm.frame_temporary = av_frame_alloc(); if (!tdm.frame_temporary) { CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } if (avpicture_fill((AVPicture*)tdm.frame_temporary, tdm.intermediateBuffer, jpg_output ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_RGBA, width, height) < 0) { CLog::Log(LOGERROR, "Could not fill picture for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } uint8_t* src[] = { bufferin, NULL, NULL, NULL }; int srcStride[] = { (int) pitch, 0, 0, 0}; //input size == output size which means only pix_fmt conversion tdm.sws = sws_getContext(width, height, AV_PIX_FMT_RGB32, width, height, jpg_output ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_RGBA, 0, 0, 0, 0); if (!tdm.sws) { CLog::Log(LOGERROR, "Could not setup scaling context for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } // Setup jpeg range for sws if (jpg_output) { int* inv_table = nullptr; int* table = nullptr; int srcRange, dstRange, brightness, contrast, saturation; if (sws_getColorspaceDetails(tdm.sws, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation) < 0) { CLog::Log(LOGERROR, "SWS_SCALE failed to get ColorSpaceDetails for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } dstRange = 1; // jpeg full range yuv420p output srcRange = 0; // full range RGB32 input if (sws_setColorspaceDetails(tdm.sws, inv_table, srcRange, table, dstRange, brightness, contrast, saturation) < 0) { CLog::Log(LOGERROR, "SWS_SCALE failed to set ColorSpace Details for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } } if (sws_scale(tdm.sws, src, srcStride, 0, height, tdm.frame_temporary->data, tdm.frame_temporary->linesize) < 0) { CLog::Log(LOGERROR, "SWS_SCALE failed for thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } tdm.frame_input->pts = 1; tdm.frame_input->quality = tdm.avOutctx->global_quality; tdm.frame_input->data[0] = (uint8_t*) tdm.frame_temporary->data[0]; tdm.frame_input->data[1] = (uint8_t*) tdm.frame_temporary->data[1]; tdm.frame_input->data[2] = (uint8_t*) tdm.frame_temporary->data[2]; tdm.frame_input->height = height; tdm.frame_input->width = width; tdm.frame_input->linesize[0] = tdm.frame_temporary->linesize[0]; tdm.frame_input->linesize[1] = tdm.frame_temporary->linesize[1]; tdm.frame_input->linesize[2] = tdm.frame_temporary->linesize[2]; // this is deprecated but mjpeg is not yet transitioned tdm.frame_input->format = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA; int got_package = 0; AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = m_outputBuffer; avpkt.size = internalBufOutSize; if ((avcodec_encode_video2(tdm.avOutctx, &avpkt, tdm.frame_input, &got_package) < 0) || (got_package == 0)) { CLog::Log(LOGERROR, "Could not encode thumbnail: %s", destFile.c_str()); CleanupLocalOutputBuffer(); return false; } bufferoutSize = avpkt.size; bufferout = m_outputBuffer; return true; }
CFFmpegImage::~CFFmpegImage() { av_frame_free(&m_pFrame); // someone could have forgotten to call us CleanupLocalOutputBuffer(); }