[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / FFmpegImage.cpp
blob33e00a1d7a680190b6b15a2135c009e38b024d89
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 // assumption quadratic maximums e.g. 2048x2048
491 float ratio = m_width / (float)m_height;
492 unsigned int nHeight = m_originalHeight;
493 unsigned int nWidth = m_originalWidth;
494 if (nHeight > height)
496 nHeight = height;
497 nWidth = (unsigned int)(nHeight * ratio + 0.5f);
499 if (nWidth > width)
501 nWidth = width;
502 nHeight = (unsigned int)(nWidth / ratio + 0.5f);
505 struct SwsContext* context = sws_getContext(m_originalWidth, m_originalHeight, pixFormat,
506 nWidth, nHeight, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
508 if (range == AVCOL_RANGE_JPEG)
510 int* inv_table = nullptr;
511 int* table = nullptr;
512 int srcRange, dstRange, brightness, contrast, saturation;
513 sws_getColorspaceDetails(context, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation);
514 srcRange = 1;
515 sws_setColorspaceDetails(context, inv_table, srcRange, table, dstRange, brightness, contrast, saturation);
518 sws_scale(context, frame->data, frame->linesize, 0, m_originalHeight,
519 pictureRGB->data, pictureRGB->linesize);
520 sws_freeContext(context);
522 if (needsCopy)
524 int minPitch = std::min((int)pitch, pictureRGB->linesize[0]);
525 if (minPitch < 0)
527 CLog::LogF(LOGERROR, "negative pitch or height");
528 av_frame_free(&pictureRGB);
529 return false;
531 const unsigned char *src = pictureRGB->data[0];
532 unsigned char* dst = pixels;
534 for (unsigned int y = 0; y < nHeight; y++)
536 memcpy(dst, src, minPitch);
537 src += pictureRGB->linesize[0];
538 dst += pitch;
540 av_frame_free(&pictureRGB);
542 else
544 // we only lended the data so don't get it deleted
545 pictureRGB->data[0] = nullptr;
546 av_frame_free(&pictureRGB);
549 // update width and height original dimensions are kept
550 m_height = nHeight;
551 m_width = nWidth;
553 return true;
556 bool CFFmpegImage::CreateThumbnailFromSurface(unsigned char* bufferin, unsigned int width,
557 unsigned int height, unsigned int format,
558 unsigned int pitch,
559 const std::string& destFile,
560 unsigned char* &bufferout,
561 unsigned int &bufferoutSize)
563 // It seems XB_FMT_A8R8G8B8 mean RGBA and not ARGB
564 if (format != XB_FMT_A8R8G8B8)
566 CLog::Log(LOGERROR, "Supplied format: {} is not supported.", format);
567 return false;
570 bool jpg_output = false;
571 if (m_strMimeType == "image/jpeg" || m_strMimeType == "image/jpg")
572 jpg_output = true;
573 else if (m_strMimeType == "image/png")
574 jpg_output = false;
575 else
577 CLog::Log(LOGERROR, "Output Format is not supported: {} is not supported.", destFile);
578 return false;
581 ThumbDataManagement tdm;
583 tdm.codec = avcodec_find_encoder(jpg_output ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG);
584 if (!tdm.codec)
586 CLog::Log(LOGERROR, "You are missing a working encoder for format: {}",
587 jpg_output ? "JPEG" : "PNG");
588 return false;
591 tdm.avOutctx = avcodec_alloc_context3(tdm.codec);
592 if (!tdm.avOutctx)
594 CLog::Log(LOGERROR, "Could not allocate context for thumbnail: {}", destFile);
595 return false;
598 tdm.avOutctx->height = height;
599 tdm.avOutctx->width = width;
600 tdm.avOutctx->time_base.num = 1;
601 tdm.avOutctx->time_base.den = 1;
602 tdm.avOutctx->pix_fmt = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA;
603 tdm.avOutctx->flags = AV_CODEC_FLAG_QSCALE;
604 tdm.avOutctx->mb_lmin = tdm.avOutctx->qmin * FF_QP2LAMBDA;
605 tdm.avOutctx->mb_lmax = tdm.avOutctx->qmax * FF_QP2LAMBDA;
606 unsigned int quality =
607 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageQualityJpeg;
608 tdm.avOutctx->global_quality =
609 jpg_output ? quality * FF_QP2LAMBDA : tdm.avOutctx->qmin * FF_QP2LAMBDA;
611 unsigned int internalBufOutSize = 0;
613 int size = av_image_get_buffer_size(tdm.avOutctx->pix_fmt, tdm.avOutctx->width, tdm.avOutctx->height, 16);
614 if (size < 0)
616 CLog::Log(LOGERROR, "Could not compute picture size for thumbnail: {}", destFile);
617 CleanupLocalOutputBuffer();
618 return false;
620 internalBufOutSize = (unsigned int) size;
622 tdm.intermediateBuffer = (uint8_t*) av_malloc(internalBufOutSize);
623 if (!tdm.intermediateBuffer)
625 CLog::Log(LOGERROR, "Could not allocate memory for thumbnail: {}", destFile);
626 CleanupLocalOutputBuffer();
627 return false;
630 if (avcodec_open2(tdm.avOutctx, tdm.codec, NULL) < 0)
632 CLog::Log(LOGERROR, "Could not open avcodec context thumbnail: {}", destFile);
633 CleanupLocalOutputBuffer();
634 return false;
637 tdm.frame_input = av_frame_alloc();
638 if (!tdm.frame_input)
640 CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: {}", destFile);
641 CleanupLocalOutputBuffer();
642 return false;
645 // convert the RGB32 frame to AV_PIX_FMT_YUV420P - we use this later on as AV_PIX_FMT_YUVJ420P
646 tdm.frame_temporary = av_frame_alloc();
647 if (!tdm.frame_temporary)
649 CLog::Log(LOGERROR, "Could not allocate frame for thumbnail: {}", destFile);
650 CleanupLocalOutputBuffer();
651 return false;
654 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)
656 CLog::Log(LOGERROR, "Could not fill picture for thumbnail: {}", destFile);
657 CleanupLocalOutputBuffer();
658 return false;
661 uint8_t* src[] = { bufferin, NULL, NULL, NULL };
662 int srcStride[] = { (int) pitch, 0, 0, 0};
664 //input size == output size which means only pix_fmt conversion
665 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);
666 if (!tdm.sws)
668 CLog::Log(LOGERROR, "Could not setup scaling context for thumbnail: {}", destFile);
669 CleanupLocalOutputBuffer();
670 return false;
673 // Setup jpeg range for sws
674 if (jpg_output)
676 int* inv_table = nullptr;
677 int* table = nullptr;
678 int srcRange, dstRange, brightness, contrast, saturation;
680 if (sws_getColorspaceDetails(tdm.sws, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation) < 0)
682 CLog::Log(LOGERROR, "SWS_SCALE failed to get ColorSpaceDetails for thumbnail: {}", destFile);
683 CleanupLocalOutputBuffer();
684 return false;
686 dstRange = 1; // jpeg full range yuv420p output
687 srcRange = 0; // full range RGB32 input
688 if (sws_setColorspaceDetails(tdm.sws, inv_table, srcRange, table, dstRange, brightness, contrast, saturation) < 0)
690 CLog::Log(LOGERROR, "SWS_SCALE failed to set ColorSpace Details for thumbnail: {}", destFile);
691 CleanupLocalOutputBuffer();
692 return false;
696 if (sws_scale(tdm.sws, src, srcStride, 0, height, tdm.frame_temporary->data, tdm.frame_temporary->linesize) < 0)
698 CLog::Log(LOGERROR, "SWS_SCALE failed for thumbnail: {}", destFile);
699 CleanupLocalOutputBuffer();
700 return false;
702 tdm.frame_input->pts = 1;
703 tdm.frame_input->quality = tdm.avOutctx->global_quality;
704 tdm.frame_input->data[0] = tdm.frame_temporary->data[0];
705 tdm.frame_input->data[1] = tdm.frame_temporary->data[1];
706 tdm.frame_input->data[2] = tdm.frame_temporary->data[2];
707 tdm.frame_input->height = height;
708 tdm.frame_input->width = width;
709 tdm.frame_input->linesize[0] = tdm.frame_temporary->linesize[0];
710 tdm.frame_input->linesize[1] = tdm.frame_temporary->linesize[1];
711 tdm.frame_input->linesize[2] = tdm.frame_temporary->linesize[2];
712 // this is deprecated but mjpeg is not yet transitioned
713 tdm.frame_input->format = jpg_output ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_RGBA;
715 int got_package = 0;
716 AVPacket* avpkt;
717 avpkt = av_packet_alloc();
719 int ret = EncodeFFmpegFrame(tdm.avOutctx, avpkt, &got_package, tdm.frame_input);
721 if ((ret < 0) || (got_package == 0))
723 CLog::Log(LOGERROR, "Could not encode thumbnail: {}", destFile);
724 CleanupLocalOutputBuffer();
725 av_packet_free(&avpkt);
726 return false;
729 bufferoutSize = avpkt->size;
730 m_outputBuffer = (uint8_t*) av_malloc(bufferoutSize);
731 if (!m_outputBuffer)
733 CLog::Log(LOGERROR, "Could not generate allocate memory for thumbnail: {}", destFile);
734 CleanupLocalOutputBuffer();
735 av_packet_free(&avpkt);
736 return false;
738 // update buffer ptr for caller
739 bufferout = m_outputBuffer;
741 // copy avpkt data into outputbuffer
742 memcpy(m_outputBuffer, avpkt->data, avpkt->size);
743 av_packet_free(&avpkt);
745 return true;
748 void CFFmpegImage::ReleaseThumbnailBuffer()
750 CleanupLocalOutputBuffer();
753 void CFFmpegImage::CleanupLocalOutputBuffer()
755 av_free(m_outputBuffer);
756 m_outputBuffer = nullptr;
759 std::shared_ptr<Frame> CFFmpegImage::ReadFrame()
761 AVFrame* avframe = ExtractFrame();
762 if (avframe == nullptr)
763 return nullptr;
764 std::shared_ptr<Frame> frame(new Frame());
766 #if LIBAVCODEC_VERSION_MAJOR < 60
767 frame->m_delay = (unsigned int)avframe->pkt_duration;
768 #else
769 frame->m_delay = (unsigned int)avframe->duration;
770 #endif
772 frame->m_pitch = avframe->width * 4;
773 frame->m_pImage = (unsigned char*) av_malloc(avframe->height * frame->m_pitch);
774 DecodeFrame(avframe, avframe->width, avframe->height, frame->m_pitch, frame->m_pImage);
775 av_frame_free(&avframe);
776 return frame;