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.
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"
21 #include <libavcodec/bsf.h>
27 static thread_local CFFmpegLog
* CFFmpegLogTls
;
29 namespace FFMPEG_HELP_TOOLS
32 std::string
FFMpegErrorToString(int err
)
35 text
.resize(AV_ERROR_MAX_STRING_SIZE
);
36 av_strerror(err
, text
.data(), AV_ERROR_MAX_STRING_SIZE
);
40 } // namespace FFMPEG_HELP_TOOLS
42 void CFFmpegLog::SetLogLevel(int level
)
44 CFFmpegLog::ClearLogLevel();
45 CFFmpegLog
*log
= new CFFmpegLog();
50 int CFFmpegLog::GetLogLevel()
52 CFFmpegLog
* log
= CFFmpegLogTls
;
58 void CFFmpegLog::ClearLogLevel()
60 CFFmpegLog
* log
= CFFmpegLogTls
;
61 CFFmpegLogTls
= nullptr;
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
++);
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
))
97 else if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel
<= LOG_LEVEL_NORMAL
)
117 std::string message
= StringUtils::FormatV(format
, va
);
118 std::string prefix
= StringUtils::Format("ffmpeg[{}]: ", fmt::ptr(threadId
));
122 prefix
+= std::string("[") + avc
->item_name(ptr
) + "] ";
123 else if (avc
->class_name
)
124 prefix
+= std::string("[") + avc
->class_name
+ "] ";
129 while ((pos
= buffer
.find_first_of('\n', start
)) >= 0)
132 CLog::Log(type
, "{}{}", prefix
, buffer
.substr(start
, pos
- start
));
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
))),
142 // using av_mallocz because some APIs require at least the padding to be zeroed, e.g. AVCodecParameters
144 throw std::bad_alloc();
147 FFmpegExtraData::FFmpegExtraData(const uint8_t* data
, size_t size
) : FFmpegExtraData(size
)
150 std::memcpy(m_data
, data
, size
);
153 FFmpegExtraData::~FFmpegExtraData()
158 FFmpegExtraData::FFmpegExtraData(const FFmpegExtraData
& e
) : FFmpegExtraData(e
.m_size
)
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
)
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
;
181 FFmpegExtraData
extraData(other
);
182 *this = std::move(extraData
);
188 FFmpegExtraData
& FFmpegExtraData::operator=(FFmpegExtraData
&& other
) noexcept
192 std::swap(m_data
, other
.m_data
);
193 std::swap(m_size
, other
.m_size
);
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()
216 FFmpegExtraData
GetPacketExtradata(const AVPacket
* pkt
, const AVCodecParameters
* codecPar
)
218 constexpr int FF_MAX_EXTRADATA_SIZE
= ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE
);
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
;
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
245 const AVBitStreamFilter
* f
= av_bsf_get_by_name("extract_extradata");
249 AVBSFContext
* bsf
= nullptr;
250 int ret
= av_bsf_alloc(f
, &bsf
);
254 ret
= avcodec_parameters_copy(bsf
->par_in
, codecPar
);
261 ret
= av_bsf_init(bsf
);
268 AVPacket
* dstPkt
= av_packet_alloc();
271 CLog::LogF(LOGERROR
, "failed to allocate packet");
276 AVPacket
* pktRef
= dstPkt
;
278 ret
= av_packet_ref(pktRef
, pkt
);
282 av_packet_free(&dstPkt
);
286 ret
= av_bsf_send_packet(bsf
, pktRef
);
289 av_packet_unref(pktRef
);
291 av_packet_free(&dstPkt
);
295 FFmpegExtraData extraData
;
299 ret
= av_bsf_receive_packet(bsf
, pktRef
);
302 if (ret
!= AVERROR(EAGAIN
) && ret
!= AVERROR_EOF
)
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
);
323 av_packet_free(&dstPkt
);
327 CLog::LogF(LOGDEBUG
, "fetching extradata, extradata_size({})", retExtraDataSize
);
329 av_packet_unref(pktRef
);
333 av_packet_unref(pktRef
);
337 av_packet_free(&dstPkt
);