2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 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
20 #ifndef GNASH_NETSTREAM_H
21 #define GNASH_NETSTREAM_H
24 #ifndef __STDC_CONSTANT_MACROS
25 #define __STDC_CONSTANT_MACROS
28 #include "MediaParser.h"
29 #include "PlayHead.h" // for composition
31 #include "VideoDecoder.h" // for visibility of dtor
32 #include "AudioDecoder.h" // for visibility of dtor
34 #include "VirtualClock.h"
36 #include "Relay.h" // for ActiveRelay inheritance
38 #include <boost/intrusive_ptr.hpp>
41 #include <boost/scoped_ptr.hpp>
43 // Forward declarations
47 class NetConnection_as
;
62 /// Buffered AudioStreamer
64 /// This class you create passing a sound handler, which will
65 /// be used to implement attach/detach and eventually throw away
66 /// buffers of sound when no sound handler is given.
68 /// Then you push samples to a buffer of it and can request attach/detach
69 /// operations. When attached, the sound handler will fetch samples
70 /// from the buffer, in a thread-safe way.
72 class BufferedAudioStreamer
{
76 /// %Sound handler to use for attach/detach
78 BufferedAudioStreamer(sound::sound_handler
* handler
);
80 /// A buffer with a cursor state
82 /// @todo Make private, have ::push take a simpler
100 /// Number of samples left in buffer starting from cursor
101 boost::uint32_t m_size
;
105 /// The data must be allocated with new []
106 /// as will be delete []'d by the dtor
107 boost::uint8_t* m_data
;
109 /// Cursor into the data
110 boost::uint8_t* m_ptr
;
113 typedef std::deque
<CursoredBuffer
*> AudioQueue
;
115 // Delete all samples in the audio queue.
116 void cleanAudioQueue();
118 sound::sound_handler
* _soundHandler
;
120 /// This is where audio frames are pushed by ::advance
121 /// and consumed by sound_handler callback (audio_streamer)
122 AudioQueue _audioQueue
;
124 /// Number of bytes in the audio queue, protected by _audioQueueMutex
125 size_t _audioQueueSize
;
127 /// The queue needs to be protected as sound_handler callback
128 /// is invoked by a separate thread (dunno if it makes sense actually)
129 boost::mutex _audioQueueMutex
;
131 // Id of an attached audio streamer, 0 if none
132 sound::InputStream
* _auxStreamer
;
134 /// Attach the aux streamer.
136 /// On success, _auxStreamerAttached will be set to true.
137 /// Won't attach again if already attached.
139 void attachAuxStreamer();
141 /// Detach the aux streamer
143 /// _auxStreamerAttached will be set to true.
144 /// Won't detach if not attached.
146 void detachAuxStreamer();
148 /// Fetch samples from the audio queue
149 unsigned int fetch(boost::int16_t* samples
, unsigned int nSamples
,
152 /// Fetch samples from the audio queue
153 static unsigned int fetchWrapper(void* owner
, boost::int16_t* samples
,
154 unsigned int nSamples
, bool& eof
);
156 /// Push a buffer to the audio queue
159 /// Samples buffer, ownership transferred.
161 /// @todo: take something simpler (SimpleBuffer?)
163 void push(CursoredBuffer
* audio
);
167 // -----------------------------------------------------------------
169 /// NetStream_as ActionScript class
171 /// This class is responsible for handlign external
172 /// media files. Provides interfaces for playback control.
174 class NetStream_as
: public ActiveRelay
180 pauseModeToggle
= -1,
185 NetStream_as(as_object
* owner
);
189 PlayHead::PlaybackStatus
playbackState() const {
190 return _playHead
.getState();
193 /// Get the real height of the video in pixels if the decoder exists.
195 /// @return the height of the video in pixels or 0 if no decoder exists.
196 /// The width returned from the decoder may also vary, and will
197 /// be 0 until it knows the width.
198 int videoHeight() const;
200 /// Get the real width of the video in pixels if the decoder exists.
202 /// @return the width of the video in pixels or 0 if no decoder exists.
203 /// The width returned from the decoder may also vary, and will
204 /// be 0 until it knows the width.
205 int videoWidth() const;
207 /// Closes the video session and frees all ressources used for decoding
208 /// except the FLV-parser (this might not be correct).
211 /// Make audio controlled by given DisplayObject
212 void setAudioController(DisplayObject
* controller
);
214 /// Pauses/starts the playback of the media played by the current instance
217 /// Defines what mode to put the instance in.
218 void pause(PauseMode mode
);
220 /// Starts the playback of the media
223 /// Defines what file to play
225 void play(const std::string
& source
);
227 /// Seek in the media played by the current instance
230 /// Defines in seconds where to seek to
231 /// @todo take milliseconds !!
233 void seek(boost::uint32_t pos
);
235 /// Tells where the playhead currently is
237 /// @return The time in milliseconds of the current playhead position
239 boost::int32_t time();
241 /// Called at the SWF heart-beat. Used to process queued status messages
242 /// and (re)start after a buffering pause. In NetStreamFfmpeg it is also
243 /// used to find the next video frame to be shown, though this might
247 /// Returns the current framerate in frames per second.
248 double getCurrentFPS() { return 0; }
250 /// Sets the NetConnection needed to access external files
253 /// The NetConnection object to use for network access
255 void setNetCon(NetConnection_as
* nc
) {
259 /// Return true if the NetStream has an associated NetConnection
260 bool isConnected() const { return (_netCon
); }
262 /// Specifies the number of milliseconds to buffer before starting
263 /// to display the stream.
266 /// The time in milliseconds that should be buffered.
268 void setBufferTime(boost::uint32_t time
);
270 /// Returns what the buffer time has been set to. (100 milliseconds
273 /// @return The size of the buffer in milliseconds.
275 boost::uint32_t bufferTime() { return m_bufferTime
; }
277 /// Returns the number of bytes of the media file that have been buffered.
280 /// Returns the total number of bytes (size) of the media file
282 /// @return the total number of bytes (size) of the media file
286 /// Returns the number of millisecond of the media file that is
287 /// buffered and yet to be played
289 /// @return Returns the number of millisecond of the media file that is
290 /// buffered and yet to be played
294 /// Tells us if there is a new video frame ready
296 /// @return true if a frame is ready, false if not
297 bool newFrameReady();
299 /// Returns the video frame closest to current cursor. See time().
301 /// @return a image containing the video frame, a NULL auto_ptr if
304 std::auto_ptr
<image::GnashImage
> get_video();
306 /// Register the DisplayObject to invalidate on video updates
307 void setInvalidatedVideo(DisplayObject
* ch
)
309 _invalidatedVideoCharacter
= ch
;
312 virtual void markReachableResources() const;
314 /// Callback used by sound_handler to get audio data
316 /// This is a sound_handler::aux_streamer_ptr type.
318 /// It might be invoked by a separate thread (neither main,
319 /// nor decoder thread).
321 static unsigned int audio_streamer(void *udata
, boost::int16_t* samples
,
322 unsigned int nSamples
, bool& eof
);
326 /// Status codes used for notifications
329 // Internal status, not a valid ActionScript value
332 /// NetStream.Buffer.Empty (level: status)
335 /// NetStream.Buffer.Full (level: status)
338 /// NetStream.Buffer.Flush (level: status)
341 /// NetStream.Play.Start (level: status)
344 /// NetStream.Play.Stop (level: status)
347 /// NetStream.Seek.Notify (level: status)
350 /// NetStream.Play.StreamNotFound (level: error)
353 /// NetStream.Seek.InvalidTime (level: error)
357 NetConnection_as
* _netCon
;
359 boost::scoped_ptr
<CharacterProxy
> _audioController
;
361 /// Set stream status.
363 /// Valid statuses are:
366 /// - NetStream.Buffer.Empty
367 /// - NetStream.Buffer.Full
368 /// - NetStream.Buffer.Flush
369 /// - NetStream.Play.Start
370 /// - NetStream.Play.Stop
371 /// - NetStream.Seek.Notify
374 /// - NetStream.Play.StreamNotFound
375 /// - NetStream.Seek.InvalidTime
377 /// This method locks the statusMutex during operations
379 void setStatus(StatusCode code
);
382 /// Call any onStatus event handler passing it
383 /// any queued status change, see _statusQueue
385 /// Will NOT lock the statusMutex itself, rather it will
386 /// iteratively call the popNextPendingStatusNotification()
387 /// private method, which will take care of locking it.
388 /// This is to make sure onStatus handler won't call methods
389 /// possibly trying to obtain the lock again (::play, ::pause, ...)
391 void processStatusNotifications();
393 // The size of the buffer in milliseconds
394 boost::uint32_t m_bufferTime
;
396 // Are a new frame ready to be returned?
397 volatile bool m_newFrameReady
;
399 // Mutex to insure we don't corrupt the image
400 boost::mutex image_mutex
;
402 // The image/videoframe which is given to the renderer
403 std::auto_ptr
<image::GnashImage
> m_imageframe
;
408 // The input media parser
409 std::auto_ptr
<media::MediaParser
> m_parser
;
411 // Are we playing a FLV?
412 // The handler which is invoked on status change
413 boost::intrusive_ptr
<as_function
> _statusHandler
;
415 // The position in the inputfile, only used when not playing a FLV
418 /// Unplug the advance timer callback
419 void stopAdvanceTimer();
421 /// Register the advance timer callback
422 void startAdvanceTimer();
424 /// The DisplayObject to invalidate on video updates
425 DisplayObject
* _invalidatedVideoCharacter
;
436 typedef std::pair
<std::string
, std::string
> NetStreamStatus
;
438 /// Get 'status' (first) and 'level' (second) strings for given status code
440 /// Any invalid code, out of bound or explicitly invalid (invalidCode)
441 /// returns two empty strings.
443 void getStatusCodeInfo(StatusCode code
, NetStreamStatus
& info
);
445 /// Return a newly allocated information object for the given status
446 as_object
* getStatusObject(StatusCode code
);
448 /// Initialize video decoder and (if successful) PlayHead consumer
450 /// @param info Video codec information
452 void initVideoDecoder(const media::VideoInfo
& info
);
454 /// Initialize audio decoder and (if successful) a PlayHead consumer
456 /// @param info Audio codec information
458 void initAudioDecoder(const media::AudioInfo
& parser
);
460 // Setups the playback
461 bool startPlayback();
463 // Pauses the playhead
466 // - ::decodeFLVFrame()
470 void pausePlayback();
472 // Resumes the playback
477 // - ::startPlayback()
480 void unpausePlayback();
482 /// Update the image/videoframe to be returned by next get_video() call.
484 /// Used by update().
486 /// Note that get_video will be called by Video::display(), which
487 /// is usually called right after Video::advance(), so the result
488 /// is that refreshVideoFrame() is called right before
489 /// get_video(). This is important
490 /// to ensure timing is correct..
492 /// @param alsoIfPaused
493 /// If true, video is consumed/refreshed even if playhead is paused.
494 /// By default this is false, but will be used on ::seek (user-reguested)
496 void refreshVideoFrame(bool alsoIfPaused
= false);
498 /// Refill audio buffers, so to contain new frames since last run
499 /// and up to current timestamp
500 void refreshAudioBuffer();
502 /// Decode next video frame fetching it MediaParser cursor
504 /// @return 0 on EOF or error, a decoded video otherwise
506 std::auto_ptr
<image::GnashImage
> decodeNextVideoFrame();
508 /// Decode next audio frame fetching it MediaParser cursor
510 /// @return 0 on EOF or error, a decoded audio frame otherwise
512 BufferedAudioStreamer::CursoredBuffer
* decodeNextAudioFrame();
515 /// Decode input audio frames with timestamp <= ts
516 /// and push them to the output audio queue
517 void pushDecodedAudioFrames(boost::uint32_t ts
);
519 /// Decode input frames up to the one with timestamp <= ts.
521 /// Decoding starts from "next" element in the parser cursor.
524 /// 1. there's no parser active.
525 /// 2. parser cursor is already on last frame.
526 /// 3. next element in cursor has timestamp > tx
527 /// 4. there was an error decoding
529 std::auto_ptr
<image::GnashImage
> getDecodedVideoFrame(boost::uint32_t ts
);
531 DecodingState
decodingStatus(DecodingState newstate
= DEC_NONE
);
533 /// Parse a chunk of input
534 /// Currently blocks, ideally should parse as much
535 /// as possible w/out blocking
536 void parseNextChunk();
538 DecodingState _decoding_state
;
540 // Mutex protecting _playback_state and _decoding_state
541 // (not sure a single one is appropriate)
542 boost::mutex _state_mutex
;
545 std::auto_ptr
<media::VideoDecoder
> _videoDecoder
;
547 /// True if video info are known
548 bool _videoInfoKnown
;
551 std::auto_ptr
<media::AudioDecoder
> _audioDecoder
;
553 /// True if an audio info are known
554 bool _audioInfoKnown
;
556 /// Virtual clock used as playback clock source
557 boost::scoped_ptr
<InterruptableVirtualClock
> _playbackClock
;
559 /// Playback control device
562 // Current sound handler
563 sound::sound_handler
* _soundHandler
;
565 // Current media handler
566 media::MediaHandler
* _mediaHandler
;
570 /// This should just be a temporary variable, transferred
571 /// to MediaParser constructor.
573 std::auto_ptr
<IOChannel
> _inputStream
;
575 /// The buffered audio streamer
576 BufferedAudioStreamer _audioStreamer
;
578 /// List of status messages to be processed
579 StatusCode _statusCode
;
581 /// Mutex protecting _statusQueue
582 boost::mutex statusMutex
;
586 void netstream_class_init(as_object
& global
, const ObjectURI
& uri
);
588 void registerNetStreamNative(as_object
& global
);