Lines 11-16
Link Here
|
11 |
#include "ServiceBroker.h" |
11 |
#include "ServiceBroker.h" |
12 |
#include "addons/Addon.h" |
12 |
#include "addons/Addon.h" |
13 |
#include "addons/AddonManager.h" |
13 |
#include "addons/AddonManager.h" |
|
|
14 |
#include "cores/FFmpeg.h" |
14 |
#include "settings/Settings.h" |
15 |
#include "settings/Settings.h" |
15 |
#include "settings/SettingsComponent.h" |
16 |
#include "settings/SettingsComponent.h" |
16 |
#include "utils/StringUtils.h" |
17 |
#include "utils/StringUtils.h" |
Lines 19-42
Link Here
|
19 |
#include "utils/log.h" |
20 |
#include "utils/log.h" |
20 |
|
21 |
|
21 |
using namespace KODI::CDRIP; |
22 |
using namespace KODI::CDRIP; |
|
|
23 |
using FFMPEG_HELP_TOOLS::FFMpegErrorToString; |
24 |
using FFMPEG_HELP_TOOLS::FFMpegException; |
22 |
|
25 |
|
23 |
namespace |
|
|
24 |
{ |
25 |
|
26 |
struct EncoderException : public std::exception |
27 |
{ |
28 |
std::string s; |
29 |
template<typename... Args> |
30 |
EncoderException(const std::string& fmt, Args&&... args) |
31 |
: s(StringUtils::Format(fmt, std::forward<Args>(args)...)) |
32 |
{ |
33 |
} |
34 |
~EncoderException() throw() {} // Updated |
35 |
const char* what() const throw() { return s.c_str(); } |
36 |
}; |
37 |
|
38 |
} /* namespace */ |
39 |
|
40 |
bool CEncoderFFmpeg::Init() |
26 |
bool CEncoderFFmpeg::Init() |
41 |
{ |
27 |
{ |
42 |
try |
28 |
try |
Lines 54-60
Link Here
|
54 |
} |
40 |
} |
55 |
else |
41 |
else |
56 |
{ |
42 |
{ |
57 |
throw EncoderException("Could not get add-on: {}", addonId); |
43 |
throw FFMpegException("Could not get add-on: {}", addonId); |
58 |
} |
44 |
} |
59 |
|
45 |
|
60 |
// Hack fix about PTS on generated files. |
46 |
// Hack fix about PTS on generated files. |
Lines 66-115
Link Here
|
66 |
else if (addonId == "audioencoder.kodi.builtin.wma") |
52 |
else if (addonId == "audioencoder.kodi.builtin.wma") |
67 |
m_samplesCountMultiply = 1000; |
53 |
m_samplesCountMultiply = 1000; |
68 |
else |
54 |
else |
69 |
throw EncoderException("Internal add-on id \"{}\" not known as usable", addonId); |
55 |
throw FFMpegException("Internal add-on id \"{}\" not known as usable", addonId); |
70 |
|
56 |
|
71 |
const std::string filename = URIUtils::GetFileName(m_strFile); |
57 |
const std::string filename = URIUtils::GetFileName(m_strFile); |
72 |
|
58 |
|
73 |
m_formatCtx = avformat_alloc_context(); |
59 |
m_formatCtx = avformat_alloc_context(); |
74 |
if (!m_formatCtx) |
60 |
if (!m_formatCtx) |
75 |
throw EncoderException("Could not allocate output format context"); |
61 |
throw FFMpegException("Could not allocate output format context"); |
76 |
|
62 |
|
77 |
m_bcBuffer = static_cast<uint8_t*>(av_malloc(BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE)); |
63 |
m_bcBuffer = static_cast<uint8_t*>(av_malloc(BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE)); |
78 |
if (!m_bcBuffer) |
64 |
if (!m_bcBuffer) |
79 |
throw EncoderException("Could not allocate buffer"); |
65 |
throw FFMpegException("Could not allocate buffer"); |
80 |
|
66 |
|
81 |
m_formatCtx->pb = avio_alloc_context(m_bcBuffer, BUFFER_SIZE, AVIO_FLAG_WRITE, this, nullptr, |
67 |
m_formatCtx->pb = avio_alloc_context(m_bcBuffer, BUFFER_SIZE, AVIO_FLAG_WRITE, this, nullptr, |
82 |
avio_write_callback, avio_seek_callback); |
68 |
avio_write_callback, avio_seek_callback); |
83 |
if (!m_formatCtx->pb) |
69 |
if (!m_formatCtx->pb) |
84 |
throw EncoderException("Failed to allocate ByteIOContext"); |
70 |
throw FFMpegException("Failed to allocate ByteIOContext"); |
85 |
|
71 |
|
86 |
/* Guess the desired container format based on the file extension. */ |
72 |
/* Guess the desired container format based on the file extension. */ |
87 |
m_formatCtx->oformat = av_guess_format(nullptr, filename.c_str(), nullptr); |
73 |
m_formatCtx->oformat = av_guess_format(nullptr, filename.c_str(), nullptr); |
88 |
if (!m_formatCtx->oformat) |
74 |
if (!m_formatCtx->oformat) |
89 |
throw EncoderException("Could not find output file format"); |
75 |
throw FFMpegException("Could not find output file format"); |
90 |
|
76 |
|
91 |
m_formatCtx->url = av_strdup(filename.c_str()); |
77 |
m_formatCtx->url = av_strdup(filename.c_str()); |
92 |
if (!m_formatCtx->url) |
78 |
if (!m_formatCtx->url) |
93 |
throw EncoderException("Could not allocate url"); |
79 |
throw FFMpegException("Could not allocate url"); |
94 |
|
80 |
|
95 |
/* Find the encoder to be used by its name. */ |
81 |
/* Find the encoder to be used by its name. */ |
96 |
AVCodec* codec = avcodec_find_encoder(m_formatCtx->oformat->audio_codec); |
82 |
const AVCodec* codec = avcodec_find_encoder(m_formatCtx->oformat->audio_codec); |
97 |
if (!codec) |
83 |
if (!codec) |
98 |
throw EncoderException("Unable to find a suitable FFmpeg encoder"); |
84 |
throw FFMpegException("Unable to find a suitable FFmpeg encoder"); |
99 |
|
85 |
|
100 |
/* Create a new audio stream in the output file container. */ |
86 |
/* Create a new audio stream in the output file container. */ |
101 |
m_stream = avformat_new_stream(m_formatCtx, nullptr); |
87 |
m_stream = avformat_new_stream(m_formatCtx, nullptr); |
102 |
if (!m_stream) |
88 |
if (!m_stream) |
103 |
throw EncoderException("Failed to allocate AVStream context"); |
89 |
throw FFMpegException("Failed to allocate AVStream context"); |
104 |
|
90 |
|
105 |
m_codecCtx = avcodec_alloc_context3(codec); |
91 |
m_codecCtx = avcodec_alloc_context3(codec); |
106 |
if (!m_codecCtx) |
92 |
if (!m_codecCtx) |
107 |
throw EncoderException("Failed to allocate the encoder context"); |
93 |
throw FFMpegException("Failed to allocate the encoder context"); |
108 |
|
94 |
|
109 |
/* Set the basic encoder parameters. |
95 |
/* Set the basic encoder parameters. |
110 |
* The input file's sample rate is used to avoid a sample rate conversion. */ |
96 |
* The input file's sample rate is used to avoid a sample rate conversion. */ |
111 |
m_codecCtx->channels = m_iInChannels; |
97 |
av_channel_layout_uninit(&m_codecCtx->ch_layout); |
112 |
m_codecCtx->channel_layout = av_get_default_channel_layout(m_iInChannels); |
98 |
av_channel_layout_default(&m_codecCtx->ch_layout, m_iInChannels); |
113 |
m_codecCtx->sample_rate = m_iInSampleRate; |
99 |
m_codecCtx->sample_rate = m_iInSampleRate; |
114 |
m_codecCtx->sample_fmt = codec->sample_fmts[0]; |
100 |
m_codecCtx->sample_fmt = codec->sample_fmts[0]; |
115 |
m_codecCtx->bit_rate = bitrate; |
101 |
m_codecCtx->bit_rate = bitrate; |
Lines 128-141
Link Here
|
128 |
|
114 |
|
129 |
int err = avcodec_open2(m_codecCtx, codec, nullptr); |
115 |
int err = avcodec_open2(m_codecCtx, codec, nullptr); |
130 |
if (err < 0) |
116 |
if (err < 0) |
131 |
throw EncoderException("Failed to open the codec {} (error '{}')", |
117 |
throw FFMpegException("Failed to open the codec {} (error '{}')", |
132 |
codec->long_name ? codec->long_name : codec->name, |
118 |
codec->long_name ? codec->long_name : codec->name, |
133 |
FFmpegErrorToString(err)); |
119 |
FFMpegErrorToString(err)); |
134 |
|
120 |
|
135 |
err = avcodec_parameters_from_context(m_stream->codecpar, m_codecCtx); |
121 |
err = avcodec_parameters_from_context(m_stream->codecpar, m_codecCtx); |
136 |
if (err < 0) |
122 |
if (err < 0) |
137 |
throw EncoderException("Failed to copy encoder parameters to output stream (error '{}')", |
123 |
throw FFMpegException("Failed to copy encoder parameters to output stream (error '{}')", |
138 |
FFmpegErrorToString(err)); |
124 |
FFMpegErrorToString(err)); |
139 |
|
125 |
|
140 |
m_inFormat = GetInputFormat(m_iInBitsPerSample); |
126 |
m_inFormat = GetInputFormat(m_iInBitsPerSample); |
141 |
m_outFormat = m_codecCtx->sample_fmt; |
127 |
m_outFormat = m_codecCtx->sample_fmt; |
Lines 150-193
Link Here
|
150 |
|
136 |
|
151 |
m_bufferFrame = av_frame_alloc(); |
137 |
m_bufferFrame = av_frame_alloc(); |
152 |
if (!m_bufferFrame || !m_buffer) |
138 |
if (!m_bufferFrame || !m_buffer) |
153 |
throw EncoderException("Failed to allocate necessary buffers"); |
139 |
throw FFMpegException("Failed to allocate necessary buffers"); |
154 |
|
140 |
|
155 |
m_bufferFrame->nb_samples = m_codecCtx->frame_size; |
141 |
m_bufferFrame->nb_samples = m_codecCtx->frame_size; |
156 |
m_bufferFrame->format = m_inFormat; |
142 |
m_bufferFrame->format = m_inFormat; |
157 |
m_bufferFrame->channel_layout = m_codecCtx->channel_layout; |
143 |
|
|
|
144 |
av_channel_layout_uninit(&m_bufferFrame->ch_layout); |
145 |
av_channel_layout_copy(&m_bufferFrame->ch_layout, &m_codecCtx->ch_layout); |
146 |
|
158 |
m_bufferFrame->sample_rate = m_codecCtx->sample_rate; |
147 |
m_bufferFrame->sample_rate = m_codecCtx->sample_rate; |
159 |
|
148 |
|
160 |
err = av_frame_get_buffer(m_bufferFrame, 0); |
149 |
err = av_frame_get_buffer(m_bufferFrame, 0); |
161 |
if (err < 0) |
150 |
if (err < 0) |
162 |
throw EncoderException("Could not allocate output frame samples (error '{}')", |
151 |
throw FFMpegException("Could not allocate output frame samples (error '{}')", |
163 |
FFmpegErrorToString(err)); |
152 |
FFMpegErrorToString(err)); |
164 |
|
153 |
|
165 |
avcodec_fill_audio_frame(m_bufferFrame, m_iInChannels, m_inFormat, m_buffer, m_neededBytes, 0); |
154 |
avcodec_fill_audio_frame(m_bufferFrame, m_iInChannels, m_inFormat, m_buffer, m_neededBytes, 0); |
166 |
|
155 |
|
167 |
if (m_needConversion) |
156 |
if (m_needConversion) |
168 |
{ |
157 |
{ |
169 |
m_swrCtx = swr_alloc_set_opts(nullptr, m_codecCtx->channel_layout, m_outFormat, |
158 |
int ret = swr_alloc_set_opts2(&m_swrCtx, &m_codecCtx->ch_layout, m_outFormat, |
170 |
m_codecCtx->sample_rate, m_codecCtx->channel_layout, m_inFormat, |
159 |
m_codecCtx->sample_rate, &m_codecCtx->ch_layout, m_inFormat, |
171 |
m_codecCtx->sample_rate, 0, nullptr); |
160 |
m_codecCtx->sample_rate, 0, nullptr); |
172 |
if (!m_swrCtx || swr_init(m_swrCtx) < 0) |
161 |
if (ret || swr_init(m_swrCtx) < 0) |
173 |
throw EncoderException("Failed to initialize the resampler"); |
162 |
throw FFMpegException("Failed to initialize the resampler"); |
174 |
|
163 |
|
175 |
m_resampledBufferSize = |
164 |
m_resampledBufferSize = |
176 |
av_samples_get_buffer_size(nullptr, m_iInChannels, m_neededFrames, m_outFormat, 0); |
165 |
av_samples_get_buffer_size(nullptr, m_iInChannels, m_neededFrames, m_outFormat, 0); |
177 |
m_resampledBuffer = static_cast<uint8_t*>(av_malloc(m_resampledBufferSize)); |
166 |
m_resampledBuffer = static_cast<uint8_t*>(av_malloc(m_resampledBufferSize)); |
178 |
m_resampledFrame = av_frame_alloc(); |
167 |
m_resampledFrame = av_frame_alloc(); |
179 |
if (!m_resampledBuffer || !m_resampledFrame) |
168 |
if (!m_resampledBuffer || !m_resampledFrame) |
180 |
throw EncoderException("Failed to allocate a frame for resampling"); |
169 |
throw FFMpegException("Failed to allocate a frame for resampling"); |
181 |
|
170 |
|
182 |
m_resampledFrame->nb_samples = m_neededFrames; |
171 |
m_resampledFrame->nb_samples = m_neededFrames; |
183 |
m_resampledFrame->format = m_outFormat; |
172 |
m_resampledFrame->format = m_outFormat; |
184 |
m_resampledFrame->channel_layout = m_codecCtx->channel_layout; |
173 |
av_channel_layout_uninit(&m_resampledFrame->ch_layout); |
|
|
174 |
av_channel_layout_copy(&m_resampledFrame->ch_layout, &m_codecCtx->ch_layout); |
185 |
m_resampledFrame->sample_rate = m_codecCtx->sample_rate; |
175 |
m_resampledFrame->sample_rate = m_codecCtx->sample_rate; |
186 |
|
176 |
|
187 |
err = av_frame_get_buffer(m_resampledFrame, 0); |
177 |
err = av_frame_get_buffer(m_resampledFrame, 0); |
188 |
if (err < 0) |
178 |
if (err < 0) |
189 |
throw EncoderException("Could not allocate output resample frame samples (error '{}')", |
179 |
throw FFMpegException("Could not allocate output resample frame samples (error '{}')", |
190 |
FFmpegErrorToString(err)); |
180 |
FFMpegErrorToString(err)); |
191 |
|
181 |
|
192 |
avcodec_fill_audio_frame(m_resampledFrame, m_iInChannels, m_outFormat, m_resampledBuffer, |
182 |
avcodec_fill_audio_frame(m_resampledFrame, m_iInChannels, m_outFormat, m_resampledBuffer, |
193 |
m_resampledBufferSize, 0); |
183 |
m_resampledBufferSize, 0); |
Lines 204-210
Link Here
|
204 |
/* write the header */ |
194 |
/* write the header */ |
205 |
err = avformat_write_header(m_formatCtx, nullptr); |
195 |
err = avformat_write_header(m_formatCtx, nullptr); |
206 |
if (err != 0) |
196 |
if (err != 0) |
207 |
throw EncoderException("Failed to write the header (error '{}')", FFmpegErrorToString(err)); |
197 |
throw FFMpegException("Failed to write the header (error '{}')", FFMpegErrorToString(err)); |
208 |
|
198 |
|
209 |
CLog::Log(LOGDEBUG, "CEncoderFFmpeg::{} - Successfully initialized with muxer {} and codec {}", |
199 |
CLog::Log(LOGDEBUG, "CEncoderFFmpeg::{} - Successfully initialized with muxer {} and codec {}", |
210 |
__func__, |
200 |
__func__, |
Lines 212-227
Link Here
|
212 |
: m_formatCtx->oformat->name, |
202 |
: m_formatCtx->oformat->name, |
213 |
codec->long_name ? codec->long_name : codec->name); |
203 |
codec->long_name ? codec->long_name : codec->name); |
214 |
} |
204 |
} |
215 |
catch (EncoderException& caught) |
205 |
catch (const FFMpegException& caught) |
216 |
{ |
206 |
{ |
217 |
CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what()); |
207 |
CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what()); |
218 |
|
208 |
|
219 |
av_freep(&m_buffer); |
209 |
av_freep(&m_buffer); |
|
|
210 |
av_channel_layout_uninit(&m_bufferFrame->ch_layout); |
220 |
av_frame_free(&m_bufferFrame); |
211 |
av_frame_free(&m_bufferFrame); |
221 |
swr_free(&m_swrCtx); |
212 |
swr_free(&m_swrCtx); |
|
|
213 |
av_channel_layout_uninit(&m_resampledFrame->ch_layout); |
222 |
av_frame_free(&m_resampledFrame); |
214 |
av_frame_free(&m_resampledFrame); |
223 |
av_freep(&m_resampledBuffer); |
215 |
av_freep(&m_resampledBuffer); |
224 |
av_free(m_bcBuffer); |
216 |
av_free(m_bcBuffer); |
|
|
217 |
av_channel_layout_uninit(&m_codecCtx->ch_layout); |
225 |
avcodec_free_context(&m_codecCtx); |
218 |
avcodec_free_context(&m_codecCtx); |
226 |
if (m_formatCtx) |
219 |
if (m_formatCtx) |
227 |
{ |
220 |
{ |
Lines 299-305
Link Here
|
299 |
if (swr_convert(m_swrCtx, m_resampledFrame->data, m_neededFrames, |
292 |
if (swr_convert(m_swrCtx, m_resampledFrame->data, m_neededFrames, |
300 |
const_cast<const uint8_t**>(m_bufferFrame->extended_data), |
293 |
const_cast<const uint8_t**>(m_bufferFrame->extended_data), |
301 |
m_neededFrames) < 0) |
294 |
m_neededFrames) < 0) |
302 |
throw EncoderException("Error resampling audio"); |
295 |
throw FFMpegException("Error resampling audio"); |
303 |
|
296 |
|
304 |
frame = m_resampledFrame; |
297 |
frame = m_resampledFrame; |
305 |
} |
298 |
} |
Lines 316-323
Link Here
|
316 |
m_bufferSize = 0; |
309 |
m_bufferSize = 0; |
317 |
err = avcodec_send_frame(m_codecCtx, frame); |
310 |
err = avcodec_send_frame(m_codecCtx, frame); |
318 |
if (err < 0) |
311 |
if (err < 0) |
319 |
throw EncoderException("Error sending a frame for encoding (error '{}')", __func__, |
312 |
throw FFMpegException("Error sending a frame for encoding (error '{}')", |
320 |
FFmpegErrorToString(err)); |
313 |
FFMpegErrorToString(err)); |
321 |
|
314 |
|
322 |
while (err >= 0) |
315 |
while (err >= 0) |
323 |
{ |
316 |
{ |
Lines 329-347
Link Here
|
329 |
} |
322 |
} |
330 |
else if (err < 0) |
323 |
else if (err < 0) |
331 |
{ |
324 |
{ |
332 |
throw EncoderException("Error during encoding (error '{}')", __func__, |
325 |
throw FFMpegException("Error during encoding (error '{}')", FFMpegErrorToString(err)); |
333 |
FFmpegErrorToString(err)); |
|
|
334 |
} |
326 |
} |
335 |
|
327 |
|
336 |
err = av_write_frame(m_formatCtx, pkt); |
328 |
err = av_write_frame(m_formatCtx, pkt); |
337 |
if (err < 0) |
329 |
if (err < 0) |
338 |
throw EncoderException("Failed to write the frame data (error '{}')", __func__, |
330 |
throw FFMpegException("Failed to write the frame data (error '{}')", |
339 |
FFmpegErrorToString(err)); |
331 |
FFMpegErrorToString(err)); |
340 |
|
332 |
|
341 |
av_packet_unref(pkt); |
333 |
av_packet_unref(pkt); |
342 |
} |
334 |
} |
343 |
} |
335 |
} |
344 |
catch (EncoderException& caught) |
336 |
catch (const FFMpegException& caught) |
345 |
{ |
337 |
{ |
346 |
CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what()); |
338 |
CLog::Log(LOGERROR, "CEncoderFFmpeg::{} - {}", __func__, caught.what()); |
347 |
} |
339 |
} |
Lines 366-373
Link Here
|
366 |
|
358 |
|
367 |
/* Flush if needed */ |
359 |
/* Flush if needed */ |
368 |
av_freep(&m_buffer); |
360 |
av_freep(&m_buffer); |
|
|
361 |
av_channel_layout_uninit(&m_bufferFrame->ch_layout); |
369 |
av_frame_free(&m_bufferFrame); |
362 |
av_frame_free(&m_bufferFrame); |
370 |
swr_free(&m_swrCtx); |
363 |
swr_free(&m_swrCtx); |
|
|
364 |
av_channel_layout_uninit(&m_resampledFrame->ch_layout); |
371 |
av_frame_free(&m_resampledFrame); |
365 |
av_frame_free(&m_resampledFrame); |
372 |
av_freep(&m_resampledBuffer); |
366 |
av_freep(&m_resampledBuffer); |
373 |
m_needConversion = false; |
367 |
m_needConversion = false; |
Lines 379-384
Link Here
|
379 |
|
373 |
|
380 |
/* cleanup */ |
374 |
/* cleanup */ |
381 |
av_free(m_bcBuffer); |
375 |
av_free(m_bcBuffer); |
|
|
376 |
av_channel_layout_uninit(&m_codecCtx->ch_layout); |
382 |
avcodec_free_context(&m_codecCtx); |
377 |
avcodec_free_context(&m_codecCtx); |
383 |
av_freep(&m_formatCtx->pb); |
378 |
av_freep(&m_formatCtx->pb); |
384 |
avformat_free_context(m_formatCtx); |
379 |
avformat_free_context(m_formatCtx); |
Lines 400-413
Link Here
|
400 |
case 32: |
395 |
case 32: |
401 |
return AV_SAMPLE_FMT_S32; |
396 |
return AV_SAMPLE_FMT_S32; |
402 |
default: |
397 |
default: |
403 |
throw EncoderException("Invalid input bits per sample"); |
398 |
throw FFMpegException("Invalid input bits per sample"); |
404 |
} |
399 |
} |
405 |
} |
|
|
406 |
|
407 |
std::string CEncoderFFmpeg::FFmpegErrorToString(int err) |
408 |
{ |
409 |
std::string text; |
410 |
text.reserve(AV_ERROR_MAX_STRING_SIZE); |
411 |
av_strerror(err, text.data(), AV_ERROR_MAX_STRING_SIZE); |
412 |
return text; |
413 |
} |
400 |
} |