big merge from master, fix rpm creation, drop fetching swfdec
[gnash.git] / libcore / Video.cpp
blob928873551bcaf708cce5d3875e8add5c0265bf9b
1 // Video.cpp: Draw individual video frames, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 Free Software Foundation, Inc
5 //
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.
10 //
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
19 //
21 #include "Video.h"
23 #include <boost/bind.hpp>
24 #include <cassert>
26 #include "MovieClip.h"
27 #include "DefineVideoStreamTag.h"
28 #include "fn_call.h"
29 #include "as_value.h"
30 #include "NetStream_as.h"
31 #include "NativeFunction.h"
32 #include "movie_root.h"
33 #include "VM.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"
38 #include "Renderer.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
45 namespace gnash {
47 namespace {
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),
63 m_def(def),
64 _ns(0),
65 _embeddedStream(m_def),
66 _lastDecodedVideoFrameNum(-1),
67 _lastDecodedVideoFrame(),
68 _smoothing(false)
70 assert(object);
71 assert(def);
73 media::MediaHandler* mh = getRunResources(*object).mediaHandler();
74 if (!mh) {
75 LOG_ONCE( log_error(_("No Media handler registered, "
76 "won't be able to decode embedded video")) );
77 return;
80 media::VideoInfo* info = m_def->getVideoInfo();
81 if (!info) return;
83 try {
84 _decoder = mh->createVideoDecoder(*info);
86 catch (const MediaException& e) {
87 log_error("Could not create Video Decoder: %s", e.what());
91 Video::~Video()
95 int
96 Video::width() const
98 if (_ns) return _ns->videoWidth();
99 return 0;
103 Video::height() const
105 if (_ns) return _ns->videoHeight();
106 return 0;
109 void
110 Video::clear()
112 // Clear the current image only if paused.
113 if (_ns && _ns->playbackState() == PlayHead::PLAY_PAUSED)
115 set_invalidated();
116 _lastDecodedVideoFrame.reset();
120 void
121 Video::display(Renderer& renderer, const Transform& base)
123 assert(m_def);
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();
131 if (img) {
132 renderer.drawVideoFrame(img, xform, &bounds, _smoothing);
135 clear_invalidated();
138 image::GnashImage*
139 Video::getVideoFrame()
141 // If this is a video from a NetStream_as object, retrieve a video
142 // frame from there.
143 if (_ns) {
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
149 // from there.
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);
165 #endif
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)",
173 current_frame);
174 #endif
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);
189 #endif
190 from_frame = 0;
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());
200 #endif
202 const size_t frames = m_def->visitSlice(
203 boost::bind(boost::mem_fn(&media::VideoDecoder::push),
204 _decoder.get(), _1),
205 from_frame, current_frame);
207 if (!frames) return _lastDecodedVideoFrame.get();
209 _lastDecodedVideoFrame = _decoder->pop();
212 return _lastDecodedVideoFrame.get();
215 void
216 Video::construct(as_object* /*init*/)
218 // For soft references.
219 saveOriginalTarget();
223 void
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);
230 assert(m_def);
232 SWFRect bounds;
233 bounds.expand_to_transformed_rect(getWorldMatrix(*this), m_def->bounds());
235 ranges.add(bounds.getRange());
238 void
239 Video::setStream(NetStream_as* ns)
241 _ns = ns;
242 _ns->setInvalidatedVideo(this);
245 // extern (used by Global.cpp)
246 void
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);
259 void
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);
268 SWFRect
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 ?
275 return SWFRect();
278 void
279 Video::markOwnResources() const
281 if (_ns) _ns->setReachable();
284 as_object*
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);
292 return obj;
295 namespace {
297 void
298 attachVideoInterface(as_object& o)
300 VM& vm = getVM(o);
301 o.init_member("attachVideo", vm.getNative(667, 1));
302 o.init_member("clear", vm.getNative(667, 2));
305 void
306 attachPrototypeProperties(as_object& proto)
308 const int protect = PropFlags::dontDelete;
310 proto.init_property("deblocking", &video_deblocking, &video_deblocking,
311 protect);
312 proto.init_property("smoothing", &video_smoothing, &video_smoothing,
313 protect);
315 const int flags = PropFlags::dontDelete |
316 PropFlags::readOnly;
318 proto.init_property("height", &video_height, &video_height, flags);
319 proto.init_property("width", &video_width, &video_width, flags);
322 as_value
323 video_attach(const fn_call& fn)
325 Video* video = ensure<IsDisplayObject<Video> >(fn);
327 if (fn.nargs < 1)
329 IF_VERBOSE_ASCODING_ERRORS(
330 log_aserror(_("attachVideo needs 1 arg"));
332 return as_value();
335 as_object* obj = toObject(fn.arg(0), getVM(fn));
336 NetStream_as* ns;
338 if (isNativeType(obj, ns)) {
339 video->setStream(ns);
341 else {
342 IF_VERBOSE_ASCODING_ERRORS(
343 log_aserror(_("attachVideo(%s) first arg is not a NetStream instance"),
344 fn.arg(0));
347 return as_value();
350 as_value
351 video_deblocking(const fn_call& fn)
353 Video* video = ensure<IsDisplayObject<Video> >(fn);
354 UNUSED(video);
356 log_unimpl("Video.deblocking");
357 return as_value();
360 as_value
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);
370 return as_value();
373 as_value
374 video_width(const fn_call& fn)
376 Video* video = ensure<IsDisplayObject<Video> >(fn);
377 return as_value(video->width());
380 as_value
381 video_height(const fn_call& fn)
383 Video* video = ensure<IsDisplayObject<Video> >(fn);
384 return as_value(video->height());
387 as_value
388 video_clear(const fn_call& fn)
390 Video* video = ensure<IsDisplayObject<Video> >(fn);
392 video->clear();
393 return as_value();
396 as_value
397 video_ctor(const fn_call& /* fn */)
399 return as_value();
402 } // anonymous namespace
404 } // end of namespace gnash