1 // Video.cpp:  Draw individual video frames, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   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.
15 //
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 //
20 
21 #include "Video.h"
22 
23 #include <functional>
24 #include <cassert>
25 
26 #include "DefineVideoStreamTag.h"
27 #include "NetStream_as.h"
28 #include "as_object.h"
29 #include "VM.h"
30 #include "MediaHandler.h" // for setting up embedded video decoder
31 #include "VideoDecoder.h" // for setting up embedded video decoder
32 #include "Renderer.h"
33 #include "RunResources.h"
34 #include "Transform.h"
35 
36 // Define this to get debug logging during embedded video decoding
37 //#define DEBUG_EMBEDDED_VIDEO_DECODING
38 
39 namespace gnash {
40 
Video(as_object * object,const SWF::DefineVideoStreamTag * def,DisplayObject * parent)41 Video::Video(as_object* object,
42         const SWF::DefineVideoStreamTag* def, DisplayObject* parent)
43 	:
44 	DisplayObject(getRoot(*object), object, parent),
45 	m_def(def),
46 	_ns(nullptr),
47 	_embeddedStream(m_def),
48 	_lastDecodedVideoFrameNum(-1),
49 	_lastDecodedVideoFrame(),
50     _smoothing(false)
51 {
52     assert(object);
53     assert(def);
54 
55     media::MediaHandler* mh = getRunResources(*object).mediaHandler();
56 	if (!mh) {
57 		LOG_ONCE(log_error(_("No Media handler registered, "
58 			"won't be able to decode embedded video")) );
59 		return;
60 	}
61 
62 	media::VideoInfo* info = m_def->getVideoInfo();
63 	if (!info) return;
64 
65     try {
66 	    _decoder = mh->createVideoDecoder(*info);
67 	}
68 	catch (const MediaException& e) {
69 	    log_error(_("Could not create Video Decoder: %s"), e.what());
70 	}
71 }
72 
~Video()73 Video::~Video()
74 {
75 }
76 
77 int
width() const78 Video::width() const
79 {
80     if (_ns) return _ns->videoWidth();
81     return 0;
82 }
83 
84 int
height() const85 Video::height() const
86 {
87     if (_ns) return _ns->videoHeight();
88     return 0;
89 }
90 
91 void
clear()92 Video::clear()
93 {
94     // Clear the current image only if paused.
95     if (_ns && _ns->playbackState() == PlayHead::PLAY_PAUSED)
96     {
97         set_invalidated();
98         _lastDecodedVideoFrame.reset();
99     }
100 }
101 
102 void
display(Renderer & renderer,const Transform & base)103 Video::display(Renderer& renderer, const Transform& base)
104 {
105 	assert(m_def);
106 
107     const DisplayObject::MaskRenderer mr(renderer, *this);
108 
109     const Transform xform = base * transform();
110 	const SWFRect& bounds = m_def->bounds();
111 
112     image::GnashImage* img = getVideoFrame();
113 	if (img) {
114 		renderer.drawVideoFrame(img, xform, &bounds, _smoothing);
115 	}
116 
117 	clear_invalidated();
118 }
119 
120 image::GnashImage*
getVideoFrame()121 Video::getVideoFrame()
122 {
123 	// If this is a video from a NetStream_as object, retrieve a video
124     // frame from there.
125 	if (_ns) {
126 		std::unique_ptr<image::GnashImage> tmp = _ns->get_video();
127 		if (tmp.get()) _lastDecodedVideoFrame = std::move(tmp);
128 	}
129 
130 	// If this is a video from a VideoFrame tag, retrieve a video frame
131     // from there.
132 	else if (_embeddedStream) {
133 
134         // Don't try to do anything if there is no decoder. If it was
135         // never constructed (most likely), we'll return nothing,
136         // otherwise the last decoded frame.
137         if (!_decoder.get()) {
138 		    LOG_ONCE(log_error(_("No Video info in video definition")));
139             return _lastDecodedVideoFrame.get();
140         }
141 
142         const std::uint16_t current_frame = get_ratio();
143 
144 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
145         log_debug("Video instance %s need display video frame (ratio) %d",
146 			getTarget(), current_frame);
147 #endif
148 
149 		// If current frame is the same then last decoded
150 		// we don't need to decode more
151 		if (_lastDecodedVideoFrameNum >= 0 &&
152                 _lastDecodedVideoFrameNum == current_frame) {
153 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
154                     log_debug("  current frame == _lastDecodedVideoFrameNum (%d)",
155                     current_frame);
156 #endif
157 			return _lastDecodedVideoFrame.get();
158 		}
159 
160         // TODO: find a better way than using -1 to show that no
161         // frames have been decoded yet.
162         assert(_lastDecodedVideoFrameNum >= -1);
163         std::uint16_t from_frame = _lastDecodedVideoFrameNum + 1;
164 
165 		// If current frame is smaller then last decoded frame
166 		// we restart decoding from scratch
167 		if (current_frame < static_cast<size_t>(_lastDecodedVideoFrameNum)) {
168 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
169                     log_debug("  current frame (%d) < _lastDecodedVideoFrameNum (%d)",
170                     current_frame, _lastDecodedVideoFrameNum);
171 #endif
172 			from_frame = 0;
173 		}
174 
175 		// Reset last decoded video frame number now, so it's correct
176 		// on early return (ie: nothing more to decode)
177 		_lastDecodedVideoFrameNum = current_frame;
178 
179 #ifdef DEBUG_EMBEDDED_VIDEO_DECODING
180 		log_debug("  decoding embedded frames from %d to %d "
181                           "for Video object %s", from_frame,
182                           current_frame, getTarget());
183 #endif
184 
185         const size_t frames = m_def->visitSlice(
186                 std::bind(std::mem_fn(&media::VideoDecoder::push),
187                     _decoder.get(), std::placeholders::_1),
188                 from_frame, current_frame);
189 
190         if (!frames) return _lastDecodedVideoFrame.get();
191 
192 		_lastDecodedVideoFrame = _decoder->pop();
193 	}
194 
195 	return _lastDecodedVideoFrame.get();
196 }
197 
198 void
construct(as_object *)199 Video::construct(as_object* /*init*/)
200 {
201     // For soft references.
202     saveOriginalTarget();
203 }
204 
205 void
add_invalidated_bounds(InvalidatedRanges & ranges,bool force)206 Video::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
207 {
208 	if (!force && !invalidated()) return; // no need to redraw
209 
210 	ranges.add(m_old_invalidated_ranges);
211 
212 	assert(m_def);
213 
214 	SWFRect bounds;
215 	bounds.expand_to_transformed_rect(getWorldMatrix(*this), m_def->bounds());
216 
217 	ranges.add(bounds.getRange());
218 }
219 
220 void
setStream(NetStream_as * ns)221 Video::setStream(NetStream_as* ns)
222 {
223 	_ns = ns;
224 	_ns->setInvalidatedVideo(this);
225 }
226 
227 SWFRect
getBounds() const228 Video::getBounds() const
229 {
230 	if (_embeddedStream) return m_def->bounds();
231 
232 	// TODO: return the bounds of the dynamically
233 	//       loaded video if not embedded ?
234 	return SWFRect();
235 }
236 
237 void
markOwnResources() const238 Video::markOwnResources() const
239 {
240 	if (_ns) _ns->setReachable();
241 }
242 
243 } // namespace gnash
244 
245