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