1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-io <https://github.com/MrKepzie/openfx-io>,
3  * Copyright (C) 2013-2018 INRIA
4  *
5  * openfx-io is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * openfx-io is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with openfx-io.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17  * ***** END LICENSE BLOCK ***** */
18 
19 /*
20  * OFX GenericReader plugin.
21  * A base class for all OpenFX-based decoders.
22  */
23 
24 #ifndef Io_GenericReader_h
25 #define Io_GenericReader_h
26 
27 #include <memory>
28 #include <ofxsImageEffect.h>
29 #include <ofxsMacros.h>
30 #include "IOUtility.h"
31 
32 namespace SequenceParsing {
33 class SequenceFromFiles;
34 }
35 
36 NAMESPACE_OFX_ENTER
37 NAMESPACE_OFX_IO_ENTER
38 
39 #ifdef OFX_IO_USING_OCIO
40 class GenericOCIO;
41 #endif
42 
43 /**
44  * @brief A generic reader plugin, derive this to create a new reader for a specific file format.
45  * This class propose to handle the common stuff among readers:
46  * - common params
47  * - a tiny cache to speed-up the successive getRegionOfDefinition() calls
48  * - a way to inform the host about the colour-space of the data.
49  **/
50 class GenericReaderPlugin
51     : public OFX::ImageEffect
52 {
53 public:
54 
55     GenericReaderPlugin(OfxImageEffectHandle handle,
56                         const std::vector<std::string>& extensions,
57                         bool supportsTiles,
58                         bool supportsRGBA,
59                         bool supportsRGB,
60                         bool supportsXY,
61                         bool supportsAlpha,
62                         bool isMultiPlanar);
63 
64     virtual ~GenericReaderPlugin();
65 
66     /**
67      * @brief Don't override this function, the GenericReaderPlugin class already does the rendering. The "decoding" of the frame
68      * must be done by the pure virtual function decode(...) instead.
69      **/
70     virtual void render(const OFX::RenderArguments &args) OVERRIDE FINAL;
71 
72     /**
73      * @brief Don't override this. Basically this function will call getTimeDomainForVideoStream(...),
74      * which your reader should implement to read from a video-stream the time range.
75      * If the file is not a video stream, the function getTimeDomainForVideoStream() should return false, indicating that
76      * we're reading a sequence of images and that the host should get the time domain for us.
77      **/
78     virtual bool getTimeDomain(OfxRangeD &range) OVERRIDE FINAL;
79 
80     /**
81      * @brief Don't override this. If the pure virtual function areHeaderAndDataTied() returns true, this
82      * function will call decode() to read the region of definition of the image and cache away the decoded image
83      * into the _dstImg member.
84      * If areHeaderAndDataTied() returns false instead, this function will call the virtual function
85      * getFrameBounds() which should read the header of the image to only extract the bounds and PAR of the image.
86      **/
87     virtual bool getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &args, OfxRectD &rod) OVERRIDE FINAL;
88 
89 
90     /**
91      * @brief You can override this to take actions in response to a param change.
92      * Make sure you call the base-class version of this function at the end: i.e:
93      *
94      * void MyReader::changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName) {
95      *      if (.....) {
96      *
97      *      } else if(.....) {
98      *
99      *      } else {
100      *          GenericReaderPlugin::changedParam(args,paramName);
101      *      }
102      * }
103      **/
104     virtual void changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName) OVERRIDE;
105 
106     /* override is identity */
107     virtual bool isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime, int& view, std::string& plane) OVERRIDE;
108 
109     /**
110      * @brief Set the output components and premultiplication state for the input image automatically.
111      * This is filled from directly the info returned by guessParamsFromFilename
112      **/
113     virtual void getClipPreferences(OFX::ClipPreferencesSetter &clipPreferences) OVERRIDE;
114 
115     /**
116      * @brief Overriden to clear any OCIO cache.
117      * This function calls clearAnyCache() if you have any cache to clear.
118      **/
119     virtual void purgeCaches(void) OVERRIDE;
120 
121     /**
122      * @brief Restore any state from the parameters set
123      * Called from createInstance() and changedParam() (via changedFilename()), must restore the
124      * state of the Reader, such as Choice param options, data members and non-persistent param values.
125      * We don't do this in the ctor of the plug-in since we can't call virtuals yet.
126      * Any derived implementation must call GenericReaderPlugin::restoreStateFromParams() first
127      **/
128     virtual void restoreStateFromParams();
129 
130 
isMultiPlanar()131     bool isMultiPlanar() const
132     {
133         return _isMultiPlanar;
134     }
135 
136     enum BeforeAfterEnum
137     {
138         eBeforeAfterHold,
139         eBeforeAfterLoop,
140         eBeforeAfterBounce,
141         eBeforeAfterBlack,
142         eBeforeAfterError,
143     };
144 
145 protected:
146     /**
147      * @brief Called from changedParam() when kParamFilename is changed for any reason other than eChangeTime.
148      * Calls restoreStateFromParams() to update any non-persistent params that may depend on the filename.
149      * If reason is eChangeUserEdit and the params where never guessed (see _guessedParams) also sets these from the file contents.
150      * Any derived implementation must call GenericReaderPlugin::changedFilename() first
151      **/
152     virtual void changedFilename(const OFX::InstanceChangedArgs &args);
153 
154 
155     OFX::ChoiceParam* _missingFrameParam; //< what to do on missing frame
156 
157     OfxStatus getFilenameAtTime(double t, std::string *filename) const;
158 
159     int getStartingTime() const;
160 
161     // get the value of kParamOutputComponents as a OFX::PixelComponentEnum
162     OFX::PixelComponentEnum getOutputComponents() const;
163 
164     // sets the value of kParamOutputComponents
165     void setOutputComponents(OFX::PixelComponentEnum comps);
166 
167     struct PlaneToRender
168     {
169         float* pixelData;
170         int rowBytes;
171         int numChans;
172         OFX::PixelComponentEnum comps;
173         std::string rawComps;
174     };
175 
176     void convertDepthAndComponents(const void* srcPixelData,
177                                    const OfxRectI& renderWindow,
178                                    const OfxRectI& srcBounds,
179                                    OFX::PixelComponentEnum srcPixelComponents,
180                                    OFX::BitDepthEnum srcBitDepth,
181                                    int srcRowBytes,
182                                    float *dstPixelData,
183                                    const OfxRectI& dstBounds,
184                                    OFX::PixelComponentEnum dstPixelComponents,
185                                    int dstRowBytes);
186 
187 private:
188     /**
189      * @brief Called when the input image/video file changed.
190      *
191      * returns true if file exists and parameters successfully guessed, false in case of error.
192      *
193      * This function is only called once: when the filename is first set.
194      *
195      * Besides returning colorspace, premult, components, and componentcount, if it returns true
196      * this function may also set extra format-specific parameters using OFX::Param::setValue.
197      * The parameters must not be animated, since their value must remain the same for a whole sequence.
198      *
199      * You shouldn't do any strong processing as this is called on the main thread and
200      * the getRegionOfDefinition() and  decode() should open the file in a separate thread.
201      *
202      * The colorspace may be set if available, else a default colorspace is used.
203      *
204      * You must also return the premultiplication state and pixel components of the image.
205      * When reading an image sequence, this is called only for the first image when the user actually selects the new sequence.
206      **/
207     virtual bool guessParamsFromFilename(const std::string& newFile,
208                                          std::string *colorspace,
209                                          OFX::PreMultiplicationEnum *filePremult,
210                                          OFX::PixelComponentEnum *components,
211                                          int *componentCount) = 0;
212 
213     /**
214      * @brief Override to clear any cache you may have.
215      **/
clearAnyCache()216     virtual void clearAnyCache() {}
217 
218 
219     /**
220      * @brief Overload this function to extract the bound of the pixel data
221      * in pixel coordinates and the pixel aspect ratio out of the header
222      * of the image targeted by the filename.
223      **/
224     virtual bool getFrameBounds(const std::string& filename,
225                                 OfxTime time,
226                                 int view,
227                                 OfxRectI *bounds,
228                                 OfxRectI *format,
229                                 double *par,
230                                 std::string *error,
231                                 int* tile_width,
232                                 int* tile_height) = 0;
233 
234     /*
235      * In case of plug-ins that can read tiled files, determines whether the image is oriented bottom up or top down.
236      * This is so that the rounding to the tile size is applied correctly. Currently this is only useful for the OIIO plug-in.
237      */
isTileOrientationTopDown()238     virtual bool isTileOrientationTopDown() const { return true; }
239 
getFrameRate(const std::string &,double *)240     virtual bool getFrameRate(const std::string& /*filename*/,
241                               double* /*fps*/) const { return false; }
242 
243     /**
244      * @brief Override this function to actually decode the image contained in the file pointed to by filename.
245      * If the file is a video-stream then you should decode the frame at the time given in parameters.
246      * You must write the decoded image into dstImg. This function should convert the read pixels into the
247      * bitdepth of the dstImg. You can inform the host of the bitdepth you support in the describe() function.
248      * You can always skip the color-space conversion, but for all linear hosts it would produce either
249      * false colors or sub-par performances in the case the end-user has to append a color-space conversion
250      * effect her/himself.
251      **/
252     virtual void decode(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds, OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, int rowBytes);
253     virtual void decodePlane(const std::string& filename, OfxTime time, int view, bool isPlayback, const OfxRectI& renderWindow, float *pixelData, const OfxRectI& bounds,
254                              OFX::PixelComponentEnum pixelComponents, int pixelComponentCount, const std::string& rawComponents, int rowBytes);
255 
256 
257     /**
258      * @brief Override to indicate the time domain. Return false if you know that the
259      * file isn't a video-stream, true when you can find-out the frame range.
260      **/
getSequenceTimeDomain(const std::string &,OfxRangeI &)261     virtual bool getSequenceTimeDomain(const std::string& /*filename*/,
262                                        OfxRangeI & /*range*/) { return false; }
263 
264     /**
265      * @brief Called internally by getTimeDomain(...)
266      **/
267     bool getSequenceTimeDomainInternal(OfxRangeI& range, bool canSetOriginalFrameRange);
268 
269     /**
270      * @brief Used internally by the GenericReader.
271      **/
272     void timeDomainFromSequenceTimeDomain(const OfxRangeI& sequenceTimeDomain, int startingTime, OfxRangeI* timeDomain);
273 
274     /**
275      * @brief Should return true if the file indicated by filename is a video-stream and not
276      * a single image file.
277      **/
278     virtual bool isVideoStream(const std::string& filename) = 0;
279     enum GetSequenceTimeRetEnum
280     {
281         eGetSequenceTimeWithinSequence = 0,
282         eGetSequenceTimeBeforeSequence,
283         eGetSequenceTimeAfterSequence,
284         eGetSequenceTimeBlack,
285         eGetSequenceTimeError,
286     };
287 
288 
289     /**
290      * @brief compute the sequence/file time from time
291      */
292     GetSequenceTimeRetEnum getSequenceTime(double t, double *sequenceTime) const WARN_UNUSED_RETURN;
293     //GetSequenceTimeRetEnum getSequenceTimeHold(double t, double *sequenceTime) const WARN_UNUSED_RETURN;
294     GetSequenceTimeRetEnum getSequenceTimeBefore(const OfxRangeI& sequenceTimeDomain, BeforeAfterEnum beforeChoice, double *sequenceTime) const;
295     GetSequenceTimeRetEnum getSequenceTimeAfter(const OfxRangeI& sequenceTimeDomain, BeforeAfterEnum afterChoice, double *sequenceTime) const;
296 
297     enum GetFilenameRetCodeEnum
298     {
299         eGetFileNameFailed = 0,
300         eGetFileNameReturnedFullRes,
301         eGetFileNameReturnedProxy,
302         eGetFileNameBlack,
303     };
304 
305     /**
306      * @brief Returns the filename of the image at the sequence time t.
307      **/
308     GetFilenameRetCodeEnum getFilenameAtSequenceTime(double t,
309                                                      bool proxyFiles,
310                                                      bool checkForExistingFile,
311                                                      std::string *filename) const WARN_UNUSED_RETURN;
312 
313 
314     void copyPixelData(const OfxRectI &renderWindow,
315                        const void *srcPixelData,
316                        const OfxRectI& srcBounds,
317                        OFX::PixelComponentEnum srcPixelComponents,
318                        int srcPixelComponentCount,
319                        OFX::BitDepthEnum srcPixelDepth,
320                        int srcRowBytes,
321                        void *dstPixelData,
322                        const OfxRectI& dstBounds,
323                        OFX::PixelComponentEnum dstPixelComponents,
324                        int dstPixelComponentCount,
325                        OFX::BitDepthEnum dstBitDepth,
326                        int dstRowBytes);
327 
328     void scalePixelData(const OfxRectI& originalRenderWindow,
329                         const OfxRectI& renderWindow,
330                         unsigned int levels,
331                         const void* srcPixelData,
332                         OFX::PixelComponentEnum srcPixelComponents,
333                         int srcPixelComponentCount,
334                         OFX::BitDepthEnum srcPixelDepth,
335                         const OfxRectI& srcBounds,
336                         int srcRowBytes,
337                         void* dstPixelData,
338                         OFX::PixelComponentEnum dstPixelComponents,
339                         int dstPixelComponentCount,
340                         OFX::BitDepthEnum dstPixelDepth,
341                         const OfxRectI& dstBounds,
342                         int dstRowBytes);
343 
344     void fillWithBlack(const OfxRectI &renderWindow,
345                        void *dstPixelData,
346                        const OfxRectI& dstBounds,
347                        OFX::PixelComponentEnum dstPixelComponents,
348                        int dstPixelComponentCount,
349                        OFX::BitDepthEnum dstBitDepth,
350                        int dstRowBytes);
351 
352 
353     void premultPixelData(const OfxRectI &renderWindow,
354                           const void *srcPixelData,
355                           const OfxRectI& srcBounds,
356                           OFX::PixelComponentEnum srcPixelComponents,
357                           int srcPixelComponentCount,
358                           OFX::BitDepthEnum srcPixelDepth,
359                           int srcRowBytes,
360                           void *dstPixelData,
361                           const OfxRectI& dstBounds,
362                           OFX::PixelComponentEnum dstPixelComponents,
363                           int dstPixelComponentCount,
364                           OFX::BitDepthEnum dstBitDepth,
365                           int dstRowBytes);
366 
367     void unPremultPixelData(const OfxRectI &renderWindow,
368                             const void *srcPixelData,
369                             const OfxRectI& srcBounds,
370                             OFX::PixelComponentEnum srcPixelComponents,
371                             int srcPixelComponentCount,
372                             OFX::BitDepthEnum srcPixelDepth,
373                             int srcRowBytes,
374                             void *dstPixelData,
375                             const OfxRectI& dstBounds,
376                             OFX::PixelComponentEnum dstPixelComponents,
377                             int dstPixelComponentCount,
378                             OFX::BitDepthEnum dstBitDepth,
379                             int dstRowBytes);
380 
381     OfxPointD detectProxyScale(const std::string& originalFileName, const std::string& proxyFileName, OfxTime time);
382 
383     void setSequenceFromFile(const std::string& filename);
384 
385     void refreshSubLabel(OfxTime time);
386 
387     bool checkExtension(const std::string& ext);
388 
389 protected:
390 #ifdef OFX_IO_USING_OCIO
391     OFX::BooleanParam* _inputSpaceSet;
392     auto_ptr<GenericOCIO> _ocio;
393 #endif
394 
395     OFX::Clip* _syncClip; //< Mantated input clip
396     OFX::Clip * _outputClip; //< Mandated output clip
397     OFX::StringParam  *_fileParam; //< The input file
398 
399     OFX::IntParam* _firstFrame; //< the first frame in the video file (first frame is 1) or file sequence (>=0)
400 
401     OFX::IntParam* _timeOffset; //< the time offset applied to the sequence
402     OFX::IntParam* _startingTime; //< the starting frame of the sequence
403 
404     OFX::Int2DParam* _originalFrameRange; //< the original frame range computed the first time by getSequenceTimeDomainInternal
405 
406 private:
407     // the following params should not be needed in derived classes.
408 
409     OFX::StringParam  *_proxyFileParam; //< the proxy input files
410     OFX::Double2DParam *_proxyThreshold; //< the proxy  images scale threshold
411     OFX::Double2DParam *_originalProxyScale; //< the original proxy image scale
412     OFX::BooleanParam *_enableCustomScale; //< is custom proxy scale enabled
413 
414     OFX::ChoiceParam* _beforeFirst;//< what to do before the first frame
415     OFX::IntParam* _lastFrame; //< the last frame in the sequence (clamped to the time domain)
416     OFX::ChoiceParam* _afterLast; //< what to do after the last frame
417 
418     OFX::ChoiceParam* _frameMode;//< do we use a time offset or an absolute starting frame
419 
420     OFX::ChoiceParam* _outputComponents;
421     OFX::ChoiceParam* _filePremult;
422     OFX::ChoiceParam* _outputPremult;
423 
424     OFX::BooleanParam* _timeDomainUserSet; //< true when the time domain has bee nuser edited
425 
426     OFX::BooleanParam* _customFPS;
427     OFX::DoubleParam* _fps;
428 
429     OFX::StringParam* _sublabel;
430     OFX::BooleanParam* _guessedParams;//!< was guessParamsFromFilename already successfully called once on this instance
431 
432     const std::vector<std::string>& _extensions;
433 
434 private:
435     const bool _supportsRGBA;
436     const bool _supportsRGB;
437     const bool _supportsXY;
438     const bool _supportsAlpha;
439     const bool _supportsTiles;
440     const bool _isMultiPlanar;
441 
442     OFX::PixelComponentEnum _outputComponentsTable[5];
443 };
444 
445 
446 void GenericReaderDescribe(OFX::ImageEffectDescriptor &desc,
447                            const std::vector<std::string>& extensions, // list of supported extensions
448                            int evaluation, // plugin quality from 0 (bad) to 100 (perfect) or -1 if not evaluated
449                            bool supportsTiles, bool multiPlanar);
450 
451 OFX::PageParamDescriptor* GenericReaderDescribeInContextBegin(OFX::ImageEffectDescriptor &desc, OFX::ContextEnum context, bool isVideoStreamPlugin, bool supportsRGBA, bool supportsRGB, bool supportsXY, bool supportsAlpha, bool supportsTiles, bool addSeparatorAfterLastParameter);
452 void GenericReaderDescribeInContextEnd(OFX::ImageEffectDescriptor &desc, OFX::ContextEnum context, OFX::PageParamDescriptor* page, const char* inputSpaceNameDefault, const char* outputSpaceNameDefault);
453 
454 #define mDeclareReaderPluginFactory(CLASS, UNLOADFUNCDEF, ISVIDEOSTREAM) \
455     class CLASS \
456         : public OFX::PluginFactoryHelper<CLASS>                       \
457     {                                                                     \
458 public:                                                                \
459         CLASS(const std::string & id, unsigned int verMaj, unsigned int verMin) \
460             : OFX::PluginFactoryHelper<CLASS>(id, verMaj, verMin) {} \
461         virtual void load();                                   \
462         virtual void unload() UNLOADFUNCDEF;                               \
463         virtual OFX::ImageEffect* createInstance(OfxImageEffectHandle handle, OFX::ContextEnum context); \
464         bool isVideoStreamPlugin() const { return ISVIDEOSTREAM; }  \
465         virtual void describe(OFX::ImageEffectDescriptor & desc);      \
466         virtual void describeInContext(OFX::ImageEffectDescriptor & desc, OFX::ContextEnum context); \
467         std::vector<std::string> _extensions; \
468     };
469 
470 NAMESPACE_OFX_IO_EXIT
471     NAMESPACE_OFX_EXIT
472 
473 #endif // ifndef Io_GenericReader_h
474