Merge pull request #26350 from jjd-uk/estuary_media_align
[xbmc.git] / xbmc / guilib / FFmpegImage.cpp
blobf1d50fcb75fa2583f9a146496db5400799591fd2
1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8 #include "FFmpegImage.h"
10 #include "cores/FFmpeg.h"
11 #include "guilib/Texture.h"
12 #include "settings/AdvancedSettings.h"
13 #include "settings/Settings.h"
14 #include "settings/SettingsComponent.h"
15 #include "utils/log.h"
17 #include <algorithm>
19 extern "C"
21 #include <libavformat/avformat.h>
22 #include <libavutil/imgutils.h>
23 #include <libavcodec/avcodec.h>
24 #include <libavutil/avutil.h>
25 #include <libswscale/swscale.h>
26 #include <libavutil/pixdesc.h>
29 Frame::Frame(const Frame& src) :
30 m_delay(src.m_delay),
31 m_imageSize(src.m_imageSize),
32 m_height(src.m_height),
33 m_width(src.m_width)
35 if (src.m_pImage)
37 m_pImage = (unsigned char*) av_malloc(m_imageSize);
38 memcpy(m_pImage, src.m_pImage, m_imageSize);
42 Frame::~Frame()
44 av_free(m_pImage);
45 m_pImage = nullptr;
48 struct ThumbDataManagement
50 uint8_t* intermediateBuffer = nullptr; // gets av_alloced
51 AVFrame* frame_input = nullptr;
52 AVFrame* frame_temporary = nullptr;
53 SwsContext* sws = nullptr;
54 AVCodecContext* avOutctx = nullptr;
55 const AVCodec* codec = nullptr;
56 ~ThumbDataManagement()
58 av_free(intermediateBuffer);
59 intermediateBuffer = nullptr;
60 av_frame_free(&frame_input);
61 frame_input = nullptr;
62 av_frame_free(&frame_temporary);
63 frame_temporary = nullptr;
64 avcodec_free_context(&avOutctx);
65 avOutctx = nullptr;
66 sws_freeContext(sws);
67 sws = nullptr;
71 // valid positions are including 0 (start of buffer)
72 // and bufferSize -1 last data point
73 static inline size_t Clamp(int64_t newPosition, size_t bufferSize)
75 return std::min(std::max((int64_t) 0, newPosition), (int64_t) (bufferSize -1));
78 static int mem_file_read(void *h, uint8_t* buf, int size)
80 if (size < 0)
81 return -1;
83 MemBuffer* mbuf = static_cast<MemBuffer*>(h);
84 int64_t unread = mbuf->size - mbuf->pos;
85 if (unread <= 0)
86 return AVERROR_EOF;
88 size_t tocopy = std::min((size_t)size, (size_t)unread);
89 memcpy(buf, mbuf->data + mbuf->pos, tocopy);
90 mbuf->pos += tocopy;
91 return static_cast<int>(tocopy);
94 static int64_t mem_file_seek(void *h, int64_t pos, int whence)
96 MemBuffer* mbuf = static_cast<MemBuffer*>(h);
97 if (whence == AVSEEK_SIZE)
98 return mbuf->size;
100 // we want to ignore the AVSEEK_FORCE flag and therefore mask it away
101 whence &= ~AVSEEK_FORCE;
103 if (whence == SEEK_SET)
105 mbuf->pos = Clamp(pos, mbuf->size);
107 else if (whence == SEEK_CUR)
109 mbuf->pos = Clamp(((int64_t)mbuf->pos) + pos, mbuf->size);
111 else
112 CLog::LogF(LOGERROR, "Unknown seek mode: {}", whence);
114 return mbuf->pos;
117 CFFmpegImage::CFFmpegImage(const std::string& strMimeType) : m_strMimeType(strMimeType)
119 m_hasAlpha = false;
120 m_pFrame = nullptr;
121 m_outputBuffer = nullptr;
124 CFFmpegImage::~CFFmpegImage()
126 av_frame_free(&m_pFrame);
127 // someone could have forgotten to call us
128 CleanupLocalOutputBuffer();
129 if (m_fctx)
131 avcodec_free_context(&m_codec_ctx);
132 avformat_close_input(&m_fctx);
134 if (m_ioctx)
135 FreeIOCtx(&m_ioctx);
137 m_buf.data = nullptr;
138 m_buf.pos = 0;
139 m_buf.size = 0;
142 bool CFFmpegImage::LoadImageFromMemory(unsigned char* buffer, unsigned int bufSize,
143 unsigned int width, unsigned int height)
146 if (!Initialize(buffer, bufSize))
148 //log
149 return false;
152 av_frame_free(&m_pFrame);
153 m_pFrame = ExtractFrame();
155 return !(m_pFrame == nullptr);
158 bool CFFmpegImage::Initialize(unsigned char* buffer, size_t bufSize)
160 int bufferSize = 4096;
161 uint8_t* fbuffer = (uint8_t*)av_malloc(bufferSize + AV_INPUT_BUFFER_PADDING_SIZE);
162 if (!fbuffer)
164 CLog::LogF(LOGERROR, "Could not allocate buffer");
165 return false;
167 m_buf.data = buffer;
168 m_buf.size = bufSize;
169 m_buf.pos = 0;
171 m_ioctx = avio_alloc_context(fbuffer, bufferSize, 0, &m_buf,
172 mem_file_read, NULL, mem_file_seek);
174 if (!m_ioctx)
176 av_free(fbuffer);
177 CLog::LogF(LOGERROR, "Could not allocate AVIOContext");
178 return false;
181 // signal to ffmepg this is not streaming protocol
182 m_ioctx->max_packet_size = bufferSize;
184 m_fctx = avformat_alloc_context();
185 if (!m_fctx)
187 FreeIOCtx(&m_ioctx);
188 CLog::LogF(LOGERROR, "Could not allocate AVFormatContext");
189 return false;
192 m_fctx->pb = m_ioctx;
194 // Some clients have pngs saved as jpeg or ask us for png but are jpeg
195 // mythv throws all mimetypes away and asks us with application/octet-stream
196 // this is poor man's fallback to at least identify png / jpeg
197 bool is_jpeg = (bufSize > 2 && buffer[0] == 0xFF && buffer[1] == 0xD8 && buffer[2] == 0xFF);
198 bool is_png = (bufSize > 3 && buffer[1] == 'P' && buffer[2] == 'N' && buffer[3] == 'G');
199 bool is_tiff = (bufSize > 2 && buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*');
201 // See Github #19113
202 #if LIBAVCODEC_VERSION_MAJOR < 60
203 constexpr char jpegFormat[] = "image2";
204 #else
205 constexpr char jpegFormat[] = "jpeg_pipe";
206 #endif
208 const AVInputFormat* inp = nullptr;
209 if (is_jpeg)
210 inp = av_find_input_format(jpegFormat);
211 else if (m_strMimeType == "image/apng")
212 inp = av_find_input_format("apng");
213 else if (is_png)
214 inp = av_find_input_format("png_pipe");
215 else if (is_tiff)
216 inp = av_find_input_format("tiff_pipe");
217 else if (m_strMimeType == "image/jp2")
218 inp = av_find_input_format("j2k_pipe");
219 else if (m_strMimeType == "image/webp")
220 inp = av_find_input_format("webp_pipe");
221 // brute force parse if above check already failed
222 else if (m_strMimeType == "image/jpeg" || m_strMimeType == "image/jpg")
223 inp = av_find_input_format(jpegFormat);
224 else if (m_strMimeType == "image/png")
225 inp = av_find_input_format("png_pipe");
226 else if (m_strMimeType == "image/tiff")
227 inp = av_find_input_format("tiff_pipe");
228 else if (m_strMimeType == "image/gif")
229 inp = av_find_input_format("gif");
230 else if (m_strMimeType == "image/avif")
231 inp = av_find_input_format("avif");
233 if (avformat_open_input(&m_fctx, NULL, inp, NULL) < 0)
235 CLog::Log(LOGERROR, "Could not find suitable input format: {}", m_strMimeType);
236 avformat_close_input(&m_fctx);
237 FreeIOCtx(&m_ioctx);
238 return false;
241 if (m_fctx->nb_streams <= 0)
243 avformat_close_input(&m_fctx);
244 FreeIOCtx(&m_ioctx);
245 return false;
247 AVCodecParameters* codec_params = m_fctx->streams[0]->codecpar;
248 const AVCodec* codec = avcodec_find_decoder(codec_params->codec_id);
249 m_codec_ctx = avcodec_alloc_context3(codec);
250 if (!m_codec_ctx)
252 avformat_close_input(&m_fctx);
253 FreeIOCtx(&m_ioctx);
254 return false;
257 if (avcodec_parameters_to_context(m_codec_ctx, codec_params) < 0)
259 avformat_close_input(&m_fctx);
260 avcodec_free_context(&m_codec_ctx);
261 FreeIOCtx(&m_ioctx);
262 return false;
265 if (avcodec_open2(m_codec_ctx, codec, NULL) < 0)
267 avformat_close_input(&m_fctx);
268 avcodec_free_context(&m_codec_ctx);
269 FreeIOCtx(&m_ioctx);
270 return false;
273 return true;
276 AVFrame* CFFmpegImage::ExtractFrame()
278 if (!m_fctx || !m_fctx->streams[0])
280 CLog::LogF(LOGERROR, "No valid format context or stream");
281 return nullptr;
284 AVPacket pkt;
285 AVFrame* frame = av_frame_alloc();
286 int frame_decoded = 0;
287 int ret = 0;
288 ret = av_read_frame(m_fctx, &pkt);
289 if (ret < 0)
291 CLog::Log(LOGDEBUG, "Error [{}] while reading frame: {}", ret, strerror(AVERROR(ret)));
292 av_frame_free(&frame);
293 av_packet_unref(&pkt);
294 return nullptr;
297 ret = DecodeFFmpegFrame(m_codec_ctx, frame, &frame_decoded, &pkt);
298 if (ret < 0 || frame_decoded == 0 || !frame)
300 CLog::Log(LOGDEBUG, "Error [{}] while decoding frame: {}", ret, strerror(AVERROR(ret)));
301 av_frame_free(&frame);
302 av_packet_unref(&pkt);
303 return nullptr;
305 //we need milliseconds
307 #if LIBAVCODEC_VERSION_MAJOR < 60
308 frame->pkt_duration =
309 av_rescale_q(frame->pkt_duration, m_fctx->streams[0]->time_base, AVRational{1, 1000});
310 #else
311 frame->duration =
312 av_rescale_q(frame->duration, m_fctx->streams[0]->time_base, AVRational{1, 1000});
313 #endif
315 m_height = frame->height;
316 m_width = frame->width;
317 m_originalWidth = m_width;
318 m_originalHeight = m_height;
320 const AVPixFmtDescriptor* pixDescriptor = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(frame->format));
321 if (pixDescriptor && ((pixDescriptor->flags & (AV_PIX_FMT_FLAG_ALPHA | AV_PIX_FMT_FLAG_PAL)) != 0))
322 m_hasAlpha = true;
324 AVDictionary* dic = frame->metadata;
325 AVDictionaryEntry* entry = NULL;
326 if (dic)
328 entry = av_dict_get(dic, "Orientation", NULL, AV_DICT_MATCH_CASE);
329 if (entry && entry->value)
331 int orientation = atoi(entry->value);
332 // only values between including 0 and including 8
333 // http://sylvana.net/jpegcrop/exif_orientation.html
334 if (orientation >= 0 && orientation <= 8)
335 m_orientation = (unsigned int)orientation;
338 av_packet_unref(&pkt);
340 return frame;
343 AVPixelFormat CFFmpegImage::ConvertFormats(AVFrame* frame)
345 switch (frame->format) {
346 case AV_PIX_FMT_YUVJ420P:
347 return AV_PIX_FMT_YUV420P;
348 break;
349 case AV_PIX_FMT_YUVJ422P:
350 return AV_PIX_FMT_YUV422P;
351 break;
352 case AV_PIX_FMT_YUVJ444P:
353 return AV_PIX_FMT_YUV444P;
354 break;
355 case AV_PIX_FMT_YUVJ440P:
356 return AV_PIX_FMT_YUV440P;
357 default:
358 return static_cast<AVPixelFormat>(frame->format);
359 break;
363 void CFFmpegImage::FreeIOCtx(AVIOContext** ioctx)
365 av_freep(&((*ioctx)->buffer));
366 av_freep(ioctx);
369 bool CFFmpegImage::Decode(unsigned char * const pixels, unsigned int width, unsigned int height,
370 unsigned int pitch, unsigned int format)
372 if (m_width == 0 || m_height == 0 || format != XB_FMT_A8R8G8B8)
373 return false;
375 if (pixels == nullptr)
377 CLog::Log(LOGERROR, "{} - No valid buffer pointer (nullptr) passed", __FUNCTION__);
378 return false;
381 if (!m_pFrame || !m_pFrame->data[0])
383 CLog::LogF(LOGERROR, "AVFrame member not allocated");
384 return false;
387 return DecodeFrame(m_pFrame, width, height, pitch, pixels);
390 int CFFmpegImage::EncodeFFmpegFrame(AVCodecContext *avctx, AVPacket *pkt, int *got_packet, AVFrame *frame)
392 int ret;
394 *got_packet = 0;
396 ret = avcodec_send_frame(avctx, frame);
397 if (ret < 0)
398 return ret;
400 ret = avcodec_receive_packet(avctx, pkt);
401 if (!ret)
402 *got_packet = 1;
404 if (ret == AVERROR(EAGAIN))
405 return 0;
407 return ret;
410 int CFFmpegImage::DecodeFFmpegFrame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
412 int ret;
414 *got_frame = 0;
416 if (pkt)
418 ret = avcodec_send_packet(avctx, pkt);
419 // In particular, we don't expect AVERROR(EAGAIN), because we read all
420 // decoded frames with avcodec_receive_frame() until done.
421 if (ret < 0)
422 return ret;
425 ret = avcodec_receive_frame(avctx, frame);
426 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
427 return ret;
428 if (ret >= 0) // static code analysers would complain
429 *got_frame = 1;
431 return 0;
434 bool CFFmpegImage::DecodeFrame(AVFrame* frame, unsigned int width, unsigned int height, unsigned int pitch, unsigned char * const pixels)
436 if (pixels == nullptr)
438 CLog::Log(LOGERROR, "{} - No valid buffer pointer (nullptr) passed", __FUNCTION__);
439 return false;
442 AVFrame* pictureRGB = av_frame_alloc();
443 if (!pictureRGB)
445 CLog::LogF(LOGERROR, "AVFrame could not be allocated");
446 return false;
449 // we align on 16 as the input provided by the Texture also aligns the buffer size to 16
450 int size = av_image_fill_arrays(pictureRGB->data, pictureRGB->linesize, NULL, AV_PIX_FMT_RGB32, width, height, 16);
451 if (size < 0)
453 CLog::LogF(LOGERROR, "Could not allocate AVFrame member with {} x {} pixels", width, height);
454 av_frame_free(&pictureRGB);
455 return false;
458 bool needsCopy = false;
459 int pixelsSize = pitch * height;
460 bool aligned = (((uintptr_t)(const void *)(pixels)) % (32) == 0);
461 if (!aligned)
462 CLog::Log(LOGDEBUG, "Alignment of external buffer is not suitable for ffmpeg intrinsics - please fix your malloc");
464 if (aligned && size == pixelsSize && (int)pitch == pictureRGB->linesize[0])
466 // We can use the pixels buffer directly
467 pictureRGB->data[0] = pixels;
469 else
471 // We need an extra buffer and copy it manually afterwards
472 pictureRGB->format = AV_PIX_FMT_RGB32;
473 pictureRGB->width = width;
474 pictureRGB->height = height;
475 // we copy the data manually later so give a chance to intrinsics (e.g. mmx, neon)
476 if (av_frame_get_buffer(pictureRGB, 32) < 0)
478 CLog::LogF(LOGERROR, "Could not allocate temp buffer of size {} bytes", size);
479 av_frame_free(&pictureRGB);
480 return false;
482 needsCopy = true;
485 // Especially jpeg formats are full range this we need to take care here
486 // Input Formats like RGBA are handled correctly automatically
487 AVColorRange range = frame->color_range;
488 AVPixelFormat pixFormat = ConvertFormats(frame);
490 SwsContext* context = sws_getContext(m_originalWidth, m_originalHeight, pixFormat, width, height,
491 AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
493 if (range == AVCOL_RANGE_JPEG)
495 int* inv_table = nullptr;
496 int* table = nullptr;
497 int srcRange, dstRange, brightness, contrast, saturation;
498 sws_getColorspaceDetails(context, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation);
499 srcRange = 1;
500 sws_setColorspaceDetails(context, inv_table, srcRange, table, dstRange, brightness, contrast, saturation);
503 sws_scale(context, frame->data, frame->linesize, 0, m_originalHeight,
504 pictureRGB->data, pictureRGB->linesize);
505 sws_freeContext(context);
507 if (needsCopy)
509 int minPitch = std::min((int)pitch, pictureRGB->linesize[0]);
510 if (minPitch < 0)
512 CLog::LogF(LOGERROR, "negative pitch or height");
513 av_frame_free(&pictureRGB);
514 return false;
516 const unsigned char *src = pictureRGB->data[0];
517 unsigned char* dst = pixels;
519 for (unsigned int y = 0; y < height; y++)
521 memcpy(dst, src, minPitch);
522 src += pictureRGB->linesize[0];
523 dst += pitch;
525 av_frame_free(&pictureRGB);
527 else
529 // we only lended the data so don't get it deleted
530 pictureRGB->data[0] = nullptr;
531 av_frame_free(&pictureRGB);
534 // update width and height
535 m_height = height;
536 m_width = width;
538 return true;
541 bool CFFmpegImage::CreateThumbnailFromSurface(unsigned char* bufferin, unsigned int width,
542 unsigned int height, unsigned int format,
543 unsigned int pitch,
544 const std::string& destFile,
545 unsigned char* &bufferout,
546 unsigned int &bufferoutSize)
548 // It seems XB_FMT_A8R8G8B8 mean RGBA and not ARGB
549 if (format != XB_FMT_A8R8G8B8)
551 CLog::Log(LOGERROR, "Supplied format: {} is not supported.", format);
552 return false;
555 bool jpg_output = false;
556 if (m_strMimeType == "image/jpeg" || m_strMimeType == "image/jpg")
557 jpg_output = true;
558 else if (m_strMimeType == "image/png")
559 jpg_output = false;
560 else
562 CLog::Log(LOGERROR, "Output Format is not supported: {} is not supported.", destFile);
563 return false;
566 ThumbDataManagement tdm;
568 tdm.codec = avcodec_find_encoder(jpg_output ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG);
569 if (!tdm.codec)
571 CLog::Log(LOGERROR, "You are missing a working encoder for format: {}",
572 jpg_output ? "JPEG" : "PNG");
573 return false;
576 tdm.avOutctx = avcodec_alloc_context3(tdm.codec);
577 if (!tdm.avOutctx)
579 CLog::Log(LOGERROR, "Could not allocate context for thumbnail: {}", destFile);
580 return false;
583 tdm.avOutctx->height = height;
584 tdm.avOutctx->width = width;
585 tdm.avOutctx->time_base.num = 1;
586 tdm.avOutctx->time_base.den = 1;
587 tdm.avOutctx->pix_fmt = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA;
588 tdm.avOutctx->flags = AV_CODEC_FLAG_QSCALE;
589 tdm.avOutctx->mb_lmin = tdm.avOutctx->qmin * FF_QP2LAMBDA;
590 tdm.avOutctx->mb_lmax = tdm.avOutctx->qmax * FF_QP2LAMBDA;
591 unsigned int quality =
592 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageQualityJpeg;
593 tdm.avOutctx->global_quality =
594 jpg_output ? quality * FF_QP2LAMBDA : tdm.avOutctx->qmin * FF_QP2LAMBDA;
596 unsigned int internalBufOutSize = 0;
598 int size = av_image_get_buffer_size(tdm.avOutctx->pix_fmt, tdm.avOutctx->width, tdm.avOutctx->height, 16);
599 if (size < 0)
601 CLog::Log(LOGERROR, "Could not compute picture size for thumbnail: {}", destFile);
602 CleanupLocalOutputBuffer();
603 return false;
605 internalBufOutSize = (unsigned int) size;
607 tdm.intermediateBuffer = (uint8_t*) av_malloc(internalBufOutSize);
608 if (!tdm.intermediateBuffer)
610 CLog::Log(LOGERROR, "Could not allocate memory for thumbnail: {}", destFile);
611 CleanupLocalOutputBuffer();
612 return false;
615 if (avcodec_open2(tdm.avOutctx, tdm.codec, NULL) < 0)
617 CLog::Log(LOGERROR, "Could not open avcodec context thumbnail: {}", destFile);
618 CleanupLocalOutputBuffer();
619 return false;
622 tdm.frame_input = av_frame_alloc();
623 if (!tdm.frame_input)
625 CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: {}", destFile);
626 CleanupLocalOutputBuffer();
627 return false;
630 // convert the RGB32 frame to AV_PIX_FMT_YUV420P - we use this later on as AV_PIX_FMT_YUVJ420P
631 tdm.frame_temporary = av_frame_alloc();
632 if (!tdm.frame_temporary)
634 CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: {}", destFile);
635 CleanupLocalOutputBuffer();
636 return false;
639 if (av_image_fill_arrays(tdm.frame_temporary->data, tdm.frame_temporary->linesize, tdm.intermediateBuffer, jpg_output ? AV_PIX_FMT_YUV420P : AV_PIX_FMT_RGBA, width, height, 16) < 0)
641 CLog::Log(LOGERROR, "Could not fill picture for thumbnail: {}", destFile);
642 CleanupLocalOutputBuffer();
643 return false;
646 uint8_t* src[] = { bufferin, NULL, NULL, NULL };
647 int srcStride[] = { (int) pitch, 0, 0, 0};
649 //input size == output size which means only pix_fmt conversion
650 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);
651 if (!tdm.sws)
653 CLog::Log(LOGERROR, "Could not setup scaling context for thumbnail: {}", destFile);
654 CleanupLocalOutputBuffer();
655 return false;
658 // Setup jpeg range for sws
659 if (jpg_output)
661 int* inv_table = nullptr;
662 int* table = nullptr;
663 int srcRange, dstRange, brightness, contrast, saturation;
665 if (sws_getColorspaceDetails(tdm.sws, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation) < 0)
667 CLog::Log(LOGERROR, "SWS_SCALE failed to get ColorSpaceDetails for thumbnail: {}", destFile);
668 CleanupLocalOutputBuffer();
669 return false;
671 dstRange = 1; // jpeg full range yuv420p output
672 srcRange = 0; // full range RGB32 input
673 if (sws_setColorspaceDetails(tdm.sws, inv_table, srcRange, table, dstRange, brightness, contrast, saturation) < 0)
675 CLog::Log(LOGERROR, "SWS_SCALE failed to set ColorSpace Details for thumbnail: {}", destFile);
676 CleanupLocalOutputBuffer();
677 return false;
681 if (sws_scale(tdm.sws, src, srcStride, 0, height, tdm.frame_temporary->data, tdm.frame_temporary->linesize) < 0)
683 CLog::Log(LOGERROR, "SWS_SCALE failed for thumbnail: {}", destFile);
684 CleanupLocalOutputBuffer();
685 return false;
687 tdm.frame_input->pts = 1;
688 tdm.frame_input->quality = tdm.avOutctx->global_quality;
689 tdm.frame_input->data[0] = tdm.frame_temporary->data[0];
690 tdm.frame_input->data[1] = tdm.frame_temporary->data[1];
691 tdm.frame_input->data[2] = tdm.frame_temporary->data[2];
692 tdm.frame_input->height = height;
693 tdm.frame_input->width = width;
694 tdm.frame_input->linesize[0] = tdm.frame_temporary->linesize[0];
695 tdm.frame_input->linesize[1] = tdm.frame_temporary->linesize[1];
696 tdm.frame_input->linesize[2] = tdm.frame_temporary->linesize[2];
697 // this is deprecated but mjpeg is not yet transitioned
698 tdm.frame_input->format = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA;
700 int got_package = 0;
701 AVPacket* avpkt;
702 avpkt = av_packet_alloc();
704 int ret = EncodeFFmpegFrame(tdm.avOutctx, avpkt, &got_package, tdm.frame_input);
706 if ((ret < 0) || (got_package == 0))
708 CLog::Log(LOGERROR, "Could not encode thumbnail: {}", destFile);
709 CleanupLocalOutputBuffer();
710 av_packet_free(&avpkt);
711 return false;
714 bufferoutSize = avpkt->size;
715 m_outputBuffer = (uint8_t*) av_malloc(bufferoutSize);
716 if (!m_outputBuffer)
718 CLog::Log(LOGERROR, "Could not generate allocate memory for thumbnail: {}", destFile);
719 CleanupLocalOutputBuffer();
720 av_packet_free(&avpkt);
721 return false;
723 // update buffer ptr for caller
724 bufferout = m_outputBuffer;
726 // copy avpkt data into outputbuffer
727 memcpy(m_outputBuffer, avpkt->data, avpkt->size);
728 av_packet_free(&avpkt);
730 return true;
733 void CFFmpegImage::ReleaseThumbnailBuffer()
735 CleanupLocalOutputBuffer();
738 void CFFmpegImage::CleanupLocalOutputBuffer()
740 av_free(m_outputBuffer);
741 m_outputBuffer = nullptr;
744 std::shared_ptr<Frame> CFFmpegImage::ReadFrame()
746 AVFrame* avframe = ExtractFrame();
747 if (avframe == nullptr)
748 return nullptr;
749 std::shared_ptr<Frame> frame(new Frame());
751 #if LIBAVCODEC_VERSION_MAJOR < 60
752 frame->m_delay = (unsigned int)avframe->pkt_duration;
753 #else
754 frame->m_delay = (unsigned int)avframe->duration;
755 #endif
757 frame->m_pitch = avframe->width * 4;
758 frame->m_pImage = (unsigned char*) av_malloc(avframe->height * frame->m_pitch);
759 DecodeFrame(avframe, avframe->width, avframe->height, frame->m_pitch, frame->m_pImage);
760 av_frame_free(&avframe);
761 return frame;