1 // Video.cpp: Draw individual video frames, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
22 #include "MovieClip.h"
24 #include "DefineVideoStreamTag.h"
27 #include "NetStream_as.h"
28 #include "NativeFunction.h"
29 #include "movie_root.h"
31 #include "MediaHandler.h" // for setting up embedded video decoder
32 #include "VideoDecoder.h" // for setting up embedded video decoder
33 #include "namedStrings.h"
34 #include "Global_as.h"
36 #include "RunResources.h"
38 // Define this to get debug logging during embedded video decoding
39 //#define DEBUG_EMBEDDED_VIDEO_DECODING
44 as_object
* getVideoInterface(as_object
& where
);
45 void attachPrototypeProperties(as_object
& o
);
46 void attachVideoInterface(as_object
& o
);
47 as_value
video_ctor(const fn_call
& fn
);
48 as_value
video_attach(const fn_call
& fn
);
49 as_value
video_clear(const fn_call
& fn
);
50 as_value
video_deblocking(const fn_call
& fn
);
51 as_value
video_smoothing(const fn_call
& fn
);
52 as_value
video_width(const fn_call
& fn
);
53 as_value
video_height(const fn_call
& fn
);
56 Video::Video(as_object
* object
,
57 const SWF::DefineVideoStreamTag
* def
, DisplayObject
* parent
)
59 DisplayObject(getRoot(*object
), object
, parent
),
62 _embeddedStream(m_def
),
63 _lastDecodedVideoFrameNum(-1),
64 _lastDecodedVideoFrame(),
70 media::MediaHandler
* mh
= getRunResources(*object
).mediaHandler();
72 LOG_ONCE( log_error(_("No Media handler registered, "
73 "won't be able to decode embedded video")) );
77 media::VideoInfo
* info
= m_def
->getVideoInfo();
79 log_error(_("No Video info in video definition"));
84 _decoder
= mh
->createVideoDecoder(*info
);
86 catch (MediaException
&e
) {
87 log_error("Could not create Video Decoder: %s", e
.what());
98 if (_ns
) return _ns
->videoWidth();
103 Video::height() const
105 if (_ns
) return _ns
->videoHeight();
112 // Clear the current image only if paused.
113 if (_ns
&& _ns
->playbackState() == PlayHead::PLAY_PAUSED
)
116 _lastDecodedVideoFrame
.reset();
121 Video::display(Renderer
& renderer
)
125 SWFMatrix m
= getWorldMatrix();
126 const SWFRect
& bounds
= m_def
->bounds();
128 GnashImage
* img
= getVideoFrame();
131 renderer
.drawVideoFrame(img
, &m
, &bounds
, _smoothing
);
138 Video::getVideoFrame()
142 // If this is a video from a NetStream_as object, retrieve a video
146 std::auto_ptr
<GnashImage
> tmp
= _ns
->get_video();
147 if ( tmp
.get() ) _lastDecodedVideoFrame
= tmp
;
150 // If this is a video from a VideoFrame tag, retrieve a video frame
152 else if (_embeddedStream
)
155 // Don't try to do anything if there is no decoder. If it was
156 // never constructed (most likely), we'll return nothing,
157 // otherwise the last decoded frame.
158 if (!_decoder
.get()) return _lastDecodedVideoFrame
.get();
160 int current_frame
= get_ratio();
162 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
163 log_debug("Video instance %s need display video frame (ratio) %d",
164 getTarget(), current_frame
);
167 // If current frame is the same then last decoded
168 // we don't need to decode more
169 if ( _lastDecodedVideoFrameNum
== current_frame
)
171 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
172 log_debug(" current frame == _lastDecodedVideoFrameNum (%d)",
175 return _lastDecodedVideoFrame
.get();
178 int from_frame
= _lastDecodedVideoFrameNum
< 0 ?
179 0 : _lastDecodedVideoFrameNum
+ 1;
181 // If current frame is smaller then last decoded frame
182 // we restart decoding from scratch
183 if ( current_frame
< _lastDecodedVideoFrameNum
)
185 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
186 log_debug(" current frame (%d) < _lastDecodedVideoFrameNum (%d)",
187 current_frame
, _lastDecodedVideoFrameNum
);
192 // Reset last decoded video frame number now, so it's correct
193 // on early return (ie: nothing more to decode)
194 _lastDecodedVideoFrameNum
= current_frame
;
196 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
197 log_debug(" decoding embedded frames from %d to %d for Video "
198 "object %s", from_frame
, current_frame
, getTarget());
201 typedef SWF::DefineVideoStreamTag::EmbeddedFrames EncodedFrames
;
203 EncodedFrames toDecode
;
204 m_def
->getEncodedFrameSlice(from_frame
, current_frame
, toDecode
);
206 // Nothing more to decode, return last decoded (possibly null)
207 if ( toDecode
.empty() )
209 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
210 log_debug(" no defined frames, we'll return last one");
212 return _lastDecodedVideoFrame
.get();
215 for (EncodedFrames::iterator it
=toDecode
.begin(),
216 itEnd
=toDecode
.end(); it
!=itEnd
; ++it
)
218 media::EncodedVideoFrame
* frame
= *it
;
219 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
220 log_debug(" pushing frame %d to decoder", frame
->frameNum());
222 _decoder
->push(*frame
);
225 _lastDecodedVideoFrame
= _decoder
->pop();
228 return _lastDecodedVideoFrame
.get();
232 Video::construct(as_object
* /*init*/)
234 // For soft references.
235 saveOriginalTarget();
240 Video::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
242 if (!force
&& !invalidated()) return; // no need to redraw
244 ranges
.add(m_old_invalidated_ranges
);
249 bounds
.expand_to_transformed_rect(getWorldMatrix(), m_def
->bounds());
251 ranges
.add(bounds
.getRange());
255 Video::setStream(NetStream_as
* ns
)
258 _ns
->setInvalidatedVideo(this);
261 // extern (used by Global.cpp)
263 video_class_init(as_object
& global
, const ObjectURI
& uri
)
265 // This is going to be the global Video "class"/"function"
266 Global_as
& gl
= getGlobal(global
);
267 as_object
* proto
= gl
.createObject();
268 as_object
* cl
= gl
.createClass(&video_ctor
, proto
);
269 attachVideoInterface(*proto
);
271 // Register _global.Video
272 global
.init_member(uri
, cl
, as_object::DefaultFlags
);
276 registerVideoNative(as_object
& global
)
278 VM
& vm
= getVM(global
);
279 vm
.registerNative(video_ctor
, 667, 0);
280 vm
.registerNative(video_attach
, 667, 1);
281 vm
.registerNative(video_clear
, 667, 2);
285 Video::getBounds() const
287 if (_embeddedStream
) return m_def
->bounds();
289 // TODO: return the bounds of the dynamically
290 // loaded video if not embedded ?
295 Video::markOwnResources() const
297 if (_ns
) _ns
->setReachable();
301 createVideoObject(Global_as
& gl
)
303 // TODO: how to use this for AS3 as well?
304 // Turn into constructBuiltin()
305 as_object
* obj
= getObjectWithPrototype(gl
, NSV::CLASS_VIDEO
);
306 as_object
* proto
= obj
->get_prototype();
307 if (proto
) attachPrototypeProperties(*proto
);
314 attachVideoInterface(as_object
& o
)
317 o
.init_member("attachVideo", vm
.getNative(667, 1));
318 o
.init_member("clear", vm
.getNative(667, 2));
322 attachPrototypeProperties(as_object
& proto
)
324 const int protect
= PropFlags::dontDelete
;
326 proto
.init_property("deblocking", &video_deblocking
, &video_deblocking
,
328 proto
.init_property("smoothing", &video_smoothing
, &video_smoothing
,
331 const int flags
= PropFlags::dontDelete
|
334 proto
.init_property("height", &video_height
, &video_height
, flags
);
335 proto
.init_property("width", &video_width
, &video_width
, flags
);
339 video_attach(const fn_call
& fn
)
341 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
345 IF_VERBOSE_ASCODING_ERRORS(
346 log_aserror(_("attachVideo needs 1 arg"));
351 as_object
* obj
= fn
.arg(0).to_object(getGlobal(fn
));
354 if (isNativeType(obj
, ns
)) {
355 video
->setStream(ns
);
358 IF_VERBOSE_ASCODING_ERRORS(
359 log_aserror(_("attachVideo(%s) first arg is not a NetStream instance"),
367 video_deblocking(const fn_call
& fn
)
369 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
372 log_unimpl("Video.deblocking");
377 video_smoothing(const fn_call
& fn
)
379 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
381 if (!fn
.nargs
) return as_value(video
->smoothing());
383 bool smooth
= fn
.arg(0).to_bool();
385 video
->setSmoothing(smooth
);
391 video_width(const fn_call
& fn
)
393 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
394 return as_value(video
->width());
398 video_height(const fn_call
& fn
)
400 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
401 return as_value(video
->height());
405 video_clear(const fn_call
& fn
)
407 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
414 video_ctor(const fn_call
& /* fn */)
419 } // anonymous namespace
421 } // end of namespace gnash