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();
69 MediaParser::isBufferEmpty() const
71 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
72 boost::mutex::scoped_lock
lock(_qMutex
);
74 return _videoFrames
.empty() && _audioFrames
.empty();
79 MediaParser::getBufferLengthNoLock() const
81 bool hasVideo
= _videoInfo
.get();
82 bool hasAudio
= _audioInfo
.get();
84 //log_debug("MediaParser::getBufferLength: %d video %d audio frames", _videoFrames.size(), _audioFrames.size());
86 if (hasVideo
&& hasAudio
) {
87 return std::min(audioBufferLength(), videoBufferLength());
90 if (hasVideo
) return videoBufferLength();
92 if (hasAudio
) return audioBufferLength();
98 MediaParser::videoBufferLength() const
100 if (_videoFrames
.empty()) return 0;
102 log_debug("videoBufferLength: %d - %d == %d",
103 _videoFrames
.back()->timestamp(), _videoFrames
.front()->timestamp(),
104 _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp());
106 return _videoFrames
.back()->timestamp() - _videoFrames
.front()->timestamp();
110 MediaParser::audioBufferLength() const
112 if (_audioFrames
.empty()) return 0;
114 log_debug("audioBufferLength: %d - %d == %d",
115 _audioFrames
.back()->timestamp
, _audioFrames
.front()->timestamp
,
116 _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
);
118 return _audioFrames
.back()->timestamp
- _audioFrames
.front()->timestamp
;
122 const EncodedVideoFrame
*
123 MediaParser::peekNextVideoFrame() const
125 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
126 // TODO: assert _qMutex is locked by this thread
127 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
128 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
130 const_cast<MediaParser
*>(this)->parseNextChunk();
134 if (!_videoInfo
.get() || _videoFrames
.empty()) return 0;
135 return _videoFrames
.front();
139 MediaParser::nextFrameTimestamp(boost::uint64_t& ts
) const
141 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
142 boost::mutex::scoped_lock
lock(_qMutex
);
143 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
144 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
146 const_cast<MediaParser
*>(this)->parseNextChunk();
150 if (_videoFrames
.empty())
152 if (_audioFrames
.empty())
158 ts
= _audioFrames
.front()->timestamp
;
164 if (_audioFrames
.empty())
166 ts
= _videoFrames
.front()->timestamp();
171 ts
= std::min(_videoFrames
.front()->timestamp(),
172 _audioFrames
.front()->timestamp
);
179 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts
) const
181 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
182 boost::mutex::scoped_lock
lock(_qMutex
);
183 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
184 const EncodedVideoFrame
* ef
= peekNextVideoFrame();
185 if ( ! ef
) return false;
186 ts
= ef
->timestamp();
190 std::auto_ptr
<EncodedVideoFrame
>
191 MediaParser::nextVideoFrame()
193 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
194 boost::mutex::scoped_lock
lock(_qMutex
);
195 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
196 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
198 const_cast<MediaParser
*>(this)->parseNextChunk();
202 std::auto_ptr
<EncodedVideoFrame
> ret
;
203 if (_videoFrames
.empty()) return ret
;
204 ret
.reset(_videoFrames
.front());
205 _videoFrames
.pop_front();
206 #ifdef GNASH_DEBUG_MEDIAPARSER
207 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
208 #endif // GNASH_DEBUG_MEDIAPARSER
209 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
213 std::auto_ptr
<EncodedAudioFrame
>
214 MediaParser::nextAudioFrame()
216 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
217 boost::mutex::scoped_lock
lock(_qMutex
);
218 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
219 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
221 const_cast<MediaParser
*>(this)->parseNextChunk();
225 std::auto_ptr
<EncodedAudioFrame
> ret
;
226 if (_audioFrames
.empty()) return ret
;
227 ret
.reset(_audioFrames
.front());
228 _audioFrames
.pop_front();
229 #ifdef GNASH_DEBUG_MEDIAPARSER
230 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
231 #endif // GNASH_DEBUG_MEDIAPARSER
232 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
237 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts
) const
239 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
240 boost::mutex::scoped_lock
lock(_qMutex
);
241 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
242 const EncodedAudioFrame
* ef
= peekNextAudioFrame();
243 if ( ! ef
) return false;
249 const EncodedAudioFrame
*
250 MediaParser::peekNextAudioFrame() const
252 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
253 // TODO: assert _qMutex is locked by this thread
254 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
255 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
257 const_cast<MediaParser
*>(this)->parseNextChunk();
260 if (!_audioInfo
.get() || _audioFrames
.empty()) return 0;
261 return _audioFrames
.front();
265 MediaParser::stopParserThread()
267 if ( _parserThread
.get() )
269 requestParserThreadKill();
270 _parserThread
->join();
271 _parserThread
.reset();
275 MediaParser::~MediaParser()
279 for (VideoFrames::iterator i
=_videoFrames
.begin(),
280 e
=_videoFrames
.end(); i
!=e
; ++i
)
285 for (AudioFrames::iterator i
=_audioFrames
.begin(),
286 e
=_audioFrames
.end(); i
!=e
; ++i
)
293 MediaParser::clearBuffers()
295 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
296 boost::mutex::scoped_lock
lock(_qMutex
);
299 for (VideoFrames::iterator i
=_videoFrames
.begin(),
300 e
=_videoFrames
.end(); i
!=e
; ++i
)
305 for (AudioFrames::iterator i
=_audioFrames
.begin(),
306 e
=_audioFrames
.end(); i
!=e
; ++i
)
311 _audioFrames
.clear();
312 _videoFrames
.clear();
314 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer
318 MediaParser::pushEncodedAudioFrame(std::auto_ptr
<EncodedAudioFrame
> frame
)
320 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
321 boost::mutex::scoped_lock
lock(_qMutex
);
324 // Find location to insert this new frame to, so that
325 // timestamps are sorted
327 AudioFrames::iterator loc
= _audioFrames
.end();
328 if ( ! _audioFrames
.empty() ) {
330 AudioFrames::reverse_iterator i
=_audioFrames
.rbegin();
331 for (AudioFrames::reverse_iterator e
=_audioFrames
.rend(); i
!=e
; ++i
)
333 if ( (*i
)->timestamp
<= frame
->timestamp
) break;
340 log_debug("Timestamp of last %d/%d audio frames in queue "
341 "greater then timestamp in the frame being "
342 "inserted to it (%d).", gap
, _audioFrames
.size(),
347 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
348 _audioFrames
.insert(loc
, frame
.release());
350 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
351 // if the push reaches a "buffer full" condition, or if we find the parsing
352 // to be completed, wait to be waken up
358 MediaParser::pushEncodedVideoFrame(std::auto_ptr
<EncodedVideoFrame
> frame
)
360 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
361 boost::mutex::scoped_lock
lock(_qMutex
);
364 // Find location to insert this new frame to, so that
365 // timestamps are sorted
367 VideoFrames::iterator loc
= _videoFrames
.end();
368 if ( ! _videoFrames
.empty() ) {
370 VideoFrames::reverse_iterator i
=_videoFrames
.rbegin();
371 for (VideoFrames::reverse_iterator e
=_videoFrames
.rend(); i
!=e
; ++i
)
373 if ( (*i
)->timestamp() <= frame
->timestamp() ) break;
380 log_debug("Timestamp of last %d/%d video frames in queue "
381 "greater then timestamp() in the frame being "
382 "inserted to it (%d).", gap
, _videoFrames
.size(),
387 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
388 _videoFrames
.insert(loc
, frame
.release());
390 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
391 waitIfNeeded(lock
); // if the push reaches a "buffer full" condition, wait to be waken up
396 MediaParser::waitIfNeeded(boost::mutex::scoped_lock
& lock
)
398 // We hold a lock on the queue here...
399 bool pc
=parsingCompleted();
400 bool ic
=indexingCompleted();
401 bool bf
=bufferFull();
402 if (( pc
|| (bf
&& ic
)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
404 #ifdef GNASH_DEBUG_MEDIAPARSER
405 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc
, bf
);
406 #endif // GNASH_DEBUG_MEDIAPARSER
407 _parserThreadWakeup
.wait(lock
);
408 #ifdef GNASH_DEBUG_MEDIAPARSER
409 log_debug("Parser thread finished waiting on wakeup lock");
410 #endif // GNASH_DEBUG_MEDIAPARSER
415 MediaParser::bufferFull() const
417 // Callers are expected to hold a lock on _qMutex
418 int bl
= getBufferLengthNoLock();
419 int bt
= getBufferTime();
420 #ifdef GNASH_DEBUG_MEDIAPARSER
421 log_debug("MediaParser::bufferFull: %d/%d", bl
, bt
);
422 #endif // GNASH_DEBUG_MEDIAPARSER
427 MediaParser::parserLoop()
429 _parserThreadStartBarrier
.wait();
430 while (!parserThreadKillRequested())
433 gnashSleep(100); // thread switch
435 // check for parsing complete
436 // TODO: have a setParsingComplete() function
437 // exposed in base class for taking care
438 // of this on appropriate time.
439 boost::mutex::scoped_lock
lock(_qMutex
);
446 MediaParser::fetchMetaTags(OrderedMetaTags
& /*tags*/, boost::uint64_t /*ts*/)
452 operator<< (std::ostream
& os
, const VideoInfo
& vi
)
454 os
<< "codec:" << vi
.codec
<< " (type " << vi
.type
<< ") - "
455 << "size:" << vi
.width
<< "x" << vi
.height
<< " - "
456 << "frameRate:" << vi
.frameRate
<< " - "
457 << "duration:" << vi
.duration
;
462 operator<< (std::ostream
& os
, const videoCodecType
& t
)
466 case VIDEO_CODEC_H263
:
469 case VIDEO_CODEC_SCREENVIDEO
:
472 case VIDEO_CODEC_VP6
:
475 case VIDEO_CODEC_VP6A
:
478 case VIDEO_CODEC_SCREENVIDEO2
:
479 os
<< "Screenvideo2";
481 case VIDEO_CODEC_H264
:
485 os
<< "unknown/invalid codec " << static_cast<int>(t
);
492 operator<< (std::ostream
& os
, const audioCodecType
& t
)
496 case AUDIO_CODEC_RAW
:
499 case AUDIO_CODEC_ADPCM
:
502 case AUDIO_CODEC_MP3
:
505 case AUDIO_CODEC_UNCOMPRESSED
:
506 os
<< "Uncompressed";
508 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO
:
509 os
<< "Nellymoser 8Hz mono";
511 case AUDIO_CODEC_NELLYMOSER
:
514 case AUDIO_CODEC_AAC
:
515 os
<< "Advanced Audio Coding";
517 case AUDIO_CODEC_SPEEX
:
521 os
<< "unknown/invalid codec " << static_cast<int>(t
);
527 } // end of gnash::media namespace
528 } // end of gnash namespace