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.
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"
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
) :
31 m_imageSize(src
.m_imageSize
),
32 m_height(src
.m_height
),
37 m_pImage
= (unsigned char*) av_malloc(m_imageSize
);
38 memcpy(m_pImage
, src
.m_pImage
, m_imageSize
);
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
);
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
)
83 MemBuffer
* mbuf
= static_cast<MemBuffer
*>(h
);
84 int64_t unread
= mbuf
->size
- mbuf
->pos
;
88 size_t tocopy
= std::min((size_t)size
, (size_t)unread
);
89 memcpy(buf
, mbuf
->data
+ 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
)
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
);
112 CLog::LogF(LOGERROR
, "Unknown seek mode: {}", whence
);
117 CFFmpegImage::CFFmpegImage(const std::string
& strMimeType
) : m_strMimeType(strMimeType
)
121 m_outputBuffer
= nullptr;
124 CFFmpegImage::~CFFmpegImage()
126 av_frame_free(&m_pFrame
);
127 // someone could have forgotten to call us
128 CleanupLocalOutputBuffer();
131 avcodec_free_context(&m_codec_ctx
);
132 avformat_close_input(&m_fctx
);
137 m_buf
.data
= nullptr;
142 bool CFFmpegImage::LoadImageFromMemory(unsigned char* buffer
, unsigned int bufSize
,
143 unsigned int width
, unsigned int height
)
146 if (!Initialize(buffer
, bufSize
))
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
);
164 CLog::LogF(LOGERROR
, "Could not allocate buffer");
168 m_buf
.size
= bufSize
;
171 m_ioctx
= avio_alloc_context(fbuffer
, bufferSize
, 0, &m_buf
,
172 mem_file_read
, NULL
, mem_file_seek
);
177 CLog::LogF(LOGERROR
, "Could not allocate AVIOContext");
181 // signal to ffmepg this is not streaming protocol
182 m_ioctx
->max_packet_size
= bufferSize
;
184 m_fctx
= avformat_alloc_context();
188 CLog::LogF(LOGERROR
, "Could not allocate AVFormatContext");
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] == '*');
202 #if LIBAVCODEC_VERSION_MAJOR < 60
203 constexpr char jpegFormat
[] = "image2";
205 constexpr char jpegFormat
[] = "jpeg_pipe";
208 const AVInputFormat
* inp
= nullptr;
210 inp
= av_find_input_format(jpegFormat
);
211 else if (m_strMimeType
== "image/apng")
212 inp
= av_find_input_format("apng");
214 inp
= av_find_input_format("png_pipe");
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
);
241 if (m_fctx
->nb_streams
<= 0)
243 avformat_close_input(&m_fctx
);
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
);
252 avformat_close_input(&m_fctx
);
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
);
265 if (avcodec_open2(m_codec_ctx
, codec
, NULL
) < 0)
267 avformat_close_input(&m_fctx
);
268 avcodec_free_context(&m_codec_ctx
);
276 AVFrame
* CFFmpegImage::ExtractFrame()
278 if (!m_fctx
|| !m_fctx
->streams
[0])
280 CLog::LogF(LOGERROR
, "No valid format context or stream");
285 AVFrame
* frame
= av_frame_alloc();
286 int frame_decoded
= 0;
288 ret
= av_read_frame(m_fctx
, &pkt
);
291 CLog::Log(LOGDEBUG
, "Error [{}] while reading frame: {}", ret
, strerror(AVERROR(ret
)));
292 av_frame_free(&frame
);
293 av_packet_unref(&pkt
);
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
);
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});
312 av_rescale_q(frame
->duration
, m_fctx
->streams
[0]->time_base
, AVRational
{1, 1000});
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))
324 AVDictionary
* dic
= frame
->metadata
;
325 AVDictionaryEntry
* entry
= NULL
;
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
);
343 AVPixelFormat
CFFmpegImage::ConvertFormats(AVFrame
* frame
)
345 switch (frame
->format
) {
346 case AV_PIX_FMT_YUVJ420P
:
347 return AV_PIX_FMT_YUV420P
;
349 case AV_PIX_FMT_YUVJ422P
:
350 return AV_PIX_FMT_YUV422P
;
352 case AV_PIX_FMT_YUVJ444P
:
353 return AV_PIX_FMT_YUV444P
;
355 case AV_PIX_FMT_YUVJ440P
:
356 return AV_PIX_FMT_YUV440P
;
358 return static_cast<AVPixelFormat
>(frame
->format
);
363 void CFFmpegImage::FreeIOCtx(AVIOContext
** ioctx
)
365 av_freep(&((*ioctx
)->buffer
));
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
)
375 if (pixels
== nullptr)
377 CLog::Log(LOGERROR
, "{} - No valid buffer pointer (nullptr) passed", __FUNCTION__
);
381 if (!m_pFrame
|| !m_pFrame
->data
[0])
383 CLog::LogF(LOGERROR
, "AVFrame member not allocated");
387 return DecodeFrame(m_pFrame
, width
, height
, pitch
, pixels
);
390 int CFFmpegImage::EncodeFFmpegFrame(AVCodecContext
*avctx
, AVPacket
*pkt
, int *got_packet
, AVFrame
*frame
)
396 ret
= avcodec_send_frame(avctx
, frame
);
400 ret
= avcodec_receive_packet(avctx
, pkt
);
404 if (ret
== AVERROR(EAGAIN
))
410 int CFFmpegImage::DecodeFFmpegFrame(AVCodecContext
*avctx
, AVFrame
*frame
, int *got_frame
, AVPacket
*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.
425 ret
= avcodec_receive_frame(avctx
, frame
);
426 if (ret
< 0 && ret
!= AVERROR(EAGAIN
) && ret
!= AVERROR_EOF
)
428 if (ret
>= 0) // static code analysers would complain
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__
);
442 AVFrame
* pictureRGB
= av_frame_alloc();
445 CLog::LogF(LOGERROR
, "AVFrame could not be allocated");
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);
453 CLog::LogF(LOGERROR
, "Could not allocate AVFrame member with {} x {} pixels", width
, height
);
454 av_frame_free(&pictureRGB
);
458 bool needsCopy
= false;
459 int pixelsSize
= pitch
* height
;
460 bool aligned
= (((uintptr_t)(const void *)(pixels
)) % (32) == 0);
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
;
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
);
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
);
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
);
509 int minPitch
= std::min((int)pitch
, pictureRGB
->linesize
[0]);
512 CLog::LogF(LOGERROR
, "negative pitch or height");
513 av_frame_free(&pictureRGB
);
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];
525 av_frame_free(&pictureRGB
);
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
541 bool CFFmpegImage::CreateThumbnailFromSurface(unsigned char* bufferin
, unsigned int width
,
542 unsigned int height
, unsigned int format
,
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
);
555 bool jpg_output
= false;
556 if (m_strMimeType
== "image/jpeg" || m_strMimeType
== "image/jpg")
558 else if (m_strMimeType
== "image/png")
562 CLog::Log(LOGERROR
, "Output Format is not supported: {} is not supported.", destFile
);
566 ThumbDataManagement tdm
;
568 tdm
.codec
= avcodec_find_encoder(jpg_output
? AV_CODEC_ID_MJPEG
: AV_CODEC_ID_PNG
);
571 CLog::Log(LOGERROR
, "You are missing a working encoder for format: {}",
572 jpg_output
? "JPEG" : "PNG");
576 tdm
.avOutctx
= avcodec_alloc_context3(tdm
.codec
);
579 CLog::Log(LOGERROR
, "Could not allocate context for thumbnail: {}", destFile
);
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);
601 CLog::Log(LOGERROR
, "Could not compute picture size for thumbnail: {}", destFile
);
602 CleanupLocalOutputBuffer();
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();
615 if (avcodec_open2(tdm
.avOutctx
, tdm
.codec
, NULL
) < 0)
617 CLog::Log(LOGERROR
, "Could not open avcodec context thumbnail: {}", destFile
);
618 CleanupLocalOutputBuffer();
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();
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();
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();
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);
653 CLog::Log(LOGERROR
, "Could not setup scaling context for thumbnail: {}", destFile
);
654 CleanupLocalOutputBuffer();
658 // Setup jpeg range for sws
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();
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();
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();
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
;
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
);
714 bufferoutSize
= avpkt
->size
;
715 m_outputBuffer
= (uint8_t*) av_malloc(bufferoutSize
);
718 CLog::Log(LOGERROR
, "Could not generate allocate memory for thumbnail: {}", destFile
);
719 CleanupLocalOutputBuffer();
720 av_packet_free(&avpkt
);
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
);
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)
749 std::shared_ptr
<Frame
> frame(new Frame());
751 #if LIBAVCODEC_VERSION_MAJOR < 60
752 frame
->m_delay
= (unsigned int)avframe
->pkt_duration
;
754 frame
->m_delay
= (unsigned int)avframe
->duration
;
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
);