1 // MediaParser.cpp: Media file parser, for Gnash.
3 // Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "MediaParser.h"
23 #include "GnashSleep.h" // for usleep.
25 #include <boost/bind.hpp>
27 // Define this to get debugging output from MediaParser
28 //#define GNASH_DEBUG_MEDIAPARSER
33 MediaParser::MediaParser(std::auto_ptr
<IOChannel
> stream
)
35 _parsingComplete(false),
38 _bufferTime(100), // 100 ms
40 _parserThreadStartBarrier(2),
41 _parserThreadKillRequested(false),
48 MediaParser::startParserThread()
50 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
51 log_debug("Starting MediaParser thread");
52 _parserThread
.reset(new boost::thread(
53 boost::bind(parserLoopStarter
, this)));
54 _parserThreadStartBarrier
.wait();
59 MediaParser::getBufferLength() const
61 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
62 boost::mutex::scoped_lock
lock(_qMutex
);
64 return getBufferLengthNoLock();
68 MediaParser::getBufferLengthNoLock() const
70 bool hasVideo
= _videoInfo
.get();
71 bool hasAudio
= _audioInfo
.get();
73 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
75 if (hasVideo
&& hasAudio
) {
76 return std::min(audioBufferLength(), videoBufferLength());
79 if (hasVideo
) return videoBufferLength();
81 if (hasAudio
) return audioBufferLength();
87 MediaParser::videoBufferLength() const
89 if (_videoFrames
.empty()) return 0;
91 log_debug("videoBufferLength: %d - %d == %d",
92 _videoFrames
.back()->timestamp(), _videoFrames
.front()->timestamp(),
93 _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp());
95 return _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp();
99 MediaParser::audioBufferLength() const
101 if (_audioFrames
.empty()) return 0;
103 log_debug("audioBufferLength: %d - %d == %d",
104 _audioFrames
.back()->timestamp
, _audioFrames
.front()->timestamp
,
105 _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
);
107 return _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
;
110 const EncodedVideoFrame
*
111 MediaParser::peekNextVideoFrame() const
113 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
114 boost::mutex::scoped_lock
lock(_qMutex
);
115 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
116 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
118 const_cast<MediaParser
*>(this)->parseNextChunk();
122 if (!_videoInfo
.get() || _videoFrames
.empty()) return 0;
123 return _videoFrames
.front();
127 MediaParser::nextFrameTimestamp(boost::uint64_t& ts
) const
129 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
130 boost::mutex::scoped_lock
lock(_qMutex
);
131 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
132 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
134 const_cast<MediaParser
*>(this)->parseNextChunk();
138 if (_videoFrames
.empty())
140 if (_audioFrames
.empty())
146 ts
= _audioFrames
.front()->timestamp
;
152 if (_audioFrames
.empty())
154 ts
= _videoFrames
.front()->timestamp();
159 ts
= std::min(_videoFrames
.front()->timestamp(),
160 _audioFrames
.front()->timestamp
);
167 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts
) const
169 const EncodedVideoFrame
* ef
= peekNextVideoFrame();
170 if ( ! ef
) return false;
171 ts
= ef
->timestamp();
175 std::auto_ptr
<EncodedVideoFrame
>
176 MediaParser::nextVideoFrame()
178 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
179 boost::mutex::scoped_lock
lock(_qMutex
);
180 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
181 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
183 const_cast<MediaParser
*>(this)->parseNextChunk();
187 std::auto_ptr
<EncodedVideoFrame
> ret
;
188 if (_videoFrames
.empty()) return ret
;
189 ret
.reset(_videoFrames
.front());
190 _videoFrames
.pop_front();
191 #ifdef GNASH_DEBUG_MEDIAPARSER
192 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
193 #endif // GNASH_DEBUG_MEDIAPARSER
194 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
198 std::auto_ptr
<EncodedAudioFrame
>
199 MediaParser::nextAudioFrame()
201 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
202 boost::mutex::scoped_lock
lock(_qMutex
);
203 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
204 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
206 const_cast<MediaParser
*>(this)->parseNextChunk();
210 std::auto_ptr
<EncodedAudioFrame
> ret
;
211 if (_audioFrames
.empty()) return ret
;
212 ret
.reset(_audioFrames
.front());
213 _audioFrames
.pop_front();
214 #ifdef GNASH_DEBUG_MEDIAPARSER
215 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
216 #endif // GNASH_DEBUG_MEDIAPARSER
217 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
222 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts
) const
224 const EncodedAudioFrame
* ef
= peekNextAudioFrame();
225 if ( ! ef
) return false;
230 const EncodedAudioFrame
*
231 MediaParser::peekNextAudioFrame() const
233 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
234 boost::mutex::scoped_lock
lock(_qMutex
);
235 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
236 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
238 const_cast<MediaParser
*>(this)->parseNextChunk();
241 if (!_audioInfo
.get() || _audioFrames
.empty()) return 0;
242 return _audioFrames
.front();
246 MediaParser::stopParserThread()
248 if ( _parserThread
.get() )
250 requestParserThreadKill();
251 _parserThread
->join();
252 _parserThread
.reset();
256 MediaParser::~MediaParser()
260 for (VideoFrames::iterator i
=_videoFrames
.begin(),
261 e
=_videoFrames
.end(); i
!=e
; ++i
)
266 for (AudioFrames::iterator i
=_audioFrames
.begin(),
267 e
=_audioFrames
.end(); i
!=e
; ++i
)
274 MediaParser::clearBuffers()
276 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
277 boost::mutex::scoped_lock
lock(_qMutex
);
280 for (VideoFrames::iterator i
=_videoFrames
.begin(),
281 e
=_videoFrames
.end(); i
!=e
; ++i
)
286 for (AudioFrames::iterator i
=_audioFrames
.begin(),
287 e
=_audioFrames
.end(); i
!=e
; ++i
)
292 _audioFrames
.clear();
293 _videoFrames
.clear();
295 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer
299 MediaParser::pushEncodedAudioFrame(std::auto_ptr
<EncodedAudioFrame
> frame
)
301 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
302 boost::mutex::scoped_lock
lock(_qMutex
);
305 // Find location to insert this new frame to, so that
306 // timestamps are sorted
308 AudioFrames::iterator loc
= _audioFrames
.end();
309 if ( ! _audioFrames
.empty() ) {
311 AudioFrames::reverse_iterator i
=_audioFrames
.rbegin();
312 for (AudioFrames::reverse_iterator e
=_audioFrames
.rend(); i
!=e
; ++i
)
314 if ( (*i
)->timestamp
<= frame
->timestamp
) break;
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(),
328 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
329 _audioFrames
.insert(loc
, frame
.release());
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
339 MediaParser::pushEncodedVideoFrame(std::auto_ptr
<EncodedVideoFrame
> frame
)
341 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
342 boost::mutex::scoped_lock
lock(_qMutex
);
345 // Find location to insert this new frame to, so that
346 // timestamps are sorted
348 VideoFrames::iterator loc
= _videoFrames
.end();
349 if ( ! _videoFrames
.empty() ) {
351 VideoFrames::reverse_iterator i
=_videoFrames
.rbegin();
352 for (VideoFrames::reverse_iterator e
=_videoFrames
.rend(); i
!=e
; ++i
)
354 if ( (*i
)->timestamp() <= frame
->timestamp() ) break;
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(),
368 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
369 _videoFrames
.insert(loc
, frame
.release());
371 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
372 waitIfNeeded(lock
); // if the push reaches a "buffer full" condition, wait to be waken up
377 MediaParser::waitIfNeeded(boost::mutex::scoped_lock
& lock
)
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 ?
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
396 MediaParser::bufferFull() const
398 // Callers are expected to hold a lock on _qMutex
399 int bl
= getBufferLengthNoLock();
400 int bt
= getBufferTime();
401 #ifdef GNASH_DEBUG_MEDIAPARSER
402 log_debug("MediaParser::bufferFull: %d/%d", bl
, bt
);
403 #endif // GNASH_DEBUG_MEDIAPARSER
408 MediaParser::parserLoop()
410 _parserThreadStartBarrier
.wait();
411 while (!parserThreadKillRequested())
414 gnashSleep(100); // thread switch
416 // check for parsing complete
417 // TODO: have a setParsingComplete() function
418 // exposed in base class for taking care
419 // of this on appropriate time.
420 boost::mutex::scoped_lock
lock(_qMutex
);
427 MediaParser::fetchMetaTags(OrderedMetaTags
& /*tags*/, boost::uint64_t /*ts*/)
433 operator<< (std::ostream
& os
, const VideoInfo
& vi
)
435 os
<< "codec:" << vi
.codec
<< " (type " << vi
.type
<< ") - "
436 << "size:" << vi
.width
<< "x" << vi
.height
<< " - "
437 << "frameRate:" << vi
.frameRate
<< " - "
438 << "duration:" << vi
.duration
;
443 operator<< (std::ostream
& os
, const videoCodecType
& t
)
447 case VIDEO_CODEC_H263
:
450 case VIDEO_CODEC_SCREENVIDEO
:
453 case VIDEO_CODEC_VP6
:
456 case VIDEO_CODEC_VP6A
:
459 case VIDEO_CODEC_SCREENVIDEO2
:
460 os
<< "Screenvideo2";
462 case VIDEO_CODEC_H264
:
466 os
<< "unknown/invalid codec " << static_cast<int>(t
);
473 operator<< (std::ostream
& os
, const audioCodecType
& t
)
477 case AUDIO_CODEC_RAW
:
480 case AUDIO_CODEC_ADPCM
:
483 case AUDIO_CODEC_MP3
:
486 case AUDIO_CODEC_UNCOMPRESSED
:
487 os
<< "Uncompressed";
489 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO
:
490 os
<< "Nellymoser 8Hz mono";
492 case AUDIO_CODEC_NELLYMOSER
:
495 case AUDIO_CODEC_AAC
:
496 os
<< "Advanced Audio Coding";
498 case AUDIO_CODEC_SPEEX
:
502 os
<< "unknown/invalid codec " << static_cast<int>(t
);
508 } // end of gnash::media namespace
509 } // end of gnash namespace