Merge pull request #26386 from ksooo/guiinfo-fix-listitem-filenamenoextension
[xbmc.git] / xbmc / cores / FFmpeg.cpp
blob1bf2c331cade58668270e66fc57ed466f6b51bc3
1 /*
2 * Copyright (C) 2005-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 */
9 #include "cores/FFmpeg.h"
11 #include "ServiceBroker.h"
12 #include "settings/AdvancedSettings.h"
13 #include "settings/SettingsComponent.h"
14 #include "threads/CriticalSection.h"
15 #include "threads/Thread.h"
16 #include "utils/StringUtils.h"
17 #include "utils/log.h"
19 extern "C"
21 #include <libavcodec/bsf.h>
24 #include <map>
25 #include <mutex>
27 static thread_local CFFmpegLog* CFFmpegLogTls;
29 namespace FFMPEG_HELP_TOOLS
32 std::string FFMpegErrorToString(int err)
34 std::string text;
35 text.resize(AV_ERROR_MAX_STRING_SIZE);
36 av_strerror(err, text.data(), AV_ERROR_MAX_STRING_SIZE);
37 return text;
40 } // namespace FFMPEG_HELP_TOOLS
42 void CFFmpegLog::SetLogLevel(int level)
44 CFFmpegLog::ClearLogLevel();
45 CFFmpegLog *log = new CFFmpegLog();
46 log->level = level;
47 CFFmpegLogTls = log;
50 int CFFmpegLog::GetLogLevel()
52 CFFmpegLog* log = CFFmpegLogTls;
53 if (!log)
54 return -1;
55 return log->level;
58 void CFFmpegLog::ClearLogLevel()
60 CFFmpegLog* log = CFFmpegLogTls;
61 CFFmpegLogTls = nullptr;
62 if (log)
63 delete log;
66 static CCriticalSection m_logSection;
67 std::map<const CThread*, std::string> g_logbuffer;
69 void ff_flush_avutil_log_buffers(void)
71 std::unique_lock<CCriticalSection> lock(m_logSection);
72 /* Loop through the logbuffer list and remove any blank buffers
73 If the thread using the buffer is still active, it will just
74 add a new buffer next time it writes to the log */
75 std::map<const CThread*, std::string>::iterator it;
76 for (it = g_logbuffer.begin(); it != g_logbuffer.end(); )
77 if ((*it).second.empty())
78 g_logbuffer.erase(it++);
79 else
80 ++it;
83 void ff_avutil_log(void* ptr, int level, const char* format, va_list va)
85 std::unique_lock<CCriticalSection> lock(m_logSection);
86 const CThread* threadId = CThread::GetCurrentThread();
87 std::string &buffer = g_logbuffer[threadId];
89 AVClass* avc= ptr ? *(AVClass**)ptr : NULL;
91 int maxLevel = AV_LOG_WARNING;
92 if (CFFmpegLog::GetLogLevel() > 0)
93 maxLevel = AV_LOG_INFO;
95 if (level > maxLevel && !CServiceBroker::GetLogging().CanLogComponent(LOGFFMPEG))
96 return;
97 else if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel <= LOG_LEVEL_NORMAL)
98 return;
100 int type;
101 switch (level)
103 case AV_LOG_INFO:
104 type = LOGINFO;
105 break;
107 case AV_LOG_ERROR:
108 type = LOGERROR;
109 break;
111 case AV_LOG_DEBUG:
112 default:
113 type = LOGDEBUG;
114 break;
117 std::string message = StringUtils::FormatV(format, va);
118 std::string prefix = StringUtils::Format("ffmpeg[{}]: ", fmt::ptr(threadId));
119 if (avc)
121 if (avc->item_name)
122 prefix += std::string("[") + avc->item_name(ptr) + "] ";
123 else if (avc->class_name)
124 prefix += std::string("[") + avc->class_name + "] ";
127 buffer += message;
128 int pos, start = 0;
129 while ((pos = buffer.find_first_of('\n', start)) >= 0)
131 if (pos > start)
132 CLog::Log(type, "{}{}", prefix, buffer.substr(start, pos - start));
133 start = pos+1;
135 buffer.erase(0, start);
138 FFmpegExtraData::FFmpegExtraData(size_t size)
139 : m_data(reinterpret_cast<uint8_t*>(av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE))),
140 m_size(size)
142 // using av_mallocz because some APIs require at least the padding to be zeroed, e.g. AVCodecParameters
143 if (!m_data)
144 throw std::bad_alloc();
147 FFmpegExtraData::FFmpegExtraData(const uint8_t* data, size_t size) : FFmpegExtraData(size)
149 if (size > 0)
150 std::memcpy(m_data, data, size);
153 FFmpegExtraData::~FFmpegExtraData()
155 av_free(m_data);
158 FFmpegExtraData::FFmpegExtraData(const FFmpegExtraData& e) : FFmpegExtraData(e.m_size)
160 if (m_size > 0)
161 std::memcpy(m_data, e.m_data, m_size);
164 FFmpegExtraData::FFmpegExtraData(FFmpegExtraData&& other) noexcept : FFmpegExtraData()
166 std::swap(m_data, other.m_data);
167 std::swap(m_size, other.m_size);
170 FFmpegExtraData& FFmpegExtraData::operator=(const FFmpegExtraData& other)
172 if (this != &other)
174 if (m_size >= other.m_size && other.m_size > 0) // reuse current buffer if large enough
176 std::memcpy(m_data, other.m_data, other.m_size);
177 m_size = other.m_size;
179 else
181 FFmpegExtraData extraData(other);
182 *this = std::move(extraData);
185 return *this;
188 FFmpegExtraData& FFmpegExtraData::operator=(FFmpegExtraData&& other) noexcept
190 if (this != &other)
192 std::swap(m_data, other.m_data);
193 std::swap(m_size, other.m_size);
195 return *this;
198 bool FFmpegExtraData::operator==(const FFmpegExtraData& other) const
200 return m_size == other.m_size && (m_size == 0 || std::memcmp(m_data, other.m_data, m_size) == 0);
203 bool FFmpegExtraData::operator!=(const FFmpegExtraData& other) const
205 return !(*this == other);
208 uint8_t* FFmpegExtraData::TakeData()
210 auto tmp = m_data;
211 m_data = nullptr;
212 m_size = 0;
213 return tmp;
216 FFmpegExtraData GetPacketExtradata(const AVPacket* pkt, const AVCodecParameters* codecPar)
218 constexpr int FF_MAX_EXTRADATA_SIZE = ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE);
220 if (!pkt)
221 return {};
223 /* extract_extradata bitstream filter is implemented only
224 * for certain codecs, as noted in discussion of PR#21248
227 AVCodecID codecId = codecPar->codec_id;
229 // clang-format off
230 if (
231 codecId != AV_CODEC_ID_MPEG1VIDEO &&
232 codecId != AV_CODEC_ID_MPEG2VIDEO &&
233 codecId != AV_CODEC_ID_H264 &&
234 codecId != AV_CODEC_ID_HEVC &&
235 codecId != AV_CODEC_ID_MPEG4 &&
236 codecId != AV_CODEC_ID_VC1 &&
237 codecId != AV_CODEC_ID_AV1 &&
238 codecId != AV_CODEC_ID_AVS2 &&
239 codecId != AV_CODEC_ID_AVS3 &&
240 codecId != AV_CODEC_ID_CAVS
242 // clang-format on
243 return {};
245 const AVBitStreamFilter* f = av_bsf_get_by_name("extract_extradata");
246 if (!f)
247 return {};
249 AVBSFContext* bsf = nullptr;
250 int ret = av_bsf_alloc(f, &bsf);
251 if (ret < 0)
252 return {};
254 ret = avcodec_parameters_copy(bsf->par_in, codecPar);
255 if (ret < 0)
257 av_bsf_free(&bsf);
258 return {};
261 ret = av_bsf_init(bsf);
262 if (ret < 0)
264 av_bsf_free(&bsf);
265 return {};
268 AVPacket* dstPkt = av_packet_alloc();
269 if (!dstPkt)
271 CLog::LogF(LOGERROR, "failed to allocate packet");
273 av_bsf_free(&bsf);
274 return {};
276 AVPacket* pktRef = dstPkt;
278 ret = av_packet_ref(pktRef, pkt);
279 if (ret < 0)
281 av_bsf_free(&bsf);
282 av_packet_free(&dstPkt);
283 return {};
286 ret = av_bsf_send_packet(bsf, pktRef);
287 if (ret < 0)
289 av_packet_unref(pktRef);
290 av_bsf_free(&bsf);
291 av_packet_free(&dstPkt);
292 return {};
295 FFmpegExtraData extraData;
296 ret = 0;
297 while (ret >= 0)
299 ret = av_bsf_receive_packet(bsf, pktRef);
300 if (ret < 0)
302 if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
303 break;
305 continue;
308 size_t retExtraDataSize = 0;
309 uint8_t* retExtraData =
310 av_packet_get_side_data(pktRef, AV_PKT_DATA_NEW_EXTRADATA, &retExtraDataSize);
311 if (retExtraData && retExtraDataSize > 0 && retExtraDataSize < FF_MAX_EXTRADATA_SIZE)
315 extraData = FFmpegExtraData(retExtraData, retExtraDataSize);
317 catch (const std::bad_alloc&)
319 CLog::LogF(LOGERROR, "failed to allocate {} bytes for extradata", retExtraDataSize);
321 av_packet_unref(pktRef);
322 av_bsf_free(&bsf);
323 av_packet_free(&dstPkt);
324 return {};
327 CLog::LogF(LOGDEBUG, "fetching extradata, extradata_size({})", retExtraDataSize);
329 av_packet_unref(pktRef);
330 break;
333 av_packet_unref(pktRef);
336 av_bsf_free(&bsf);
337 av_packet_free(&dstPkt);
339 return extraData;