1 /*
2 Copyright (c) 2012-2020 Maarten Baert <maarten-baert@hotmail.com>
3
4 This file is part of SimpleScreenRecorder.
5
6 SimpleScreenRecorder is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SimpleScreenRecorder is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with SimpleScreenRecorder. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "Muxer.h"
21
22 #include "Logger.h"
23 #include "AVWrapper.h"
24 #include "BaseEncoder.h"
25 #include "VideoEncoder.h"
26 #include "AudioEncoder.h"
27
Muxer(const QString & container_name,const QString & output_file)28 Muxer::Muxer(const QString& container_name, const QString& output_file) {
29
30 m_container_name = container_name;
31 m_output_file = output_file;
32
33 m_format_context = NULL;
34 m_started = false;
35
36 // initialize stream data
37 for(int i = 0; i < MUXER_MAX_STREAMS; ++i) {
38 StreamLock lock(&m_stream_data[i]);
39 lock->m_is_done = false;
40 m_encoders[i] = NULL;
41 }
42
43 // initialize shared data
44 {
45 SharedLock lock(&m_shared_data);
46 lock->m_total_bytes = 0;
47 lock->m_stats_actual_bit_rate = 0.0;
48 lock->m_stats_previous_time = NOPTS_DOUBLE;
49 lock->m_stats_previous_bytes = 0;
50 }
51
52 // initialize thread signals
53 m_is_done = false;
54 m_error_occurred = false;
55
56 try {
57 Init();
58 } catch(...) {
59 Free();
60 throw;
61 }
62
63 }
64
~Muxer()65 Muxer::~Muxer() {
66
67 if(m_started) {
68
69 // stop the encoders
70 Logger::LogInfo("[Muxer::~Muxer] " + Logger::tr("Stopping encoders ..."));
71 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
72 m_encoders[i]->Stop(); // no deadlock: nothing in Muxer is locked in this thread (and BaseEncoder::Stop is lock-free, but that could change)
73 }
74
75 // wait for the thread to stop
76 if(m_thread.joinable()) {
77 Logger::LogInfo("[Muxer::~Muxer] " + Logger::tr("Waiting for muxer thread to stop ..."));
78 m_thread.join();
79 }
80
81 }
82
83 // free everything
84 Free();
85
86 }
87
AddVideoEncoder(const QString & codec_name,const std::vector<std::pair<QString,QString>> & codec_options,unsigned int bit_rate,unsigned int width,unsigned int height,unsigned int frame_rate)88 VideoEncoder* Muxer::AddVideoEncoder(const QString& codec_name, const std::vector<std::pair<QString, QString> >& codec_options,
89 unsigned int bit_rate, unsigned int width, unsigned int height, unsigned int frame_rate) {
90 AVCodec *codec = FindCodec(codec_name);
91 AVCodecContext *codec_context = NULL;
92 AVStream *stream = AddStream(codec, &codec_context);
93 VideoEncoder *encoder;
94 AVDictionary *options = NULL;
95 try {
96 VideoEncoder::PrepareStream(stream, codec_context, codec, &options, codec_options, bit_rate, width, height, frame_rate);
97 m_encoders[stream->index] = encoder = new VideoEncoder(this, stream, codec_context, codec, &options);
98 #if SSR_USE_AVSTREAM_CODECPAR
99 if(avcodec_parameters_from_context(stream->codecpar, codec_context) < 0) {
100 Logger::LogError("[Muxer::AddVideoEncoder] " + Logger::tr("Error: Can't copy parameters to stream!"));
101 throw LibavException();
102 }
103 #endif
104 av_dict_free(&options);
105 } catch(...) {
106 av_dict_free(&options);
107 throw;
108 }
109 return encoder;
110 }
111
AddAudioEncoder(const QString & codec_name,const std::vector<std::pair<QString,QString>> & codec_options,unsigned int bit_rate,unsigned int channels,unsigned int sample_rate)112 AudioEncoder* Muxer::AddAudioEncoder(const QString& codec_name, const std::vector<std::pair<QString, QString> >& codec_options,
113 unsigned int bit_rate, unsigned int channels, unsigned int sample_rate) {
114 AVCodec *codec = FindCodec(codec_name);
115 AVCodecContext *codec_context = NULL;
116 AVStream *stream = AddStream(codec, &codec_context);
117 AudioEncoder *encoder;
118 AVDictionary *options = NULL;
119 try {
120 AudioEncoder::PrepareStream(stream, codec_context, codec, &options, codec_options, bit_rate, channels, sample_rate);
121 m_encoders[stream->index] = encoder = new AudioEncoder(this, stream, codec_context, codec, &options);
122 #if SSR_USE_AVSTREAM_CODECPAR
123 if(avcodec_parameters_from_context(stream->codecpar, codec_context) < 0) {
124 Logger::LogError("[Muxer::AddAudioEncoder] " + Logger::tr("Error: Can't copy parameters to stream!"));
125 throw LibavException();
126 }
127 #endif
128 av_dict_free(&options);
129 } catch(...) {
130 av_dict_free(&options);
131 throw;
132 }
133 return encoder;
134 }
135
Start()136 void Muxer::Start() {
137 assert(!m_started);
138
139 // make sure all encoders were created successfully
140 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
141 assert(m_encoders[i] != NULL);
142 }
143
144 // write header
145 if(avformat_write_header(m_format_context, NULL) != 0) {
146 Logger::LogError("[Muxer::Start] " + Logger::tr("Error: Can't write header!", "Don't translate 'header'"));
147 throw LibavException();
148 }
149
150 m_started = true;
151 m_thread = std::thread(&Muxer::MuxerThread, this);
152
153 }
154
Finish()155 void Muxer::Finish() {
156 assert(m_started);
157 Logger::LogInfo("[Muxer::Finish] " + Logger::tr("Finishing encoders ..."));
158 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
159 assert(m_encoders[i] != NULL);
160 m_encoders[i]->Finish(); // no deadlock: nothing in Muxer is locked in this thread (and BaseEncoder::Finish is lock-free, but that could change)
161 }
162 }
163
GetActualBitRate()164 double Muxer::GetActualBitRate() {
165 SharedLock lock(&m_shared_data);
166 return lock->m_stats_actual_bit_rate;
167 }
168
GetTotalBytes()169 uint64_t Muxer::GetTotalBytes() {
170 SharedLock lock(&m_shared_data);
171 return lock->m_total_bytes;
172 }
173
EndStream(unsigned int stream_index)174 void Muxer::EndStream(unsigned int stream_index) {
175 assert(stream_index < m_format_context->nb_streams);
176 StreamLock lock(&m_stream_data[stream_index]);
177 lock->m_is_done = true;
178 }
179
AddPacket(unsigned int stream_index,std::unique_ptr<AVPacketWrapper> packet)180 void Muxer::AddPacket(unsigned int stream_index, std::unique_ptr<AVPacketWrapper> packet) {
181 assert(m_started);
182 assert(stream_index < m_format_context->nb_streams);
183 StreamLock lock(&m_stream_data[stream_index]);
184 lock->m_packet_queue.push_back(std::move(packet));
185 }
186
GetQueuedPacketCount(unsigned int stream_index)187 unsigned int Muxer::GetQueuedPacketCount(unsigned int stream_index) {
188 assert(m_started);
189 assert(stream_index < m_format_context->nb_streams);
190 StreamLock lock(&m_stream_data[stream_index]);
191 return lock->m_packet_queue.size();
192 }
193
Init()194 void Muxer::Init() {
195
196 // get the format we want (this is just a pointer, we don't have to free this)
197 AVOutputFormat *format = av_guess_format(m_container_name.toUtf8().constData(), NULL, NULL);
198 if(format == NULL) {
199 Logger::LogError("[Muxer::Init] " + Logger::tr("Error: Can't find chosen output format!"));
200 throw LibavException();
201 }
202
203 Logger::LogInfo("[Muxer::Init] " + Logger::tr("Using format %1 (%2).").arg(format->name).arg(format->long_name));
204
205 // allocate format context
206 // ffmpeg probably wants us to use avformat_alloc_output_context2 instead, but libav doesn't have it and I can't figure out why it's needed anyway
207 m_format_context = avformat_alloc_context();
208 if(m_format_context == NULL) {
209 Logger::LogError("[Muxer::Init] " + Logger::tr("Error: Can't allocate format context!"));
210 throw LibavException();
211 }
212 m_format_context->oformat = format;
213
214 // open file
215 if(avio_open(&m_format_context->pb, QFile::encodeName(m_output_file).constData(), AVIO_FLAG_WRITE) < 0) {
216 Logger::LogError("[Muxer::Init] " + Logger::tr("Error: Can't open output file!"));
217 throw LibavException();
218 }
219
220 }
221
Free()222 void Muxer::Free() {
223 if(m_format_context != NULL) {
224
225 // write trailer (needed to free private muxer data)
226 if(m_started) {
227 if(av_write_trailer(m_format_context) != 0) {
228 // we can't throw exceptions here because this is called from the destructor
229 Logger::LogError("[Muxer::Free] " + Logger::tr("Error: Can't write trailer, continuing anyway.", "Don't translate 'trailer'"));
230 }
231 m_started = false;
232 }
233
234 // destroy the encoders
235 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
236 if(m_encoders[i] != NULL) {
237 delete m_encoders[i]; // no deadlock: nothing in Muxer is locked in this thread
238 m_encoders[i] = NULL;
239 }
240 }
241
242 // close file
243 if(m_format_context->pb != NULL) {
244 avio_close(m_format_context->pb);
245 m_format_context->pb = NULL;
246 }
247
248 // free everything
249 #if SSR_USE_AVFORMAT_FREE_CONTEXT
250 avformat_free_context(m_format_context);
251 m_format_context = NULL;
252 #else
253 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
254 av_freep(&m_format_context->streams[i]->codec);
255 av_freep(&m_format_context->streams[i]);
256 }
257 av_free(m_format_context);
258 m_format_context = NULL;
259 #endif
260
261 }
262 }
263
FindCodec(const QString & codec_name)264 AVCodec* Muxer::FindCodec(const QString& codec_name) {
265 AVCodec *codec = avcodec_find_encoder_by_name(codec_name.toUtf8().constData());
266 if(codec == NULL) {
267 Logger::LogError("[Muxer::FindCodec] " + Logger::tr("Error: Can't find codec!"));
268 throw LibavException();
269 }
270 return codec;
271 }
272
AddStream(AVCodec * codec,AVCodecContext ** codec_context)273 AVStream* Muxer::AddStream(AVCodec* codec, AVCodecContext** codec_context) {
274 assert(!m_started);
275 assert(m_format_context->nb_streams < MUXER_MAX_STREAMS);
276
277 Logger::LogInfo("[Muxer::AddStream] " + Logger::tr("Using codec %1 (%2).").arg(codec->name).arg(codec->long_name));
278
279 // create a new stream
280 #if SSR_USE_AVSTREAM_CODECPAR
281 AVStream *stream = avformat_new_stream(m_format_context, NULL);
282 #elif SSR_USE_AVFORMAT_NEW_STREAM
283 AVStream *stream = avformat_new_stream(m_format_context, codec);
284 #else
285 AVStream *stream = av_new_stream(m_format_context, m_format_context->nb_streams);
286 #endif
287 if(stream == NULL) {
288 Logger::LogError("[Muxer::AddStream] " + Logger::tr("Error: Can't create new stream!"));
289 throw LibavException();
290 }
291 assert(stream->index == (int) m_format_context->nb_streams - 1);
292 #if SSR_USE_AVSTREAM_CODECPAR
293 *codec_context = avcodec_alloc_context3(codec);
294 if(*codec_context == NULL) {
295 Logger::LogError("[Muxer::AddStream] " + Logger::tr("Error: Can't create new codec context!"));
296 throw LibavException();
297 }
298 #else
299 assert(stream->codec != NULL);
300 *codec_context = stream->codec;
301 #endif
302 //stream->id = m_format_context->nb_streams - 1;
303
304 #if !SSR_USE_AVFORMAT_NEW_STREAM
305 // initialize the codec context (only needed for old API)
306 if(avcodec_get_context_defaults3(*codec_context, codec) < 0) {
307 Logger::LogError("[Muxer::AddStream] " + Logger::tr("Error: Can't get codec context defaults!"));
308 throw LibavException();
309 }
310 (*codec_context)->codec_id = codec->id;
311 (*codec_context)->codec_type = codec->type;
312 #endif
313
314 // not sure why this is needed, but it's in the example code and it doesn't work without this
315 if(m_format_context->oformat->flags & AVFMT_GLOBALHEADER)
316 (*codec_context)->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
317
318 // if the codec is experimental, allow it
319 if(codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) {
320 Logger::LogWarning("[Muxer::AddStream] " + Logger::tr("Warning: This codec is considered experimental by libav/ffmpeg."));
321 (*codec_context)->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
322 }
323
324 #if SSR_USE_SIDE_DATA_ONLY_PACKETS && !SSR_USE_SIDE_DATA_ONLY_PACKETS_DEPRECATED
325 // this option was added with the intent to deprecate it again in the next version,
326 // because the ffmpeg/libav devs like deprecating things :)
327 (*codec_context)->side_data_only_packets = 1;
328 #endif
329
330 return stream;
331 }
332
MuxerThread()333 void Muxer::MuxerThread() {
334 try {
335
336 Logger::LogInfo("[Muxer::MuxerThread] " + Logger::tr("Muxer thread started."));
337
338 double total_time = 0.0;
339
340 // start muxing
341 for( ; ; ) {
342
343 // get a packet from a stream that isn't done yet
344 std::unique_ptr<AVPacketWrapper> packet;
345 unsigned int current_stream = 0, streams_done = 0;
346 for(unsigned int i = 0; i < m_format_context->nb_streams; ++i) {
347 StreamLock lock(&m_stream_data[i]);
348 if(lock->m_packet_queue.empty()) {
349 if(lock->m_is_done)
350 ++streams_done;
351 } else {
352 current_stream = i;
353 packet = std::move(lock->m_packet_queue.front());
354 lock->m_packet_queue.pop_front();
355 break;
356 }
357 }
358
359 // if all streams are done, we can stop
360 if(streams_done == m_format_context->nb_streams) {
361 break;
362 }
363
364 // if there is no packet, wait and try again later
365 if(packet == NULL) {
366 usleep(20000);
367 continue;
368 }
369
370 // try to figure out the time (the exact value is not critical, it's only used for bitrate statistics)
371 AVStream *stream = m_encoders[current_stream]->GetStream();
372 AVCodecContext *codec_context = m_encoders[current_stream]->GetCodecContext();
373 double packet_time = 0.0;
374 if(packet->GetPacket()->dts != (int64_t) AV_NOPTS_VALUE)
375 packet_time = (double) packet->GetPacket()->dts * ToDouble(codec_context->time_base);
376 else if(packet->GetPacket()->pts != (int64_t) AV_NOPTS_VALUE)
377 packet_time = (double) packet->GetPacket()->pts * ToDouble(codec_context->time_base);
378 if(packet_time > total_time)
379 total_time = packet_time;
380
381 // prepare packet
382 packet->GetPacket()->stream_index = current_stream;
383 #if SSR_USE_AV_PACKET_RESCALE_TS
384 av_packet_rescale_ts(packet->GetPacket(), codec_context->time_base, stream->time_base);
385 #else
386 if(packet->GetPacket()->pts != (int64_t) AV_NOPTS_VALUE) {
387 packet->GetPacket()->pts = av_rescale_q(packet->GetPacket()->pts, codec_context->time_base, stream->time_base);
388 }
389 if(packet->GetPacket()->dts != (int64_t) AV_NOPTS_VALUE) {
390 packet->GetPacket()->dts = av_rescale_q(packet->GetPacket()->dts, codec_context->time_base, stream->time_base);
391 }
392 #endif
393
394 // write the packet (again, why does libav/ffmpeg call this a frame?)
395 if(av_interleaved_write_frame(m_format_context, packet->GetPacket()) != 0) {
396 Logger::LogError("[Muxer::MuxerThread] " + Logger::tr("Error: Can't write frame to muxer!"));
397 throw LibavException();
398 }
399
400 // the data is now owned by libav/ffmpeg, so don't free it
401 packet->SetFreeOnDestruct(false);
402
403 // update the byte counter
404 {
405 SharedLock lock(&m_shared_data);
406 lock->m_total_bytes = m_format_context->pb->pos + (m_format_context->pb->buf_ptr - m_format_context->pb->buffer);
407 if(lock->m_stats_previous_time == NOPTS_DOUBLE) {
408 lock->m_stats_previous_time = total_time;
409 lock->m_stats_previous_bytes = lock->m_total_bytes;
410 }
411 double timedelta = total_time - lock->m_stats_previous_time;
412 if(timedelta > 0.999999) {
413 lock->m_stats_actual_bit_rate = (double) ((lock->m_total_bytes - lock->m_stats_previous_bytes) * 8) / timedelta;
414 lock->m_stats_previous_time = total_time;
415 lock->m_stats_previous_bytes = lock->m_total_bytes;
416 }
417 }
418
419 }
420
421 // tell the others that we're done
422 m_is_done = true;
423
424 Logger::LogInfo("[Muxer::MuxerThread] " + Logger::tr("Muxer thread stopped."));
425
426 } catch(const std::exception& e) {
427 m_error_occurred = true;
428 Logger::LogError("[Muxer::MuxerThread] " + Logger::tr("Exception '%1' in muxer thread.").arg(e.what()));
429 } catch(...) {
430 m_error_occurred = true;
431 Logger::LogError("[Muxer::MuxerThread] " + Logger::tr("Unknown exception in muxer thread."));
432 }
433 }
434