1 // MediaParser.cpp:  Media file parser, for Gnash.
2 //
3 //   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016
4 //   Free Software Foundation, Inc.
5 //
6 // This program 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 // This program 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 this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 
22 #include "MediaParser.h"
23 
24 #include <functional>
25 #include <thread>
26 
27 #include "log.h"
28 #include "GnashSleep.h" // for usleep.
29 #include "Id3Info.h"
30 
31 // Define this to get debugging output from MediaParser
32 //#define GNASH_DEBUG_MEDIAPARSER
33 
34 namespace gnash {
35 namespace media {
36 
MediaParser(std::unique_ptr<IOChannel> stream)37 MediaParser::MediaParser(std::unique_ptr<IOChannel> stream)
38 	:
39 	_parsingComplete(false),
40 	_bytesLoaded(0),
41 	_stream(std::move(stream)),
42 	_bufferTime(100), // 100 ms
43 	_parserThreadKillRequested(false),
44 	_seekRequest(false)
45 {
46 }
47 
48 /*protected*/
49 void
startParserThread()50 MediaParser::startParserThread()
51 {
52 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
53 	log_debug("Starting MediaParser thread");
54 	_parserThread = std::thread(&MediaParser::parserLoop, this);
55 #endif
56 }
57 
58 std::uint64_t
getBufferLength() const59 MediaParser::getBufferLength() const
60 {
61 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
62 	std::lock_guard<std::mutex> lock(_qMutex);
63 #endif
64 	return getBufferLengthNoLock();
65 }
66 
67 /* public */
68 bool
isBufferEmpty() const69 MediaParser::isBufferEmpty() const
70 {
71 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
72 	std::lock_guard<std::mutex> lock(_qMutex);
73 #endif
74 	return _videoFrames.empty() && _audioFrames.empty();
75 }
76 
77 boost::optional<Id3Info>
getId3Info() const78 MediaParser::getId3Info() const
79 {
80     log_error(_("No ID3 support implemented in this MediaParser"));
81     return boost::optional<Id3Info>();
82 }
83 
84 std::uint64_t
getBufferLengthNoLock() const85 MediaParser::getBufferLengthNoLock() const
86 {
87 	bool hasVideo = _videoInfo.get();
88 	bool hasAudio = _audioInfo.get();
89 
90 	//log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
91 
92 	if (hasVideo && hasAudio) {
93 		return std::min(audioBufferLength(), videoBufferLength());
94 	}
95 
96     if (hasVideo) return videoBufferLength();
97 
98     if (hasAudio) return audioBufferLength();
99 
100     return 0;
101 }
102 
103 std::uint64_t
videoBufferLength() const104 MediaParser::videoBufferLength() const
105 {
106 	if (_videoFrames.empty()) return 0;
107 #if 0 // debugging
108 	log_debug("videoBufferLength: %d - %d == %d",
109 		_videoFrames.back()->timestamp(), _videoFrames.front()->timestamp(),
110 		_videoFrames.back()->timestamp() - _videoFrames.front()->timestamp());
111 #endif
112 	return _videoFrames.back()->timestamp() - _videoFrames.front()->timestamp();
113 }
114 
115 std::uint64_t
audioBufferLength() const116 MediaParser::audioBufferLength() const
117 {
118 	if (_audioFrames.empty()) return 0;
119 #if 0 // debugging
120 	log_debug("audioBufferLength: %d - %d == %d",
121 		_audioFrames.back()->timestamp, _audioFrames.front()->timestamp,
122 		_audioFrames.back()->timestamp - _audioFrames.front()->timestamp);
123 #endif
124 	return _audioFrames.back()->timestamp - _audioFrames.front()->timestamp;
125 }
126 
127 /*private*/
128 const EncodedVideoFrame*
peekNextVideoFrame() const129 MediaParser::peekNextVideoFrame() const
130 {
131 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
132     // TODO: assert _qMutex is locked by this thread
133 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
134 	while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
135 	{
136 		const_cast<MediaParser*>(this)->parseNextChunk();
137 	}
138 #endif
139 
140 	if (!_videoInfo.get() || _videoFrames.empty()) return nullptr;
141 	return _videoFrames.front().get();
142 }
143 
144 bool
nextFrameTimestamp(std::uint64_t & ts) const145 MediaParser::nextFrameTimestamp(std::uint64_t& ts) const
146 {
147 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
148     std::lock_guard<std::mutex> lock(_qMutex);
149 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
150     while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
151     {
152         const_cast<MediaParser*>(this)->parseNextChunk();
153     }
154 #endif
155 
156     if (_videoFrames.empty())
157     {
158         if (_audioFrames.empty())
159         {
160             return false;
161         }
162         else
163         {
164             ts = _audioFrames.front()->timestamp;
165             return true;
166         }
167     }
168     else
169     {
170         if (_audioFrames.empty())
171         {
172             ts = _videoFrames.front()->timestamp();
173             return true;
174         }
175         else
176         {
177             ts = std::min(_videoFrames.front()->timestamp(),
178                           _audioFrames.front()->timestamp);
179             return true;
180         }
181     }
182 }
183 
184 bool
nextVideoFrameTimestamp(std::uint64_t & ts) const185 MediaParser::nextVideoFrameTimestamp(std::uint64_t& ts) const
186 {
187 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
188 	std::lock_guard<std::mutex> lock(_qMutex);
189 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
190 	const EncodedVideoFrame* ef = peekNextVideoFrame();
191 	if ( ! ef ) return false;
192 	ts = ef->timestamp();
193 	return true;
194 }
195 
196 std::unique_ptr<EncodedVideoFrame>
nextVideoFrame()197 MediaParser::nextVideoFrame()
198 {
199 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
200 	std::lock_guard<std::mutex> lock(_qMutex);
201 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
202 	while (!parsingCompleted() && _videoInfo.get() && _videoFrames.empty())
203 	{
204 		const_cast<MediaParser*>(this)->parseNextChunk();
205 	}
206 #endif
207 
208 	std::unique_ptr<EncodedVideoFrame> ret;
209 	if (_videoFrames.empty()) return ret;
210 	ret = std::move(_videoFrames.front());
211 	_videoFrames.pop_front();
212 #ifdef GNASH_DEBUG_MEDIAPARSER
213 	log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
214 #endif // GNASH_DEBUG_MEDIAPARSER
215 	_parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
216 	return ret;
217 }
218 
219 std::unique_ptr<EncodedAudioFrame>
nextAudioFrame()220 MediaParser::nextAudioFrame()
221 {
222 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
223 	std::lock_guard<std::mutex> lock(_qMutex);
224 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
225 	while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
226 	{
227 		const_cast<MediaParser*>(this)->parseNextChunk();
228 	}
229 #endif
230 
231 	std::unique_ptr<EncodedAudioFrame> ret;
232 	if (_audioFrames.empty()) return ret;
233 	ret = std::move(_audioFrames.front());
234 	_audioFrames.pop_front();
235 #ifdef GNASH_DEBUG_MEDIAPARSER
236 	log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
237 #endif // GNASH_DEBUG_MEDIAPARSER
238 	_parserThreadWakeup.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
239 	return ret;
240 }
241 
242 bool
nextAudioFrameTimestamp(std::uint64_t & ts) const243 MediaParser::nextAudioFrameTimestamp(std::uint64_t& ts) const
244 {
245 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
246 	std::lock_guard<std::mutex> lock(_qMutex);
247 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
248 	const EncodedAudioFrame* ef = peekNextAudioFrame();
249 	if ( ! ef ) return false;
250 	ts = ef->timestamp;
251 	return true;
252 }
253 
254 /*private*/
255 const EncodedAudioFrame*
peekNextAudioFrame() const256 MediaParser::peekNextAudioFrame() const
257 {
258 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
259     // TODO: assert _qMutex is locked by this thread
260 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
261 	while (!parsingCompleted() && _audioInfo.get() && _audioFrames.empty())
262 	{
263 		const_cast<MediaParser*>(this)->parseNextChunk();
264 	}
265 #endif
266 	if (!_audioInfo.get() || _audioFrames.empty()) return nullptr;
267 	return _audioFrames.front().get();
268 }
269 
270 void
stopParserThread()271 MediaParser::stopParserThread()
272 {
273 	if ( _parserThread.joinable() )
274 	{
275 		requestParserThreadKill();
276 		_parserThread.join();
277 	}
278 }
279 
~MediaParser()280 MediaParser::~MediaParser()
281 {
282 	stopParserThread();
283 }
284 
285 void
clearBuffers()286 MediaParser::clearBuffers()
287 {
288 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
289 	std::lock_guard<std::mutex> lock(_qMutex);
290 #endif
291 
292 	_audioFrames.clear();
293 	_videoFrames.clear();
294 
295 	_parserThreadWakeup.notify_all(); // wake it up, to refill the buffer
296 }
297 
298 void
pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame> frame)299 MediaParser::pushEncodedAudioFrame(std::unique_ptr<EncodedAudioFrame> frame)
300 {
301 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
302     std::unique_lock<std::mutex> lock(_qMutex);
303 #endif
304 
305     // Find location to insert this new frame to, so that
306     // timestamps are sorted
307     //
308     AudioFrames::iterator loc = _audioFrames.end();
309     if ( ! _audioFrames.empty() ) {
310         size_t gap=0;
311         AudioFrames::reverse_iterator i=_audioFrames.rbegin();
312         for (AudioFrames::reverse_iterator e=_audioFrames.rend(); i!=e; ++i)
313         {
314             if ( (*i)->timestamp <= frame->timestamp ) break;
315             ++gap;
316         }
317 
318         loc = i.base();
319 
320         if ( gap ) {
321             log_debug("Timestamp of last %d/%d audio frames in queue "
322                 "greater then timestamp in the frame being "
323                 "inserted to it (%d).", gap, _audioFrames.size(),
324                 frame->timestamp);
325         }
326     }
327 
328 	//log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
329 	_audioFrames.insert(loc, std::move(frame));
330 
331 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
332 	// if the push reaches a "buffer full" condition, or if we find the parsing
333 	// to be completed, wait to be waken up
334 	waitIfNeeded(lock);
335 #endif
336 }
337 
338 void
pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame> frame)339 MediaParser::pushEncodedVideoFrame(std::unique_ptr<EncodedVideoFrame> frame)
340 {
341 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
342 	std::unique_lock<std::mutex> lock(_qMutex);
343 #endif
344 
345     // Find location to insert this new frame to, so that
346     // timestamps are sorted
347     //
348     VideoFrames::iterator loc = _videoFrames.end();
349     if ( ! _videoFrames.empty() ) {
350         size_t gap=0;
351         VideoFrames::reverse_iterator i=_videoFrames.rbegin();
352         for (VideoFrames::reverse_iterator e=_videoFrames.rend(); i!=e; ++i)
353         {
354             if ( (*i)->timestamp() <= frame->timestamp() ) break;
355             ++gap;
356         }
357 
358         loc = i.base();
359 
360         if ( gap ) {
361             log_debug("Timestamp of last %d/%d video frames in queue "
362                 "greater then timestamp() in the frame being "
363                 "inserted to it (%d).", gap, _videoFrames.size(),
364                 frame->timestamp());
365         }
366     }
367 
368 	//log_debug("Pushing video frame with timestamp %d", frame->timestamp());
369 	_videoFrames.insert(loc, std::move(frame));
370 
371 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
372 	waitIfNeeded(lock); // if the push reaches a "buffer full" condition, wait to be waken up
373 #endif
374 }
375 
376 void
waitIfNeeded(std::unique_lock<std::mutex> & lock)377 MediaParser::waitIfNeeded(std::unique_lock<std::mutex>& lock)
378 {
379 	//  We hold a lock on the queue here...
380 	bool pc=parsingCompleted();
381 	bool ic=indexingCompleted();
382 	bool bf=bufferFull();
383 	if (( pc || (bf && ic)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
384 	{
385 #ifdef GNASH_DEBUG_MEDIAPARSER
386 		log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc, bf);
387 #endif // GNASH_DEBUG_MEDIAPARSER
388 		_parserThreadWakeup.wait(lock);
389 #ifdef GNASH_DEBUG_MEDIAPARSER
390 		log_debug("Parser thread finished waiting on wakeup lock");
391 #endif // GNASH_DEBUG_MEDIAPARSER
392 	}
393 }
394 
395 bool
bufferFull() const396 MediaParser::bufferFull() const
397 {
398 	// Callers are expected to hold a lock on _qMutex
399 	std::uint64_t bl = getBufferLengthNoLock();
400 	std::uint64_t bt = getBufferTime();
401 #ifdef GNASH_DEBUG_MEDIAPARSER
402 	log_debug("MediaParser::bufferFull: %d/%d", bl, bt);
403 #endif // GNASH_DEBUG_MEDIAPARSER
404 	return bl > bt;
405 }
406 
407 void
parserLoop()408 MediaParser::parserLoop()
409 {
410 	while (!parserThreadKillRequested())
411 	{
412 		parseNextChunk();
413 		gnashSleep(100); // thread switch
414 
415 		// check for parsing complete
416 		// TODO: have a setParsingComplete() function
417 		//       exposed in base class for taking care
418 		//       of this on appropriate time.
419 		std::unique_lock<std::mutex> lock(_qMutex);
420 		waitIfNeeded(lock);
421 	}
422 }
423 
424 
425 void
fetchMetaTags(OrderedMetaTags &,std::uint64_t)426 MediaParser::fetchMetaTags(OrderedMetaTags& /*tags*/, std::uint64_t /*ts*/)
427 {
428 }
429 
430 
431 std::ostream&
operator <<(std::ostream & os,const VideoInfo & vi)432 operator<< (std::ostream& os, const VideoInfo& vi)
433 {
434 	os << "codec:" << vi.codec << " (type " << vi.type << ") - "
435 	   << "size:" << vi.width << "x" << vi.height << " - "
436 	   << "frameRate:" << vi.frameRate << " - "
437 	   << "duration:" << vi.duration;
438 	return os;
439 }
440 
441 std::ostream&
operator <<(std::ostream & os,const videoCodecType & t)442 operator<< (std::ostream& os, const videoCodecType& t)
443 {
444     switch (t)
445     {
446         case VIDEO_CODEC_H263:
447             os << "H263";
448             break;
449         case VIDEO_CODEC_SCREENVIDEO:
450             os << "Screenvideo";
451             break;
452         case VIDEO_CODEC_VP6:
453             os << "VP6";
454             break;
455         case VIDEO_CODEC_VP6A:
456             os << "VP6A";
457             break;
458         case VIDEO_CODEC_SCREENVIDEO2:
459             os << "Screenvideo2";
460             break;
461         case VIDEO_CODEC_H264:
462             os << "H264";
463             break;
464         default:
465             os << "unknown/invalid codec " << static_cast<int>(t);
466             break;
467     }
468     return os;
469 }
470 
471 } // end of gnash::media namespace
472 } // end of gnash namespace
473 
474 #undef PADDING_BYTES
475 #undef READ_CHUNKS
476