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