1 // Copyright (c) 2010 Hewlett-Packard Development Company, L.P. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <webvfx/webvfx.h>
6 extern "C" {
7 #include <mlt/framework/mlt_log.h>
8 #include <mlt/framework/mlt_factory.h>
9 #include <mlt/framework/mlt_frame.h>
10 #include <mlt/framework/mlt_producer.h>
11 #include <mlt/framework/mlt_consumer.h>
12 }
13 #include "service_manager.h"
14
15 namespace MLTWebVfx
16 {
17
18 ////////////////////////
19
20 class ServiceParameters : public WebVfx::Parameters
21 {
22 public:
ServiceParameters(mlt_service service)23 ServiceParameters(mlt_service service)
24 : properties(MLT_SERVICE_PROPERTIES(service))
25 , position(0)
26 , length(0)
27 {
28 }
29
getNumberParameter(const QString & name)30 double getNumberParameter(const QString& name) {
31 return mlt_properties_anim_get_double(properties, name.toLatin1().constData(), position, length);
32 }
33
getStringParameter(const QString & name)34 QString getStringParameter(const QString& name) {
35 return QString::fromUtf8(mlt_properties_anim_get(properties, name.toLatin1().constData(), position, length));
36 }
37
setPositionAndLength(mlt_position newPosition,mlt_position newLength)38 void setPositionAndLength(mlt_position newPosition, mlt_position newLength)
39 {
40 position = newPosition;
41 length = newLength;
42 }
43
44
45 private:
46 mlt_properties properties;
47 mlt_position position;
48 mlt_position length;
49 };
50
51 ////////////////////////
52
53 class ImageProducer
54 {
55 public:
ImageProducer(const QString & name,mlt_producer producer)56 ImageProducer(const QString& name, mlt_producer producer)
57 : name(name)
58 , producerFrame(0)
59 , producer(producer) {}
60
~ImageProducer()61 ~ImageProducer() {
62 if (producerFrame)
63 mlt_frame_close(producerFrame);
64 mlt_producer_close(producer);
65 }
66
getName()67 const QString& getName() { return name; }
68
isPositionValid(mlt_position position)69 bool isPositionValid(mlt_position position) {
70 return position < mlt_producer_get_playtime(producer);
71 }
72
produceImage(mlt_position position,int width,int height,bool hasAlpha)73 WebVfx::Image produceImage(mlt_position position, int width, int height, bool hasAlpha) {
74 // Close previous frame and request a new one.
75 // We don't close the current frame because the image data we return
76 // needs to remain valid until we are rendered.
77 if (producerFrame) {
78 mlt_frame_close(producerFrame);
79 producerFrame = 0;
80 }
81 mlt_producer_seek(producer, position);
82 mlt_service_get_frame(MLT_PRODUCER_SERVICE(producer), &producerFrame, 0);
83
84 mlt_image_format format;
85 if (hasAlpha)
86 format = mlt_image_rgb24a;
87 else
88 format = mlt_image_rgb24;
89
90 uint8_t *image = NULL;
91 int error = mlt_frame_get_image(producerFrame, &image, &format,
92 &width, &height, 0);
93 if (error)
94 return WebVfx::Image();
95 return WebVfx::Image(image, width, height, width * height * (hasAlpha ? 4 : 3), hasAlpha);
96 }
97
98 private:
99 QString name;
100 mlt_frame producerFrame;
101 mlt_producer producer;
102 };
103
104 ////////////////////////
105
ServiceManager(mlt_service service)106 ServiceManager::ServiceManager(mlt_service service)
107 : service(service)
108 , event(0)
109 , effects(0)
110 , imageProducers(0)
111 {
112 mlt_properties_set(MLT_SERVICE_PROPERTIES(service), "factory", mlt_environment("MLT_PRODUCER"));
113 }
114
~ServiceManager()115 ServiceManager::~ServiceManager()
116 {
117 mlt_events_disconnect(event, this);
118 if (effects)
119 effects->destroy();
120
121 if (imageProducers) {
122 for (std::vector<ImageProducer*>::iterator it = imageProducers->begin();
123 it != imageProducers->end(); it++) {
124 delete *it;
125 }
126 delete imageProducers;
127 }
128 }
129
initialize(int width,int height)130 bool ServiceManager::initialize(int width, int height)
131 {
132 if (effects)
133 return true;
134
135 mlt_properties properties = MLT_SERVICE_PROPERTIES(service);
136
137 // Create and initialize Effects
138 const char* fileName = mlt_properties_get(properties, "resource");
139 if (!fileName) {
140 mlt_log(service, MLT_LOG_ERROR, "No 'resource' property found\n");
141 return false;
142 }
143 bool isTransparent = mlt_properties_get_int(properties, "transparent") || mlt_service_identify(service) == filter_type;
144 parameters = new ServiceParameters(service);
145 effects = WebVfx::createEffects(fileName, width, height,
146 parameters, isTransparent);
147 if (!effects) {
148 mlt_log(service, MLT_LOG_ERROR,
149 "Failed to create WebVfx Effects for resource %s\n", fileName);
150 return false;
151 }
152
153 // Iterate over image map - save source and target image names,
154 // and create an ImageProducer for each extra image.
155 char* factory = mlt_properties_get(properties, "factory");
156 WebVfx::Effects::ImageTypeMapIterator it(effects->getImageTypeMap());
157 while (it.hasNext()) {
158 it.next();
159
160 const QString& imageName = it.key();
161
162 switch (it.value()) {
163
164 case WebVfx::Effects::SourceImageType:
165 sourceImageName = imageName;
166 break;
167
168 case WebVfx::Effects::TargetImageType:
169 targetImageName = imageName;
170 break;
171
172 case WebVfx::Effects::ExtraImageType:
173 {
174 if (!imageProducers)
175 imageProducers = new std::vector<ImageProducer*>(3);
176
177 // Property prefix "producer.<name>."
178 QString producerPrefix("producer.");
179 producerPrefix.append(imageName).append(".");
180
181 // Find producer.<name>.resource property
182 QString resourceName(producerPrefix);
183 resourceName.append("resource");
184 char* resource = mlt_properties_get(properties, resourceName.toLatin1().constData());
185 if (resource) {
186 mlt_producer producer = mlt_factory_producer(mlt_service_profile(service), factory, resource);
187 if (!producer) {
188 mlt_log(service, MLT_LOG_ERROR, "WebVfx failed to create extra image producer for %s\n", resourceName.toLatin1().constData());
189 return false;
190 }
191 // Copy producer.<name>.* properties onto producer
192 mlt_properties_pass(MLT_PRODUCER_PROPERTIES(producer), properties, producerPrefix.toLatin1().constData());
193 // Append ImageProducer to vector
194 imageProducers->insert(imageProducers->end(), new ImageProducer(imageName, producer));
195 }
196 else
197 mlt_log(service, MLT_LOG_WARNING, "WebVfx no producer resource property specified for extra image %s\n", resourceName.toLatin1().constData());
198 break;
199 }
200
201 default:
202 mlt_log(service, MLT_LOG_ERROR, "Invalid WebVfx image type %d\n", it.value());
203 break;
204 }
205 }
206
207 return true;
208 }
209
setImageForName(const QString & name,WebVfx::Image * image)210 void ServiceManager::setImageForName(const QString& name, WebVfx::Image* image)
211 {
212 if (!name.isEmpty())
213 effects->setImage(name, image);
214 }
215
216
consumerStoppingListener(mlt_properties owner,ServiceManager * self)217 static void consumerStoppingListener(mlt_properties owner, ServiceManager* self)
218 {
219 Q_UNUSED(owner);
220 self->onConsumerStopping();
221 }
222
render(WebVfx::Image * outputImage,mlt_position position,mlt_position length,bool hasAlpha)223 int ServiceManager::render(WebVfx::Image* outputImage, mlt_position position, mlt_position length, bool hasAlpha)
224 {
225 double time = length > 0 ? position / (double)length : 0;
226
227 parameters->setPositionAndLength(position, length);
228
229 if (mlt_properties_get_int(MLT_SERVICE_PROPERTIES(service), "_reload")) {
230 mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_reload", 0);
231 effects->reload();
232 }
233
234 // Produce any extra images
235 if (imageProducers) {
236 for (std::vector<ImageProducer*>::iterator it = imageProducers->begin();
237 it != imageProducers->end(); it++) {
238 ImageProducer* imageProducer = *it;
239 if (imageProducer && imageProducer->isPositionValid(position)) {
240 WebVfx::Image extraImage =
241 imageProducer->produceImage(position,
242 outputImage->width(),
243 outputImage->height(),
244 hasAlpha);
245 if (extraImage.isNull()) {
246 mlt_log(service, MLT_LOG_ERROR, "WebVfx failed to produce image for name %s\n", imageProducer->getName().toLatin1().constData());
247 return 1;
248 }
249 effects->setImage(imageProducer->getName(), &extraImage);
250 }
251 }
252 }
253
254 return !effects->render(time, outputImage);
255 }
256
setupConsumerListener(mlt_frame frame)257 void ServiceManager::setupConsumerListener(mlt_frame frame)
258 {
259 // If there is a consumer property, listen to the consumer-stopping event to cancel rendering.
260 if (!event) {
261 mlt_consumer consumer = static_cast<mlt_consumer>(mlt_properties_get_data(MLT_FRAME_PROPERTIES(frame), "consumer", 0));
262 if (consumer) {
263 event = MLT_CONSUMER_PROPERTIES(consumer);
264 mlt_events_listen(event, this, "consumer-stopping",
265 reinterpret_cast<mlt_listener>(MLTWebVfx::consumerStoppingListener));
266 }
267 }
268 }
269
onConsumerStopping()270 void ServiceManager::onConsumerStopping()
271 {
272 mlt_events_disconnect(event, this);
273 event = 0;
274 if (effects)
275 effects->renderComplete(false);
276 }
277
278 }
279