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