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"
37 #include "Transform.h"
39 // Define this to get debug logging during embedded video decoding
40 //#define DEBUG_EMBEDDED_VIDEO_DECODING
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();
81 _decoder
= mh
->createVideoDecoder(*info
);
83 catch (const MediaException
& e
) {
84 log_error("Could not create Video Decoder: %s", e
.what());
95 if (_ns
) return _ns
->videoWidth();
100 Video::height() const
102 if (_ns
) return _ns
->videoHeight();
109 // Clear the current image only if paused.
110 if (_ns
&& _ns
->playbackState() == PlayHead::PLAY_PAUSED
)
113 _lastDecodedVideoFrame
.reset();
118 Video::display(Renderer
& renderer
, const Transform
& base
)
122 const DisplayObject::MaskRenderer
mr(renderer
, *this);
124 const Transform xform
= base
* transform();
125 const SWFRect
& bounds
= m_def
->bounds();
127 image::GnashImage
* img
= getVideoFrame();
129 renderer
.drawVideoFrame(img
, xform
, &bounds
, _smoothing
);
136 Video::getVideoFrame()
140 // 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
)
153 // Don't try to do anything if there is no decoder. If it was
154 // never constructed (most likely), we'll return nothing,
155 // otherwise the last decoded frame.
156 if (!_decoder
.get()) {
157 LOG_ONCE( log_error(_("No Video info in video definition")) );
158 return _lastDecodedVideoFrame
.get();
161 int current_frame
= get_ratio();
163 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
164 log_debug("Video instance %s need display video frame (ratio) %d",
165 getTarget(), current_frame
);
168 // If current frame is the same then last decoded
169 // we don't need to decode more
170 if ( _lastDecodedVideoFrameNum
== current_frame
)
172 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
173 log_debug(" current frame == _lastDecodedVideoFrameNum (%d)",
176 return _lastDecodedVideoFrame
.get();
179 int from_frame
= _lastDecodedVideoFrameNum
< 0 ?
180 0 : _lastDecodedVideoFrameNum
+ 1;
182 // If current frame is smaller then last decoded frame
183 // we restart decoding from scratch
184 if ( current_frame
< _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 typedef SWF::DefineVideoStreamTag::EmbeddedFrames EncodedFrames
;
204 EncodedFrames toDecode
;
205 m_def
->getEncodedFrameSlice(from_frame
, current_frame
, toDecode
);
207 // Nothing more to decode, return last decoded (possibly null)
208 if ( toDecode
.empty() )
210 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
211 log_debug(" no defined frames, we'll return last one");
213 return _lastDecodedVideoFrame
.get();
216 for (EncodedFrames::iterator it
=toDecode
.begin(),
217 itEnd
=toDecode
.end(); it
!=itEnd
; ++it
)
219 media::EncodedVideoFrame
* frame
= *it
;
220 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
221 log_debug(" pushing frame %d to decoder", frame
->frameNum());
223 _decoder
->push(*frame
);
226 _lastDecodedVideoFrame
= _decoder
->pop();
229 return _lastDecodedVideoFrame
.get();
233 Video::construct(as_object
* /*init*/)
235 // For soft references.
236 saveOriginalTarget();
241 Video::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
243 if (!force
&& !invalidated()) return; // no need to redraw
245 ranges
.add(m_old_invalidated_ranges
);
250 bounds
.expand_to_transformed_rect(getWorldMatrix(*this), m_def
->bounds());
252 ranges
.add(bounds
.getRange());
256 Video::setStream(NetStream_as
* ns
)
259 _ns
->setInvalidatedVideo(this);
262 // extern (used by Global.cpp)
264 video_class_init(as_object
& global
, const ObjectURI
& uri
)
266 // This is going to be the global Video "class"/"function"
267 Global_as
& gl
= getGlobal(global
);
268 as_object
* proto
= createObject(gl
);
269 as_object
* cl
= gl
.createClass(&video_ctor
, proto
);
270 attachVideoInterface(*proto
);
272 // Register _global.Video
273 global
.init_member(uri
, cl
, as_object::DefaultFlags
);
277 registerVideoNative(as_object
& global
)
279 VM
& vm
= getVM(global
);
280 vm
.registerNative(video_ctor
, 667, 0);
281 vm
.registerNative(video_attach
, 667, 1);
282 vm
.registerNative(video_clear
, 667, 2);
286 Video::getBounds() const
288 if (_embeddedStream
) return m_def
->bounds();
290 // TODO: return the bounds of the dynamically
291 // loaded video if not embedded ?
296 Video::markOwnResources() const
298 if (_ns
) _ns
->setReachable();
302 createVideoObject(Global_as
& gl
)
304 // TODO: how to use this for AS3 as well?
305 // Turn into constructBuiltin()
306 as_object
* obj
= getObjectWithPrototype(gl
, NSV::CLASS_VIDEO
);
307 as_object
* proto
= obj
->get_prototype();
308 if (proto
) attachPrototypeProperties(*proto
);
315 attachVideoInterface(as_object
& o
)
318 o
.init_member("attachVideo", vm
.getNative(667, 1));
319 o
.init_member("clear", vm
.getNative(667, 2));
323 attachPrototypeProperties(as_object
& proto
)
325 const int protect
= PropFlags::dontDelete
;
327 proto
.init_property("deblocking", &video_deblocking
, &video_deblocking
,
329 proto
.init_property("smoothing", &video_smoothing
, &video_smoothing
,
332 const int flags
= PropFlags::dontDelete
|
335 proto
.init_property("height", &video_height
, &video_height
, flags
);
336 proto
.init_property("width", &video_width
, &video_width
, flags
);
340 video_attach(const fn_call
& fn
)
342 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
346 IF_VERBOSE_ASCODING_ERRORS(
347 log_aserror(_("attachVideo needs 1 arg"));
352 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
355 if (isNativeType(obj
, ns
)) {
356 video
->setStream(ns
);
359 IF_VERBOSE_ASCODING_ERRORS(
360 log_aserror(_("attachVideo(%s) first arg is not a NetStream instance"),
368 video_deblocking(const fn_call
& fn
)
370 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
373 log_unimpl("Video.deblocking");
378 video_smoothing(const fn_call
& fn
)
380 Video
* video
= ensure
<IsDisplayObject
<Video
> >(fn
);
382 if (!fn
.nargs
) return as_value(video
->smoothing());
384 const bool smooth
= toBool(fn
.arg(0), getVM(fn
));
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