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
23 #include <boost/bind.hpp>
26 #include "MovieClip.h"
27 #include "DefineVideoStreamTag.h"
30 #include "NetStream_as.h"
31 #include "NativeFunction.h"
32 #include "movie_root.h"
34 #include "MediaHandler.h" // for setting up embedded video decoder
35 #include "VideoDecoder.h" // for setting up embedded video decoder
36 #include "namedStrings.h"
37 #include "Global_as.h"
39 #include "RunResources.h"
40 #include "Transform.h"
42 // Define this to get debug logging during embedded video decoding
43 //#define DEBUG_EMBEDDED_VIDEO_DECODING
48 void attachPrototypeProperties(as_object
& o
);
49 void attachVideoInterface(as_object
& o
);
50 as_value
video_ctor(const fn_call
& fn
);
51 as_value
video_attach(const fn_call
& fn
);
52 as_value
video_clear(const fn_call
& fn
);
53 as_value
video_deblocking(const fn_call
& fn
);
54 as_value
video_smoothing(const fn_call
& fn
);
55 as_value
video_width(const fn_call
& fn
);
56 as_value
video_height(const fn_call
& fn
);
59 Video::Video(as_object
* object
,
60 const SWF::DefineVideoStreamTag
* def
, DisplayObject
* parent
)
62 DisplayObject(getRoot(*object
), object
, parent
),
65 _embeddedStream(m_def
),
66 _lastDecodedVideoFrameNum(-1),
67 _lastDecodedVideoFrame(),
73 media::MediaHandler
* mh
= getRunResources(*object
).mediaHandler();
75 LOG_ONCE( log_error(_("No Media handler registered, "
76 "won't be able to decode embedded video")) );
80 media::VideoInfo
* info
= m_def
->getVideoInfo();
84 _decoder
= mh
->createVideoDecoder(*info
);
86 catch (const 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
, const Transform
& base
)
125 const DisplayObject::MaskRenderer
mr(renderer
, *this);
127 const Transform xform
= base
* transform();
128 const SWFRect
& bounds
= m_def
->bounds();
130 image::GnashImage
* img
= getVideoFrame();
132 renderer
.drawVideoFrame(img
, xform
, &bounds
, _smoothing
);
139 Video::getVideoFrame()
141 // If this is a video from a NetStream_as object, retrieve a video
144 std::auto_ptr
<image::GnashImage
> tmp
= _ns
->get_video();
145 if (tmp
.get()) _lastDecodedVideoFrame
= tmp
;
148 // If this is a video from a VideoFrame tag, retrieve a video frame
150 else if (_embeddedStream
) {
152 // Don't try to do anything if there is no decoder. If it was
153 // never constructed (most likely), we'll return nothing,
154 // otherwise the last decoded frame.
155 if (!_decoder
.get()) {
156 LOG_ONCE(log_error(_("No Video info in video definition")));
157 return _lastDecodedVideoFrame
.get();
160 const boost::uint16_t 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
>= 0 &&
170 _lastDecodedVideoFrameNum
== current_frame
) {
171 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
172 log_debug(" current frame == _lastDecodedVideoFrameNum (%d)",
175 return _lastDecodedVideoFrame
.get();
178 // TODO: find a better way than using -1 to show that no
179 // frames have been decoded yet.
180 assert(_lastDecodedVideoFrameNum
>= -1);
181 boost::uint16_t from_frame
= _lastDecodedVideoFrameNum
+ 1;
183 // If current frame is smaller then last decoded frame
184 // we restart decoding from scratch
185 if (current_frame
< static_cast<size_t>(_lastDecodedVideoFrameNum
)) {
186 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
187 log_debug(" current frame (%d) < _lastDecodedVideoFrameNum (%d)",
188 current_frame
, _lastDecodedVideoFrameNum
);
193 // Reset last decoded video frame number now, so it's correct
194 // on early return (ie: nothing more to decode)
195 _lastDecodedVideoFrameNum
= current_frame
;
197 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
198 log_debug(" decoding embedded frames from %d to %d for Video "
199 "object %s", from_frame
, current_frame
, getTarget());
202 const size_t frames
= m_def
->visitSlice(
203 boost::bind(boost::mem_fn(&media::VideoDecoder::push
),
205 from_frame
, current_frame
);
207 if (!frames
) return _lastDecodedVideoFrame
.get();
209 _lastDecodedVideoFrame
= _decoder
->pop();
212 return _lastDecodedVideoFrame
.get();
216 Video::construct(as_object
* /*init*/)
218 // For soft references.
219 saveOriginalTarget();
224 Video::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
226 if (!force
&& !invalidated()) return; // no need to redraw
228 ranges
.add(m_old_invalidated_ranges
);
233 bounds
.expand_to_transformed_rect(getWorldMatrix(*this), m_def
->bounds());
235 ranges
.add(bounds
.getRange());
239 Video::setStream(NetStream_as
* ns
)
242 _ns
->setInvalidatedVideo(this);
245 // extern (used by Global.cpp)
247 video_class_init(as_object
& global
, const ObjectURI
& uri
)
249 // This is going to be the global Video "class"/"function"
250 Global_as
& gl
= getGlobal(global
);
251 as_object
* proto
= createObject(gl
);
252 as_object
* cl
= gl
.createClass(&video_ctor
, proto
);
253 attachVideoInterface(*proto
);
255 // Register _global.Video
256 global
.init_member(uri
, cl
, as_object::DefaultFlags
);
260 registerVideoNative(as_object
& global
)
262 VM
& vm
= getVM(global
);
263 vm
.registerNative(video_ctor
, 667, 0);
264 vm
.registerNative(video_attach
, 667, 1);
265 vm
.registerNative(video_clear
, 667, 2);
269 Video::getBounds() const
271 if (_embeddedStream
) return m_def
->bounds();
273 // TODO: return the bounds of the dynamically
274 // loaded video if not embedded ?
279 Video::markOwnResources() const
281 if (_ns
) _ns
->setReachable();
285 createVideoObject(Global_as
& gl
)
287 // TODO: how to use this for AS3 as well?
288 // Turn into constructBuiltin()
289 as_object
* obj
= getObjectWithPrototype(gl
, NSV::CLASS_VIDEO
);
290 as_object
* proto
= obj
->get_prototype();
291 if (proto
) attachPrototypeProperties(*proto
);
298 attachVideoInterface(as_object
& o
)
301 o
.init_member("attachVideo", vm
.getNative(667, 1));
302 o
.init_member("clear", vm
.getNative(667, 2));
306 attachPrototypeProperties(as_object
& proto
)
308 const int protect
= PropFlags::dontDelete
;
310 proto
.init_property("deblocking", &video_deblocking
, &video_deblocking
,
312 proto
.init_property("smoothing", &video_smoothing
, &video_smoothing
,
315 const int flags
= PropFlags::dontDelete
|
318 proto
.init_property("height", &video_height
, &video_height
, flags
);
319 proto
.init_property("width", &video_width
, &video_width
, flags
);
323 video_attach(const fn_call
& fn
)
325 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
329 IF_VERBOSE_ASCODING_ERRORS(
330 log_aserror(_("attachVideo needs 1 arg"));
335 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
338 if (isNativeType(obj
, ns
)) {
339 video
->setStream(ns
);
342 IF_VERBOSE_ASCODING_ERRORS(
343 log_aserror(_("attachVideo(%s) first arg is not a NetStream instance"),
351 video_deblocking(const fn_call
& fn
)
353 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
356 log_unimpl("Video.deblocking");
361 video_smoothing(const fn_call
& fn
)
363 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
365 if (!fn
.nargs
) return as_value(video
->smoothing());
367 const bool smooth
= toBool(fn
.arg(0), getVM(fn
));
368 video
->setSmoothing(smooth
);
374 video_width(const fn_call
& fn
)
376 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
377 return as_value(video
->width());
381 video_height(const fn_call
& fn
)
383 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
384 return as_value(video
->height());
388 video_clear(const fn_call
& fn
)
390 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
397 video_ctor(const fn_call
& /* fn */)
402 } // anonymous namespace
404 } // end of namespace gnash