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
;
111 const EncodedVideoFrame
*
112 MediaParser::peekNextVideoFrame() const
114 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
115 // TODO: assert _qMutex is locked by this thread
116 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
117 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
119 const_cast<MediaParser
*>(this)->parseNextChunk();
123 if (!_videoInfo
.get() || _videoFrames
.empty()) return 0;
124 return _videoFrames
.front();
128 MediaParser::nextFrameTimestamp(boost::uint64_t& ts
) const
130 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
131 boost::mutex::scoped_lock
lock(_qMutex
);
132 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
133 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
135 const_cast<MediaParser
*>(this)->parseNextChunk();
139 if (_videoFrames
.empty())
141 if (_audioFrames
.empty())
147 ts
= _audioFrames
.front()->timestamp
;
153 if (_audioFrames
.empty())
155 ts
= _videoFrames
.front()->timestamp();
160 ts
= std::min(_videoFrames
.front()->timestamp(),
161 _audioFrames
.front()->timestamp
);
168 MediaParser::nextVideoFrameTimestamp(boost::uint64_t& ts
) const
170 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
171 boost::mutex::scoped_lock
lock(_qMutex
);
172 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
173 const EncodedVideoFrame
* ef
= peekNextVideoFrame();
174 if ( ! ef
) return false;
175 ts
= ef
->timestamp();
179 std::auto_ptr
<EncodedVideoFrame
>
180 MediaParser::nextVideoFrame()
182 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
183 boost::mutex::scoped_lock
lock(_qMutex
);
184 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
185 while (!parsingCompleted() && _videoInfo
.get() && _videoFrames
.empty())
187 const_cast<MediaParser
*>(this)->parseNextChunk();
191 std::auto_ptr
<EncodedVideoFrame
> ret
;
192 if (_videoFrames
.empty()) return ret
;
193 ret
.reset(_videoFrames
.front());
194 _videoFrames
.pop_front();
195 #ifdef GNASH_DEBUG_MEDIAPARSER
196 log_debug("nextVideoFrame: waking up parser (in case it was sleeping)");
197 #endif // GNASH_DEBUG_MEDIAPARSER
198 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
202 std::auto_ptr
<EncodedAudioFrame
>
203 MediaParser::nextAudioFrame()
205 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
206 boost::mutex::scoped_lock
lock(_qMutex
);
207 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
208 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
210 const_cast<MediaParser
*>(this)->parseNextChunk();
214 std::auto_ptr
<EncodedAudioFrame
> ret
;
215 if (_audioFrames
.empty()) return ret
;
216 ret
.reset(_audioFrames
.front());
217 _audioFrames
.pop_front();
218 #ifdef GNASH_DEBUG_MEDIAPARSER
219 log_debug("nextAudioFrame: waking up parser (in case it was sleeping)");
220 #endif // GNASH_DEBUG_MEDIAPARSER
221 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer, SHOULDN'T WE HOLD A LoCK HERE?
226 MediaParser::nextAudioFrameTimestamp(boost::uint64_t& ts
) const
228 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
229 boost::mutex::scoped_lock
lock(_qMutex
);
230 #endif // def LOAD_MEDIA_IN_A_SEPARATE_THREAD
231 const EncodedAudioFrame
* ef
= peekNextAudioFrame();
232 if ( ! ef
) return false;
238 const EncodedAudioFrame
*
239 MediaParser::peekNextAudioFrame() const
241 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
242 // TODO: assert _qMutex is locked by this thread
243 #else // ndef LOAD_MEDIA_IN_A_SEPARATE_THREAD
244 while (!parsingCompleted() && _audioInfo
.get() && _audioFrames
.empty())
246 const_cast<MediaParser
*>(this)->parseNextChunk();
249 if (!_audioInfo
.get() || _audioFrames
.empty()) return 0;
250 return _audioFrames
.front();
254 MediaParser::stopParserThread()
256 if ( _parserThread
.get() )
258 requestParserThreadKill();
259 _parserThread
->join();
260 _parserThread
.reset();
264 MediaParser::~MediaParser()
268 for (VideoFrames::iterator i
=_videoFrames
.begin(),
269 e
=_videoFrames
.end(); i
!=e
; ++i
)
274 for (AudioFrames::iterator i
=_audioFrames
.begin(),
275 e
=_audioFrames
.end(); i
!=e
; ++i
)
282 MediaParser::clearBuffers()
284 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
285 boost::mutex::scoped_lock
lock(_qMutex
);
288 for (VideoFrames::iterator i
=_videoFrames
.begin(),
289 e
=_videoFrames
.end(); i
!=e
; ++i
)
294 for (AudioFrames::iterator i
=_audioFrames
.begin(),
295 e
=_audioFrames
.end(); i
!=e
; ++i
)
300 _audioFrames
.clear();
301 _videoFrames
.clear();
303 _parserThreadWakeup
.notify_all(); // wake it up, to refill the buffer
307 MediaParser::pushEncodedAudioFrame(std::auto_ptr
<EncodedAudioFrame
> frame
)
309 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
310 boost::mutex::scoped_lock
lock(_qMutex
);
313 // Find location to insert this new frame to, so that
314 // timestamps are sorted
316 AudioFrames::iterator loc
= _audioFrames
.end();
317 if ( ! _audioFrames
.empty() ) {
319 AudioFrames::reverse_iterator i
=_audioFrames
.rbegin();
320 for (AudioFrames::reverse_iterator e
=_audioFrames
.rend(); i
!=e
; ++i
)
322 if ( (*i
)->timestamp
<= frame
->timestamp
) break;
329 log_debug("Timestamp of last %d/%d audio frames in queue "
330 "greater then timestamp in the frame being "
331 "inserted to it (%d).", gap
, _audioFrames
.size(),
336 //log_debug("Inserting audio frame with timestamp %d", frame->timestamp);
337 _audioFrames
.insert(loc
, frame
.release());
339 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
340 // if the push reaches a "buffer full" condition, or if we find the parsing
341 // to be completed, wait to be waken up
347 MediaParser::pushEncodedVideoFrame(std::auto_ptr
<EncodedVideoFrame
> frame
)
349 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
350 boost::mutex::scoped_lock
lock(_qMutex
);
353 // Find location to insert this new frame to, so that
354 // timestamps are sorted
356 VideoFrames::iterator loc
= _videoFrames
.end();
357 if ( ! _videoFrames
.empty() ) {
359 VideoFrames::reverse_iterator i
=_videoFrames
.rbegin();
360 for (VideoFrames::reverse_iterator e
=_videoFrames
.rend(); i
!=e
; ++i
)
362 if ( (*i
)->timestamp() <= frame
->timestamp() ) break;
369 log_debug("Timestamp of last %d/%d video frames in queue "
370 "greater then timestamp() in the frame being "
371 "inserted to it (%d).", gap
, _videoFrames
.size(),
376 //log_debug("Pushing video frame with timestamp %d", frame->timestamp());
377 _videoFrames
.insert(loc
, frame
.release());
379 #ifdef LOAD_MEDIA_IN_A_SEPARATE_THREAD
380 waitIfNeeded(lock
); // if the push reaches a "buffer full" condition, wait to be waken up
385 MediaParser::waitIfNeeded(boost::mutex::scoped_lock
& lock
)
387 // We hold a lock on the queue here...
388 bool pc
=parsingCompleted();
389 bool ic
=indexingCompleted();
390 bool bf
=bufferFull();
391 if (( pc
|| (bf
&& ic
)) && !parserThreadKillRequested()) // TODO: or seekRequested ?
393 #ifdef GNASH_DEBUG_MEDIAPARSER
394 log_debug("Parser thread waiting on wakeup lock, parsingComplete=%d, bufferFull=%d", pc
, bf
);
395 #endif // GNASH_DEBUG_MEDIAPARSER
396 _parserThreadWakeup
.wait(lock
);
397 #ifdef GNASH_DEBUG_MEDIAPARSER
398 log_debug("Parser thread finished waiting on wakeup lock");
399 #endif // GNASH_DEBUG_MEDIAPARSER
404 MediaParser::bufferFull() const
406 // Callers are expected to hold a lock on _qMutex
407 int bl
= getBufferLengthNoLock();
408 int bt
= getBufferTime();
409 #ifdef GNASH_DEBUG_MEDIAPARSER
410 log_debug("MediaParser::bufferFull: %d/%d", bl
, bt
);
411 #endif // GNASH_DEBUG_MEDIAPARSER
416 MediaParser::parserLoop()
418 _parserThreadStartBarrier
.wait();
419 while (!parserThreadKillRequested())
422 gnashSleep(100); // thread switch
424 // check for parsing complete
425 // TODO: have a setParsingComplete() function
426 // exposed in base class for taking care
427 // of this on appropriate time.
428 boost::mutex::scoped_lock
lock(_qMutex
);
435 MediaParser::fetchMetaTags(OrderedMetaTags
& /*tags*/, boost::uint64_t /*ts*/)
441 operator<< (std::ostream
& os
, const VideoInfo
& vi
)
443 os
<< "codec:" << vi
.codec
<< " (type " << vi
.type
<< ") - "
444 << "size:" << vi
.width
<< "x" << vi
.height
<< " - "
445 << "frameRate:" << vi
.frameRate
<< " - "
446 << "duration:" << vi
.duration
;
451 operator<< (std::ostream
& os
, const videoCodecType
& t
)
455 case VIDEO_CODEC_H263
:
458 case VIDEO_CODEC_SCREENVIDEO
:
461 case VIDEO_CODEC_VP6
:
464 case VIDEO_CODEC_VP6A
:
467 case VIDEO_CODEC_SCREENVIDEO2
:
468 os
<< "Screenvideo2";
470 case VIDEO_CODEC_H264
:
474 os
<< "unknown/invalid codec " << static_cast<int>(t
);
481 operator<< (std::ostream
& os
, const audioCodecType
& t
)
485 case AUDIO_CODEC_RAW
:
488 case AUDIO_CODEC_ADPCM
:
491 case AUDIO_CODEC_MP3
:
494 case AUDIO_CODEC_UNCOMPRESSED
:
495 os
<< "Uncompressed";
497 case AUDIO_CODEC_NELLYMOSER_8HZ_MONO
:
498 os
<< "Nellymoser 8Hz mono";
500 case AUDIO_CODEC_NELLYMOSER
:
503 case AUDIO_CODEC_AAC
:
504 os
<< "Advanced Audio Coding";
506 case AUDIO_CODEC_SPEEX
:
510 os
<< "unknown/invalid codec " << static_cast<int>(t
);
516 } // end of gnash::media namespace
517 } // end of gnash namespace