diff -Nru mlt-0.9.0/ChangeLog mlt-0.9.2/ChangeLog --- mlt-0.9.0/ChangeLog 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/ChangeLog 2014-06-29 20:23:17.000000000 +0000 @@ -1,3 +1,950 @@ +2014-06-29 Dan Dennedy + + * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version + to 0.9.2. + +2014-06-27 Maksym Veremeyenko + + * src/modules/core/Makefile, src/modules/core/factory.c, + src/modules/core/transition_matte.c, src/modules/core/transition_matte.yml: + Add matte transition. + +2014-06-02 Dan Dennedy + + * src/modules/videostab/filter_videostab.yml, + src/modules/videostab/filter_videostab2.yml: Document the videostab module as + deprecated. This does not remove from the build process because we want to + continue to support legacy projects especially Kdenlive. However, now + documentation lets people know to use vidstab instead. + + * src/modules/Makefile, src/modules/configure, + src/modules/dv/consumer_libdv.yml, src/modules/dv/producer_libdv.yml, + src/modules/vorbis/producer_vorbis.yml: Deprecate the dv, kino, and vorbis + modules. They are removed from the normal configure & make build process, + but they are still easy to build manually if needed, e.g.: make -C src/ + + * src/modules/core/Makefile, src/modules/core/factory.c, + src/modules/core/producer_ppm.c: Remove the ppm pipe producer. No one uses + this, and it is problematic to support. It was mainly used in very early + development before avformat module was any good and smilutils was still + maintained and in use. + + * src/modules/core/filter_transition.yml, src/modules/core/producer_hold.yml, + src/modules/core/producer_melt_file.yml: Finish documenting transition filter + and hold and melt file producers. + + * src/modules/core/consumer_null.c, src/modules/core/filter_mirror.c, + src/modules/core/producer_consumer.c, src/modules/core/producer_hold.c, + src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c, + src/modules/core/transition_luma.c, src/modules/core/transition_mix.c: Remove + usage of "this" as a variable name in core module. + +2014-05-24 Dan Dennedy + + * src/modules/gtk2/producer_pango.c, src/modules/qt/producer_kdenlivetitle.c, + src/modules/qt/producer_qtext.cpp: Fix file names with ext. chars on Windows + for text producers. + + * src/modules/core/transition_composite.c, + src/modules/core/transition_luma.c: Fix file names with ext. chars on Windows + for luma files. + +2014-05-18 Dan Dennedy + + * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix file + names with ext. chars on Windows for xml module. + + * src/framework/mlt_properties.c, src/mlt++/MltProperties.cpp: Fix file names + with exts chars on Windows for mlt_properties. + + * src/framework/Makefile, src/framework/mlt.vers, + src/framework/mlt_properties.c, src/framework/mlt_properties.h, + src/framework/mlt_util.c, src/framework/mlt_util.h, + src/modules/avformat/producer_avformat.c: Move from_utf8 function into + mlt_properties; remove mlt_util. + + * src/framework/Makefile, src/framework/mlt.vers, src/framework/mlt_util.c, + src/framework/mlt_util.h, src/win32/win32.c: Add mlt_util API with + mlt_util_from_utf8(). This will be used for converting a text encoding from + a standard MLT internal string encoding of UTF-8 into other encodings - on + Windows only initially. + +2014-05-07 Dan Dennedy + + * src/modules/plusgpl/Makefile, src/modules/plusgpl/consumer_cbrts.c: Fix + cbrts build on Windows. + +2014-05-06 Dan Dennedy + + * src/modules/plusgpl/Makefile, src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml, src/modules/plusgpl/factory.c: Add + cbrts consumer. See + http://www.mltframework.org/bin/view/MLT/ConsumerCbrtsMore + +2014-04-26 Janne Liljeblad + + * src/modules/core/filter_channelcopy.c, src/modules/core/filter_data_feed.c, + src/modules/core/filter_data_show.c: Clean up channel_copy, data_feed and + data_show + +2014-04-16 Dan Dennedy + + * src/modules/kdenlive/filter_boxblur.yml, + src/modules/kdenlive/filter_wave.yml: Improve service metadata/documentation + for boxblur filter. + +2014-04-15 Janne Liljeblad + + * src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c: + Add animated property wave to filter wave, minor boxblur cleanups + + * src/modules/kdenlive/filter_boxblur.c, + src/modules/kdenlive/filter_boxblur.yml: Add animated property blur to + boxblur + + * src/modules/core/filter_panner.c, src/modules/core/filter_panner.yml: Add + animated property split to panner + +2014-04-08 Dan Dennedy + + * src/modules/core/filter_brightness.yml, + src/modules/normalize/filter_volume.yml, + src/modules/plusgpl/filter_lumaliftgaingamma.yml: Fix YAML validation errors: + double => float. + +2014-04-08 Janne Liljeblad + + * src/modules/normalize/filter_volume.c, + src/modules/normalize/filter_volume.yml: Add animated property level to + filter volume + +2014-04-08 Steinar H. Gunderson + + * src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: + Convert on CPU if we are asked to finalize an empty Movit chain. This fixes + an issue with interlaced content and no resize or other normalizers in actual + use. + +2014-04-05 Dan Dennedy + + * src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + .../opengl/filter_movit_deconvolution_sharpen.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + .../opengl/filter_movit_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_movit_white_balance.cpp, + src/modules/opengl/transition_movit_luma.cpp, + src/modules/opengl/transition_movit_mix.cpp: Hide the movit.parms properties + from serialization. These properties were adding unncessary noise in the XML + output. + +2014-04-04 Dan Dennedy + + * src/framework/mlt_frame.c, src/modules/core/producer_colour.c, + src/modules/gtk2/producer_pixbuf.c, src/modules/opengl/filter_movit_crop.cpp, + src/modules/qt/qimage_wrapper.cpp: Fix crashing when using opengl services + with wrapper producers. Steinar reported crashing in Kdenlive when using the + framebuffer producer due to movit.crop changing the format to mlt_image_none. + Removing that required making other components handle requests with format = + mlt_image_none. + +2014-04-01 Dan Dennedy + + * src/modules/qt/Makefile, src/modules/qt/common.cpp, + src/modules/qt/common.h, src/modules/qt/consumer_qglsl.cpp, + src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/producer_qtext.cpp, + src/modules/qt/qimage_wrapper.cpp, src/modules/qt/transition_vqm.cpp: + Refactor QApplication creation and fix lifetime of its args. + +2014-03-27 Brian Matherly + + * src/modules/gtk2/Makefile, src/modules/gtk2/configure, + src/modules/gtk2/factory.c, src/modules/gtk2/producer_pixbuf.c, + src/modules/kino/Makefile, src/modules/kino/avi.cc, + src/modules/kino/configure, src/modules/kino/filehandler.cc, + src/modules/kino/kino_wrapper.cc, src/modules/kino/riff.cc, + src/modules/opengl/Makefile, src/modules/qt/Makefile, + src/modules/qt/configure, src/modules/qt/qimage_wrapper.h: Avoid unnecessary + compilation when running "./configure; make; make install" multiple times. + +2014-03-25 Brian Matherly + + * src/modules/plus/Makefile, src/modules/plus/factory.c, + src/modules/plus/filter_lift_gamma_gain.c, + src/modules/plus/filter_lift_gamma_gain.yml: Add lift_gamma_gain filter. + This filter is equivalent to the movit.lift_gamma_gain filter but does not + depend on opengl/movit. + +2014-03-25 Dan Dennedy + + * src/modules/opengl/Makefile, src/modules/opengl/factory.c, + src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/transition_movit_luma.cpp, + src/modules/opengl/transition_movit_luma.yml: Add movit.luma transition. + This can be improved by adding an invert parameter to the Movit effect and by + supplying full 16-bit PGM to the Movit input, but it is a start. + +2014-03-24 Dan Dennedy + + * src/modules/core/transition_composite.c, + src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c, + src/modules/vmfx/producer_pgm.c, src/modules/vorbis/producer_vorbis.c: Fix + reading binary files on Windows. + +2014-03-22 Dan Dennedy + + * src/framework/mlt.vers, src/framework/mlt_playlist.c, + src/framework/mlt_playlist.h, src/mlt++/MltPlaylist.cpp, + src/mlt++/MltPlaylist.h, src/mlt++/mlt++.vers: Add mlt_playlist_mix_in() and + mlt_playlist_mix_out(). These are new alternatives to mlt_playlist_mix() + that Shotcut is using. + +2014-03-17 Janne Liljeblad + + * src/modules/core/filter_brightness.c, + src/modules/core/filter_brightness.yml: Add animated level parameter to + brightness + +2014-02-26 Dan Dennedy + + * src/framework/mlt_properties.c, src/framework/mlt_property.c, + src/framework/mlt_property.h: Implement LC_NUMERIC handling for non-glibc and + non-Darwin OS. + +2014-02-24 Dan Dennedy + + * presets/filter/movit.opacity/fade_in, + presets/filter/movit.opacity/fade_in_out, + presets/filter/movit.opacity/fade_out: Add some movit.opacity presets for + fading. + + * src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_opacity.yml: Add alpha property to + movit.opacity filter. + +2014-02-24 Janne Liljeblad + + * src/modules/plusgpl/Makefile, src/modules/plusgpl/factory.c, + src/modules/plusgpl/filter_lumaliftgaingamma.c, + src/modules/plusgpl/filter_lumaliftgaingamma.yml: Add lumaliftgaingamma + filter to plusgpl + +2014-02-19 Dan Dennedy + + * src/modules/avformat/filter_avcolour_space.c, + src/modules/avformat/producer_avformat.c: Fix YUV to RGB conversion when + profile colorspace not 601. The non-default coefficients for RGB cause + incorrect conversion. Reported by Claus R. F. Overbeck on the kdenlive-devel + mailing list. + +2014-02-17 Dan Dennedy + + * src/modules/decklink/consumer_decklink.cpp, + src/modules/decklink/producer_decklink.cpp: Fix compiler warnings due to + non-virtual destructors. + +2014-02-16 Dan Dennedy + + * src/modules/plusgpl/Makefile, src/modules/plusgpl/cJSON.c, + src/modules/plusgpl/cJSON.h, src/modules/plusgpl/factory.c, + src/modules/plusgpl/filter_rotoscoping.c, + src/modules/plusgpl/filter_rotoscoping.yml, src/modules/rotoscoping/Makefile, + src/modules/rotoscoping/cJSON.c, src/modules/rotoscoping/cJSON.h, + src/modules/rotoscoping/factory.c, + src/modules/rotoscoping/filter_rotoscoping.c, + src/modules/rotoscoping/filter_rotoscoping.yml: Move rotoscoping filter into + plusgpl module. + + * src/modules/effectv/Makefile, src/modules/effectv/factory.c, + src/modules/effectv/filter_burn.c, src/modules/effectv/filter_burningtv.yml, + src/modules/effectv/image.c, src/modules/effectv/utils.c, + src/modules/effectv/utils.h, src/modules/plusgpl/Makefile, + src/modules/plusgpl/factory.c, src/modules/plusgpl/filter_burn.c, + src/modules/plusgpl/filter_burningtv.yml, src/modules/plusgpl/image.c, + src/modules/plusgpl/utils.c, src/modules/plusgpl/utils.h: Move burningtv into + plusgpl module. + + * src/modules/dgraft/Makefile, src/modules/dgraft/factory.c, + src/modules/dgraft/filter_telecide.c, src/modules/plusgpl/Makefile, + src/modules/plusgpl/factory.c, src/modules/plusgpl/filter_telecide.c: Start + new plusgpl module from dgraft. + +2014-02-13 Maksym Veremeyenko + + * src/modules/decklink/common.cpp, src/modules/decklink/common.h, + src/modules/decklink/consumer_decklink.cpp, + src/modules/decklink/producer_decklink.cpp: implement SSE optimized swab + function + +2014-02-12 Dan Dennedy + + * demo/mlt_swf_variables, demo/txtField.swf: Remove Flash binary. + +2014-02-12 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + .../opengl/filter_movit_deconvolution_sharpen.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + .../opengl/filter_movit_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_mirror.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_rect.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_movit_white_balance.cpp, + src/modules/opengl/mlt_flip_effect.h, src/modules/opengl/mlt_movit_input.cpp, + src/modules/opengl/mlt_movit_input.h, src/modules/opengl/optional_effect.h, + src/modules/opengl/transition_movit_mix.cpp, + src/modules/opengl/transition_movit_overlay.cpp: Adjust for Movit moving into + namespace movit. + +2014-02-11 Janne Liljeblad + + * src/modules/plus/filter_rgblut.c, src/modules/plus/filter_rgblut.yml: + Change this to frame or filter as appropriate + +2014-02-10 Janne Liljeblad + + * src/modules/plus/Makefile, src/modules/plus/factory.c, + src/modules/plus/filter_rgblut.c, src/modules/plus/filter_rgblut.yml: Add + rgblut filter + +2014-02-05 Brian Matherly + + * src/modules/plus/filter_loudness.c, src/modules/plus/filter_loudness.yml: + Make program property mutable + +2014-02-05 Dan Dennedy + + * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/queue.h: Fix + build on Windows due to missing queue macros. + +2014-02-04 Brian Matherly + + * src/modules/plus/Makefile, src/modules/plus/ebur128/COPYING, + src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h, + src/modules/plus/factory.c, src/modules/plus/filter_loudness.c, + src/modules/plus/filter_loudness.yml: Add new audio loudness filter based on + EBU R128 + +2014-01-28 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h: Remove the FBO freelist. FBOs are + cheap to construct and delete (they carry almost no state), so it is less + complex just to do it on the fly. It also gives less leakage, as we use new + contexts all the time. + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: + Take MltInput out of the EffectChain. Having the MltInput be an Input which + forwards down to the real implementation has been a source of multiple + headaches, and now lastly, when finalize() disappeared, source of a broken + build. We still need the unified set_pixel_pointer() etc., but the class is + now simply a holder of the Input*, not a forwarder as viewed from the + EffectChain. + +2014-01-29 Brian Matherly + + * src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, + src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_vidstab.cpp, + src/modules/vid.stab/filter_vidstab.yml: Save vidstab results to file. + Rather than save vidstab results (which can get quite large) in the + properties, save them in a separate file. Also redirect vid.stab log messages + through the MLT logging system (sort of). + +2014-01-28 Dan Dennedy + + * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml, + src/modules/xml/producer_xml.c, src/modules/xml/producer_xml.yml: Add + xml_retain property support to xml module. This is used to serialize and + deserialize extra services that are not part of the lastmost service's graph. + This is useful, for example, to save and load a media bin as a playlist in + addition to the main multitrack graph. Or, it can be used for compound + documents. + +2014-01-21 Steinar H. Gunderson + + * src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: + Call invalidate_pixel_data() after frame rendering. This helps the input + return its values back to the ResourcePool, which means we won't be + allocating ever more textures as we get more clips on the timeline. + +2014-01-17 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: + Use the new ResourcePool Movit functionality. + +2014-01-25 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + .../opengl/filter_movit_deconvolution_sharpen.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + .../opengl/filter_movit_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_mirror.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_rect.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_movit_white_balance.cpp: Propertly refcount the + GlslManager. Makes sure it is not deleted before all the associated services + connected to it are. + +2014-01-20 Dan Dennedy + + * presets/consumer/avformat/dv_ntsc/DV, + presets/consumer/avformat/dv_ntsc/DVCPRO50, + presets/consumer/avformat/dv_ntsc_wide/DV, + presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, + presets/consumer/avformat/dv_pal/DV, + presets/consumer/avformat/dv_pal/DVCPRO50, + presets/consumer/avformat/dv_pal_wide/DV, + presets/consumer/avformat/dv_pal_wide/DVCPRO50: Add default format and + extension to all DV presets. + + * src/modules/avformat/filter_avcolour_space.c, + src/modules/avformat/producer_avformat.c: Fix a few problems with YCbCr + colorspace conversion. In avformat producer on libav (and FFmpeg < v2.1) + conversion from RGB to YCbCr would not use the destination colorspace because + sws_getColorspaceDetails() fails. Switch to calling only + sws_setColorspaceDetails(). In full luma yuvj420p->mlt_image_yuv420p + conversion, the luma range was always scaled down to MPEG range. The swscale + implementation does not let one override the range as the conversion routines + are initialized at the time a swscale context is allocated and initialized. + Any changes in sws_setColorspaceChanges() are mute. In RGB->YCbCr + conversion, the existing (source) colorspace was used instead of the profile + colorspace. Also, we need to set the new colorspace as a property of the + frame. + +2014-01-19 Brian Matherly + + * src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, + src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_deshake.yml, + src/modules/vid.stab/filter_vidstab.cpp, + src/modules/vid.stab/filter_vidstab.yml: Updates to vid.stab module. * + Correct some metadata * Remove "reset" property by making deshake properties + mutable. * Implement "reload" for vidstab for reloading results. * Misc. + changes for MLT consistency. + +2014-01-17 Brian Matherly + + * src/framework/mlt_frame.h, src/modules/core/filter_imageconvert.c, + src/modules/vid.stab/Makefile, src/modules/vid.stab/common.c, + src/modules/vid.stab/common.h, src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_vidstab.cpp: Add support for more image formats + to vid.stab + +2014-01-16 Steinar H. Gunderson + + * src/modules/opengl/filter_movit_convert.cpp, + .../opengl/filter_movit_deconvolution_sharpen.cpp: Let Movit effects supply + their own fingerprint. This allows effects to signal that some sort of + change means the chain needs to be regenerated. In particular, this unbreaks + changing the matrix_size parameter of DeconvolutionSharpenEffect; if you + change it, the entire chain will now be regenerated, instead of getting an + assertion failure. + + * src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp: Stop special-casing the disable + parameter for setting. There are more parameters then just 'disable' that + should be set before chain finalization; in particular, + DeconvolutionSharpenEffect compiles the matrix size into the shader. Instead, + just set all the parameters once right after the chain has been built, which + includes the disable parameter. + +2014-01-15 Brian Matherly + + * src/framework/mlt_animation.c, + src/tests/test_properties/test_properties.cpp: Fix animation serialization + when length is not specified + +2014-01-11 Steinar H. Gunderson + + * src/modules/opengl/Makefile, src/modules/opengl/fbo_input.cpp, + src/modules/opengl/fbo_input.h, src/modules/opengl/mlt_movit_input.cpp, + src/modules/opengl/mlt_movit_input.h: Remove now unused FBOInput. + +2014-01-01 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + .../opengl/filter_movit_deconvolution_sharpen.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + .../opengl/filter_movit_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_mirror.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_movit_white_balance.cpp, + src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h, + src/modules/opengl/optional_effect.h, + src/modules/opengl/transition_movit_mix.cpp, + src/modules/opengl/transition_movit_overlay.cpp: Change how the Movit chain + is built. * Build the chain in GlslManager. This allows us to get rid of + effects that don't actually do anything (like all the normalizers in the + common case); in Movit, they tend to burn a lot of memory bandwidth. We solve + this by a new OptionalEffect template, that can rewrite itself out of the + graph if it sees it is a no-op. We need to recreate the chain from scratch if + this status should change (e.g. the input resolution changed from one frame + to the next, and we thus suddenly need resizing after all), so we keep a + "fingerprint" string that contains all the unique IDs of the services in use, + as well as their disabled status, and compare against this frame. Building + the chain in one piece also opens up for transitions to be more efficient; + they are now built as part of one big Movit chain, instead of bouncing to an + 8-bit sRGB buffer and back. * Change the mlt_glsl type. Now, the mlt_glsl + image type has a defined value, which is the mlt_service pointer. Each filter + is responsible for storing this input service. This, together with the + mlt_frame, enables us to actually build the Movit chain based on the MLT + relations, instead of just relying in the order in which they are called and + assuming everything has a single input. As a special case, the value + (mlt_service) -1 (which should never be a valid pointer) means that we read + the information from an input rather than an effect. In this case, we take a + copy of the pixel data we get in (since it will soon be garbage collected), + store it in an MltInput and then store that MltInput for later use. This + could probably be further simplified in the future to get completely rid of + MltInput and just use the regular FlatInput/YCbCrInput instead. This also + requires us to change so that the chain is built and finalized at the _end_ + of the conversion steps (where it's logically needed), instead of at the + beginning as before. The beginning (conversion from * -> mlt_glsl) now only + stores the input as described below. * Change Effect and EffectChain + storage. This changes the storage of Movit stuff as follows: - The + EffectChain (along with some associated information to be able to more easily + locate the services and Effect pointers; together, called a GlslChain) is now + stored on the output service, not on the input producer. This allows us to + have multiple EffectChains floating around. - The Effect pointers no longer + live permanently on the MLT graph, since each MLT service can have more than + one Effect. Instead, they live temporarily on the frame (because the frame is + not shared between threads, giving us a poor man's version of thread-local + storage), until they reach the point where we decide if we need to rebuild + the EffectChain or not. At this point, they are either made part of the chain + (and owned by it), or disposed as unneeded. - The MltInput also lives on the + frame. (If we have multiple inputs, we also have multiple frames.) As + mentioned above, its use is signaled by an mlt_service of -1. * Change how + Movit parameter setting works. Services no longer set parameters directly on + the Movit filters, since they cannot know before the graph construction time + whether the correct destination is the newly created Effect, or a similar one + in the EffectChain. Instead, they set special properties + (movit.parms..[]), and then the convert filter uses these + to set Movit parameters on the right Effects. + +2014-01-13 Steinar H. Gunderson + + * src/modules/opengl/Makefile, .../opengl/filter_deconvolution_sharpen.cpp, + .../opengl/filter_deconvolution_sharpen.yml, + src/modules/opengl/filter_lift_gamma_gain.cpp, + src/modules/opengl/filter_lift_gamma_gain.yml, + .../opengl/filter_movit_deconvolution_sharpen.cpp, + .../opengl/filter_movit_deconvolution_sharpen.yml, + .../opengl/filter_movit_lift_gamma_gain.cpp, + .../opengl/filter_movit_lift_gamma_gain.yml, + src/modules/opengl/filter_movit_white_balance.cpp, + src/modules/opengl/filter_movit_white_balance.yml, + src/modules/opengl/filter_white_balance.cpp, + src/modules/opengl/filter_white_balance.yml: Rename some Movit filter + filenames, for consistency. + +2014-01-12 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h: Add back automatic cleanup of + OpenGL fences. Instead of having the client do deletion of fences, work + around the problem with missing contexts by adding them to a list in the + GlslManager, which then it garbage-collected before creating more fences. + +2014-01-08 Steinar H. Gunderson + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h: Replace glFinish with OpenGL + fences. The glFinish after rendering to a texture serves two purposes: + First, and maybe most importantly, it makes sure that if we send the texture + ID to another thread and try to draw it there, it is actually valid in that + context. (If not, the command to allocate it could still be stuck in the + queue, or the command to draw the quad to the screen could be queued before + the command to actually render the image to the texture.) Second, it makes + sure we don't overwhelm the GPU with rendering commands, especially in the + readahead thread. GPUs have a long pipeline, and our commands buffers are + typically very short (we render only one or a few quads per frame), which + means that we could queue so much rendering that we couldn't actually get to + display the frames, or do compositing and other normal UI tasks. (GPUs are + not all that good at scheduling.) However, glFinish() also has an unwanted + side effect: Since the CPU waits for the GPU to finish, it means it cannot do + anything useful in that period; in particular, it cannot start decoding input + video for the next frame, which is very frequently a win. Thus, we replace + glFinish() with fences: One that we store on the frame and that the client + can wait for, and one that we wait for ourselves before we render the next + frame. The first fulfills purpose #1 above (although a client that doesn't + render in a different thread can just ignore it), while the second fulfills + purpose #2. #2 does reduce the possible pipelining somewhat (compared to not + having any fence at all), but it seems that the actual performance lost is + very small in practice. In any case, this is markedly faster than glFinish -- + on my Intel HD 3000, it increases GPU utilization from ~40% to over 80% in a + typical transition. Note that this is an API change; a client that wants to + send the OpenGL texture number on to a different thread for display, will now + need to wait for the fence before it can actually draw using it. + + * src/modules/avformat/producer_avformat.c, + src/modules/opengl/filter_movit_convert.cpp: Make the Movit converter use the + correct color primaries. We need to distinguish between the YUV primaries + and the color space; for instance, my camera outputs Rec. 601/525 YUV but + uses Rec. 709 color primaries. Also fix so that we read the correct full_luma + flag, not just check force_full_luma. (Again, my camera outputs this.) Movit + doesn't support all the exotic color spaces ffmpeg/libav does, but this + should cover most of the common ones. + +2014-01-11 Brian Matherly + + * src/modules/vid.stab/common.h, src/modules/vid.stab/factory.c, + src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_vidstab.cpp: File header consistency + + * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, + src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_detect.cpp, + src/modules/vid.stab/filter_detect.yml, + src/modules/vid.stab/filter_transform.cpp, + src/modules/vid.stab/filter_transform.yml, + src/modules/vid.stab/filter_vidstab.cpp, + src/modules/vid.stab/filter_vidstab.yml: Merge filter_detect and + filter_transform into filter_vidstab + +2014-01-09 Jakub Ksiezniak + + * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, + src/modules/vid.stab/configure, src/modules/vid.stab/factory.c, + src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_deshake.yml, + src/modules/vid.stab/filter_detect.cpp, + src/modules/vid.stab/filter_detect.yml, + src/modules/vid.stab/filter_transform.cpp, + src/modules/vid.stab/filter_transform.yml, + src/modules/vid.stab/filter_vidstab.cpp, + src/modules/vid.stab/filter_vidstab.yml: Added a fourth filter, that combines + both detect and transform passes. * Increased a default smoothing factor, + according to the original vid.stab default settings. * Added a deshake data + clear when seeking is performed. * Added a version check in configure script. + +2014-01-07 Brian Matherly + + * : Mark vid.stab module as GPL + +2014-01-07 Jakub Ksiezniak + + * src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_deshake.yml, + src/modules/vid.stab/filter_detect.cpp, + src/modules/vid.stab/filter_detect.yml, + src/modules/vid.stab/filter_transform.cpp, + src/modules/vid.stab/filter_transform.yml: Update module to work with the + latest vid.stab version 0.98. * Added a new property "zoomspeed" for + adaptive zooming feature. * Removed a no longer used "sharpen" property. + +2013-12-31 Jakub Ksiezniak + + * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, + src/modules/vid.stab/configure, src/modules/vid.stab/factory.c, + src/modules/vid.stab/filter_deshake.cpp, + src/modules/vid.stab/filter_deshake.yml, + src/modules/vid.stab/filter_detect.cpp, + src/modules/vid.stab/filter_detect.yml, + src/modules/vid.stab/filter_transform.cpp, + src/modules/vid.stab/filter_transform.yml: Created a new module to support + vid.stab library. + +2014-01-05 Steinar H. Gunderson + + * .../opengl/filter_deconvolution_sharpen.cpp, + src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_crop.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + src/modules/opengl/filter_movit_mirror.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_white_balance.cpp: Change get_effect/add_effect to + take in mlt_service. We want to be able to set effects on transitions as + well, but in MLT, transitions are not filters, only services. + +2014-01-03 Steinar H. Gunderson + + * .../opengl/filter_deconvolution_sharpen.cpp, + src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_glsl_manager.h, + src/modules/opengl/filter_lift_gamma_gain.cpp, + src/modules/opengl/filter_movit_blur.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/filter_movit_crop.cpp, + src/modules/opengl/filter_movit_diffusion.cpp, + src/modules/opengl/filter_movit_glow.cpp, + src/modules/opengl/filter_movit_mirror.cpp, + src/modules/opengl/filter_movit_opacity.cpp, + src/modules/opengl/filter_movit_rect.cpp, + src/modules/opengl/filter_movit_resample.cpp, + src/modules/opengl/filter_movit_resize.cpp, + src/modules/opengl/filter_movit_saturation.cpp, + src/modules/opengl/filter_movit_vignette.cpp, + src/modules/opengl/filter_white_balance.cpp, + src/modules/opengl/glsl_manager.h, + src/modules/opengl/transition_movit_mix.cpp, + src/modules/opengl/transition_movit_overlay.cpp: Rename glsl_manager.h to + filter_glsl_manager.h, to be consistent with the .cpp file. + +2014-01-04 Dan Dennedy + + * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h, + src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h: Change param name + "length" to "out" in mlt_playlist_insert_blank. + +2014-01-01 Dan Dennedy + + * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add + consumer-thread-create and consumer-thread-join events. If an app listens to + these, it can override the implementation of thread creation and joining. + Otherwise, if no listeners, it falls back to pthread_create() and + pthread_join() as usual. At this time, only the base mlt_consumer uses this + for real_time=1 or -1 only. + + * src/framework/mlt_events.c, src/framework/mlt_events.h, + src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Let mlt_events_fire() + return the number of listeners. Callers can determine if there is a listener + that overrides some behavior. + +2013-12-31 Dan Dennedy + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/glsl_manager.h: Prevent sharing FBOs between contexts. + Based on patch by Steinar Gunderson. + +2013-12-30 Dan Dennedy + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/opengl/glsl_manager.h, + src/modules/opengl/transition_movit_mix.cpp, + src/modules/opengl/transition_movit_overlay.cpp: Refactor movit.convert, + movit.mix, and movit.overlay. To use new methods on GlslManager: + render_frame_texture() and render_frame_rgba(). The latter routine was + changed to use GL_BGRA in glReadPixels() to improve performance on more + OpenGL implementation (per Steinar Gunderson's recommendation). + + * Makefile, src/examples/Makefile, src/melt/Makefile, + src/modules/avformat/Makefile, src/modules/frei0r/Makefile, + src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, + src/modules/opengl/Makefile, src/modules/resample/Makefile, + src/modules/rtaudio/Makefile, src/modules/sdl/Makefile, + src/modules/vorbis/Makefile, src/modules/xml/Makefile, src/swig/Makefile: + Convert backtick to $(shell) in Makefiles. + + * src/modules/gtk2/Makefile, src/modules/gtk2/producer_pango.c: Fix build on + Freetype 2.5. Reported by Patrick Matthhai/Debian. + +2013-12-23 Dan Dennedy + + * src/framework/mlt_transition.c, src/framework/mlt_transition.h: Add disable + property to mlt_transition. + + * src/framework/mlt_filter.h, src/framework/mlt_service.h: Move "disable" + property doc from service to filter. + +2013-12-20 Dan Dennedy + + * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/producer_qimage.c, + src/modules/qt/qimage_wrapper.cpp: Fix concurrency bug in image producers. + Reported by Michael Marina. + +2013-12-02 Dan Dennedy + + * src/framework/mlt.vers, src/framework/mlt_properties.c, + src/framework/mlt_properties.h, src/mlt++/MltProperties.cpp, + src/mlt++/MltProperties.h, src/mlt++/mlt++.vers: Add + mlt_properties_frames_to_time() and mlt_properties_time_to_frames(). Handy + conversion functions for apps. + +2013-12-17 Brian Matherly + + * docs/install.txt, src/modules/qt/Makefile, src/modules/qt/configure, + src/modules/qt/factory.c: Finalize qimage->qt rename + + * src/modules/qimage/Makefile, src/modules/qimage/configure, + src/modules/qimage/consumer_qglsl.cpp, src/modules/qimage/factory.c, + src/modules/qimage/kdenlivetitle_wrapper.cpp, + src/modules/qimage/kdenlivetitle_wrapper.h, + src/modules/qimage/producer_kdenlivetitle.c, + src/modules/qimage/producer_kdenlivetitle.yml, + src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.yml, + src/modules/qimage/producer_qtext.cpp, src/modules/qimage/producer_qtext.yml, + src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h, + src/modules/qimage/transition_vqm.cpp, src/modules/qimage/transition_vqm.yml, + src/modules/qt/Makefile, src/modules/qt/configure, + src/modules/qt/consumer_qglsl.cpp, src/modules/qt/factory.c, + src/modules/qt/kdenlivetitle_wrapper.cpp, + src/modules/qt/kdenlivetitle_wrapper.h, + src/modules/qt/producer_kdenlivetitle.c, + src/modules/qt/producer_kdenlivetitle.yml, src/modules/qt/producer_qimage.c, + src/modules/qt/producer_qimage.yml, src/modules/qt/producer_qtext.cpp, + src/modules/qt/producer_qtext.yml, src/modules/qt/qimage_wrapper.cpp, + src/modules/qt/qimage_wrapper.h, src/modules/qt/transition_vqm.cpp, + src/modules/qt/transition_vqm.yml: Rename 'qimage' module to 'qt' + +2013-12-04 Brian Matherly + + * src/modules/avsync/Makefile, src/modules/avsync/consumer_blipflash.c, + src/modules/avsync/consumer_blipflash.yml, src/modules/avsync/factory.c, + src/modules/avsync/producer_blipflash.c, + src/modules/avsync/producer_blipflash.yml, src/modules/gtk2/Makefile, + src/modules/gtk2/factory.c, src/modules/gtk2/filter_dynamictext.c, + src/modules/gtk2/filter_dynamictext.yml, src/modules/gtk2/producer_count.c, + src/modules/gtk2/producer_count.yml, src/modules/plus/Makefile, + src/modules/plus/consumer_blipflash.c, + src/modules/plus/consumer_blipflash.yml, src/modules/plus/factory.c, + src/modules/plus/filter_dynamictext.c, + src/modules/plus/filter_dynamictext.yml, + src/modules/plus/producer_blipflash.c, + src/modules/plus/producer_blipflash.yml, src/modules/plus/producer_count.c, + src/modules/plus/producer_count.yml: Move blipflash, dynamictext and count + into plus module + +2013-11-28 Brian Matherly + + * src/modules/qimage/Makefile, src/modules/qimage/factory.c, + src/modules/qimage/producer_qtext.cpp, src/modules/qimage/producer_qtext.yml: + Initial implementation of producer_qtext + +2013-12-03 Dan Dennedy + + * presets/consumer/avformat/lossless/FFV1, + presets/consumer/avformat/lossless/HuffYUV: Fix matroska avformat presets. + +2013-11-07 Dan Dennedy + + * src/modules/videostab/filter_videostab2.c, + src/modules/videostab/filter_videostab2.yml: Add a refresh property to + videostab2 filter. + +2013-11-05 Dan Dennedy + + * src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.yml: Add + analysis_level property to sox filter. For normalization analysis when not + use_peak, sets the target amplitude. + +2013-10-27 Dan Dennedy + + * src/modules/videostab/filter_videostab2.c, + src/modules/videostab/transform_image.c: Fix videostab2 interpolation. This + filter uses RGB mode, for which bicubic is broken. vid.stab still to this day + uses bilinear with packed pixel formats. In order to fix bilinear, needed to + remove extra calls to floor function. + +2013-10-16 Dan Dennedy + + * src/framework/metaschema.yaml, src/modules/core/filter_channelcopy.yml: Add + 'boolean' and 'argument' to service metadata schema. + +2013-09-17 Dan Dennedy + + * src/mlt++/MltTokeniser.cpp, src/mlt++/MltTokeniser.h: Fix compile warnings + on string literal for default parameter. + +2013-09-12 Dan Dennedy + + * src/modules/xml/factory.c, src/modules/xml/producer_xml-nogl.yml, + src/modules/xml/producer_xml-string.yml, src/modules/xml/producer_xml.c: Add + producer xml-nogl to disable auto-qglsl creation. Sometimes you want to load + MLT XML while ignoring the presence of OpenGL filters and transitions. + +2013-08-24 Dan Dennedy + + * src/modules/qimage/configure, src/modules/qimage/consumer_qglsl.cpp: Fix + qglsl on Qt 5 for OS X. + + * src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/glsl_manager.h: Add "close glsl" event to glsl.manager + service. Qt 5 apps (and possibly others) must use this because the OpenGL + context for rendering needs to be created and destroyed on the thread on + which it is actually used. This should be fired on the glsl.manager filter + instance inside of a consumer-thread-stopped mlt_event listener. + + * src/modules/qimage/Makefile, src/modules/qimage/consumer_qglsl.cpp: Fix + qglsl consumer for Qt 5. + +2013-08-18 Dan Dennedy + + * presets/consumer/avformat/MPEG-4, presets/consumer/avformat/MPEG-4-ASP, + presets/consumer/avformat/x264-medium, + presets/consumer/avformat/x264-medium-baseline, + presets/consumer/avformat/x264-medium-main, + presets/consumer/avformat/x264-medium-pass1: Add faststart muxing flag to MP4 + presets. + +2013-08-07 Dan Dennedy + + * src/modules/qimage/Makefile, src/modules/qimage/configure, + src/modules/qimage/consumer_qglsl.cpp, + src/modules/qimage/kdenlivetitle_wrapper.cpp, + src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/transition_vqm.cpp: + Add support for Qt 5, drop support for Qt 3 and KDE 3. + + * src/framework/mlt_frame.c, src/modules/core/consumer_multi.c: Move the + aspect ratio for multi consumer from mlt_frame. + +2013-06-21 Dan Dennedy + + * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add + consumer-stopping event fired before joining threads. + +2013-06-19 Dan Dennedy + + * configure, src/framework/mlt_version.h: Set to interim version 0.9.1. + +2013-06-04 Dan Dennedy + + * src/swig/ruby/build, src/swig/ruby/metadata.rb: Add exception handling + around YAML parsing in metadata.rb. + 2013-06-02 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version diff -Nru mlt-0.9.0/configure mlt-0.9.2/configure --- mlt-0.9.0/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/configure 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ #!/bin/sh -export version=0.9.0 +export version=0.9.2 export soversion=6 show_help() @@ -81,10 +81,12 @@ if [ "$optimisations" = "true" ] then echo "OPTIMISATIONS=-O2 -pipe" - # Since gcc 4.6, this optimization enabled with -O1 causes filter_line_sse2 to crash. - echo "OPTIMISATIONS+=-fno-tree-dominator-opts" - # Since gcc 4.6, this optimization enabled with -O2 causes filter_line_sse2 to crash. - echo "OPTIMISATIONS+=-fno-tree-pre" + if $("$CC" --version 2> /dev/null | grep gcc); then + # Since gcc 4.6, this optimization enabled with -O1 causes filter_line_sse2 to crash. + echo "OPTIMISATIONS+=-fno-tree-dominator-opts" + # Since gcc 4.6, this optimization enabled with -O2 causes filter_line_sse2 to crash. + echo "OPTIMISATIONS+=-fno-tree-pre" + fi fi echo "CFLAGS+=-Wall -DPIC \$(TARGETARCH) \$(TARGETCPU) \$(OPTIMISATIONS) \$(MMX_FLAGS) \$(SSE_FLAGS) \$(SSE2_FLAGS) \$(DEBUG_FLAGS) \$(LARGE_FILE)" @@ -301,7 +303,7 @@ grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false ;; FreeBSD) - [ "$(make -V MACHINE_CPU:Mmmx)" ] || mmx=false + [ "$(make -V MACHINE_CPU:Mmmx -f /dev/null)" ] || mmx=false ;; *) grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false @@ -320,7 +322,7 @@ grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false ;; FreeBSD) - [ "$(make -V MACHINE_CPU:Msse)" ] || sse=false + [ "$(make -V MACHINE_CPU:Msse -f /dev/null)" ] || sse=false ;; *) grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false @@ -339,7 +341,7 @@ grep sse2 /proc/cpuinfo > /dev/null 2>&1 || sse2=false ;; FreeBSD) - [ "$(make -V MACHINE_CPU:Msse2)" ] || sse2=false + [ "$(make -V MACHINE_CPU:Msse2 -f /dev/null)" ] || sse2=false ;; *) grep sse2 /proc/cpuinfo > /dev/null 2>&1 || sse2=false diff -Nru mlt-0.9.0/debian/changelog mlt-0.9.2/debian/changelog --- mlt-0.9.0/debian/changelog 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/changelog 2014-09-14 12:26:47.000000000 +0000 @@ -1,3 +1,38 @@ +mlt (0.9.2-1) trusty; urgency=medium + + * For trusty (just looking ppa + + -- Doug McMahon Sun, 14 Sep 2014 08:26:14 -0400 + +mlt (0.9.2-1build1) utopic; urgency=medium + + * Rebuild against libav11. + + -- Colin Watson Fri, 05 Sep 2014 01:19:44 +0100 + +mlt (0.9.2-1) unstable; urgency=medium + + * New upstream release. + - Refresh hunky patch 01-changed-preset-path. + - Remove merged patch 02-crash-empty-ladspa-path. + - Remove merged patch 03-freetype-ftbfs. + * Overwrite lintian warning intra-source-package-circular-dependency, because + those dependencies are required. + * Move away from hardening-wrapper. + * Overwrite some hardening warnings in python-mlt. + + -- Patrick Matthäi Wed, 09 Jul 2014 10:06:36 +0200 + +mlt (0.9.0+dfsg1-1) unstable; urgency=high + + * Remove demo/txtField.swf from source and repackage mlt as +dfsg1. + Closes: #737428 + * Migrate package to libxine2. + Closes: #741362 + * Remove useless Pre-Depends fields. + + -- Patrick Matthäi Thu, 03 Apr 2014 10:45:58 +0200 + mlt (0.9.0-3) unstable; urgency=high * Bump Standards-Version to 3.9.5 (no changes needed). diff -Nru mlt-0.9.0/debian/control mlt-0.9.2/debian/control --- mlt-0.9.0/debian/control 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/control 2014-07-09 08:06:53.000000000 +0000 @@ -19,12 +19,11 @@ libsox-dev (>= 14.3.0), libswscale-dev, libvorbis-dev, - libxine-dev, + libxine2-dev, libxml2-dev, imagemagick, frei0r-plugins-dev, swig, - hardening-wrapper, python-all-dev (>= 2.6.6-3~) Standards-Version: 3.9.5 Section: libs @@ -33,7 +32,6 @@ Package: libmlt-dev Section: libdevel Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, libmlt6 (= ${binary:Version}) Description: multimedia framework (development) @@ -49,7 +47,6 @@ Package: libmlt6 Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends} Recommends: libmlt-data (>= ${source:Version}) @@ -80,7 +77,6 @@ Package: libmlt-data Architecture: all -Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends} Enhances: libmlt6 Description: multimedia framework (data) @@ -96,7 +92,6 @@ Package: libmlt++-dev Section: libdevel Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: libmlt++3 (= ${binary:Version}), ${misc:Depends} Description: MLT multimedia framework C++ wrapper (development) @@ -112,7 +107,6 @@ Package: libmlt++3 Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: libmlt++0.2, @@ -134,7 +128,6 @@ Section: debug Priority: extra Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, libmlt6 (= ${binary:Version}), @@ -152,7 +145,6 @@ Package: melt Section: utils Architecture: any -Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, libmlt-data (>= ${source:Version}) @@ -164,7 +156,6 @@ Package: python-mlt Architecture: any Section: python -Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} diff -Nru mlt-0.9.0/debian/patches/01-changed-preset-path.diff mlt-0.9.2/debian/patches/01-changed-preset-path.diff --- mlt-0.9.0/debian/patches/01-changed-preset-path.diff 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/patches/01-changed-preset-path.diff 2014-07-09 08:06:53.000000000 +0000 @@ -4,9 +4,9 @@ # /usr/share/avconv/*.avpreset # Closes: #681339 -diff -Naur mlt-0.9.0.orig/src/modules/avformat/configure mlt-0.9.0/src/modules/avformat/configure ---- mlt-0.9.0.orig/src/modules/avformat/configure 2013-06-03 05:34:35.000000000 +0200 -+++ mlt-0.9.0/src/modules/avformat/configure 2013-06-04 11:50:18.832675538 +0200 +diff -Naur mlt-0.9.2.orig/src/modules/avformat/configure mlt-0.9.2/src/modules/avformat/configure +--- mlt-0.9.2.orig/src/modules/avformat/configure 2014-06-29 22:23:17.000000000 +0200 ++++ mlt-0.9.2/src/modules/avformat/configure 2014-07-09 09:40:26.572402187 +0200 @@ -84,7 +84,7 @@ then if [ -d "$static_ffmpeg" ] @@ -16,10 +16,10 @@ echo "CFLAGS+=-I$static_ffmpeg" >> config.mak echo "LDFLAGS+=-L$static_ffmpeg/libavformat -L$static_ffmpeg/libavcodec -L$static_ffmpeg/libavutil" >> config.mak echo "LDFLAGS+=-L$static_ffmpeg/libswscale" >> config.mak -diff -Naur mlt-0.9.0.orig/src/modules/avformat/consumer_avformat.c mlt-0.9.0/src/modules/avformat/consumer_avformat.c ---- mlt-0.9.0.orig/src/modules/avformat/consumer_avformat.c 2013-06-03 05:34:35.000000000 +0200 -+++ mlt-0.9.0/src/modules/avformat/consumer_avformat.c 2013-06-04 11:51:04.280674956 +0200 -@@ -783,12 +783,12 @@ +diff -Naur mlt-0.9.2.orig/src/modules/avformat/consumer_avformat.c mlt-0.9.2/src/modules/avformat/consumer_avformat.c +--- mlt-0.9.2.orig/src/modules/avformat/consumer_avformat.c 2014-06-29 22:23:17.000000000 +0200 ++++ mlt-0.9.2/src/modules/avformat/consumer_avformat.c 2014-07-09 09:40:26.572402187 +0200 +@@ -805,12 +805,12 @@ AVCodec *codec = avcodec_find_encoder( c->codec_id ); if ( codec ) { diff -Nru mlt-0.9.0/debian/patches/02-crash-empty-ladspa-path.diff mlt-0.9.2/debian/patches/02-crash-empty-ladspa-path.diff --- mlt-0.9.0/debian/patches/02-crash-empty-ladspa-path.diff 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/patches/02-crash-empty-ladspa-path.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -# Upstream patch to fix crash if LADSPA_PATH is empty. -# Closes: #726197 - -diff -Naur mlt-0.9.0.orig/src/modules/jackrack/plugin_mgr.c mlt-0.9.0/src/modules/jackrack/plugin_mgr.c ---- mlt-0.9.0.orig/src/modules/jackrack/plugin_mgr.c 2013-06-03 05:34:35.000000000 +0200 -+++ mlt-0.9.0/src/modules/jackrack/plugin_mgr.c 2013-10-17 14:49:57.147889337 +0200 -@@ -237,10 +237,8 @@ - ladspa_path = g_strdup ("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa"); - #endif - -- dir = strtok (ladspa_path, ":"); -- do -+ for (dir = strtok (ladspa_path, ":"); dir; dir = strtok (NULL, ":")) - plugin_mgr_get_dir_plugins (plugin_mgr, dir); -- while ((dir = strtok (NULL, ":"))); - - g_free (ladspa_path); - } diff -Nru mlt-0.9.0/debian/patches/03-freetype-ftbfs.diff mlt-0.9.2/debian/patches/03-freetype-ftbfs.diff --- mlt-0.9.0/debian/patches/03-freetype-ftbfs.diff 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/patches/03-freetype-ftbfs.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# Upstream patch to fix FTBFS with newer freetype versions. -# Closes: #733375 - -diff -Naur mlt-0.9.0.orig/src/modules/gtk2/Makefile mlt-0.9.0/src/modules/gtk2/Makefile ---- mlt-0.9.0.orig/src/modules/gtk2/Makefile 2013-06-03 05:34:35.000000000 +0200 -+++ mlt-0.9.0/src/modules/gtk2/Makefile 2014-01-03 11:43:13.518109987 +0100 -@@ -37,6 +37,7 @@ - OBJS += producer_count.o - OBJS += filter_dynamictext.o - CFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --cflags pangoft2` -+CFLAGS += `pkg-config --cflags-only-I freetype2 | sed 's/ *$$//g')`/freetype - LDFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --libs pangoft2` - ifeq ($(targetos),Darwin) - LDFLAGS += -liconv -diff -Naur mlt-0.9.0.orig/src/modules/gtk2/producer_pango.c mlt-0.9.0/src/modules/gtk2/producer_pango.c ---- mlt-0.9.0.orig/src/modules/gtk2/producer_pango.c 2013-06-03 05:34:35.000000000 +0200 -+++ mlt-0.9.0/src/modules/gtk2/producer_pango.c 2014-01-03 11:41:10.270113314 +0100 -@@ -25,7 +25,7 @@ - #include - #include - #include --#include -+#include - #include - #include - #include diff -Nru mlt-0.9.0/debian/patches/series mlt-0.9.2/debian/patches/series --- mlt-0.9.0/debian/patches/series 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/patches/series 2014-07-09 08:06:53.000000000 +0000 @@ -1,3 +1 @@ 01-changed-preset-path.diff -02-crash-empty-ladspa-path.diff -03-freetype-ftbfs.diff diff -Nru mlt-0.9.0/debian/python-mlt.lintian-overrides mlt-0.9.2/debian/python-mlt.lintian-overrides --- mlt-0.9.0/debian/python-mlt.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/debian/python-mlt.lintian-overrides 2014-07-09 08:06:53.000000000 +0000 @@ -0,0 +1,2 @@ +python-mlt: hardening-no-relro usr/lib/python*/dist-packages/_mlt.so +python-mlt: hardening-no-fortify-functions usr/lib/python*/dist-packages/_mlt.so diff -Nru mlt-0.9.0/debian/rules mlt-0.9.2/debian/rules --- mlt-0.9.0/debian/rules 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/rules 2014-07-09 08:06:53.000000000 +0000 @@ -1,7 +1,5 @@ #!/usr/bin/make -f -export DEB_BUILD_HARDENING=1 - # Enable MMX and SSE on amd64 architecture ifeq ($(DEB_HOST_ARCH),$(findstring $(DEB_HOST_ARCH), "amd64")) EXTRA_CONFIGURE_OPTS += --enable-mmx --enable-sse diff -Nru mlt-0.9.0/debian/source.lintian-overrides mlt-0.9.2/debian/source.lintian-overrides --- mlt-0.9.0/debian/source.lintian-overrides 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/source.lintian-overrides 2014-07-09 08:06:53.000000000 +0000 @@ -1 +1,2 @@ mlt source: debian-watch-may-check-gpg-signature +mlt source: intra-source-package-circular-dependency libmlt++3 libmlt6 diff -Nru mlt-0.9.0/debian/watch mlt-0.9.2/debian/watch --- mlt-0.9.0/debian/watch 2014-01-03 11:01:26.000000000 +0000 +++ mlt-0.9.2/debian/watch 2014-07-09 08:06:53.000000000 +0000 @@ -1,3 +1,3 @@ version=3 -opts=dversionmangle=s/\+git\d+// \ +opts=dversionmangle=s/\+git\d+//;s/\+dfsg\d// \ http://sf.net/mlt/mlt-([\d.]*)\.tar\.gz diff -Nru mlt-0.9.0/demo/mlt_swf_variables mlt-0.9.2/demo/mlt_swf_variables --- mlt-0.9.0/demo/mlt_swf_variables 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/demo/mlt_swf_variables 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -melt clip1.dv \ --track txtField.swf variables="title=My Title&subtitle=The Subtitle" out=120 \ --transition composite geometry="0/0:100%x100%:80%;100=0/0:100%x100%:80%;119=0/0:100%x100%:0" \ -a_track=0 b_track=1 out=120 progressive=1 \ -$* Binary files /tmp/CABKy6dhm7/mlt-0.9.0/demo/txtField.swf and /tmp/vfZp2XnQlO/mlt-0.9.2/demo/txtField.swf differ diff -Nru mlt-0.9.0/docs/install.txt mlt-0.9.2/docs/install.txt --- mlt-0.9.0/docs/install.txt 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/docs/install.txt 2014-06-29 20:23:17.000000000 +0000 @@ -1,166 +1,157 @@ -Installation Documentation +---+ Installation Documentation -Copyright (C) 2004-2009 Ushodaya Enterprises Limited -Author: Charles Yates -Last Revision: 2009-05-08 +Last Revision: 2013-09-07 + This document provides a description of the MLT project installation and + organisation. -INSTALL -------- - This document provides a description of the MLT project installation and - organisation. +---++ Directories + The directory heirarchy is defined as follows: -Directories ------------ + * demo - A selection of samples to show off capabilities. + * docs - Location of all documentation + * presets - Properties presets for various services + * profiles - MLT profile configurations + * src - All project source is provided here + * framework - The MLT media framework + * melt - A media playing test application (*) + * mlt++ - C++ wrapper for framework + * modules - All services are defined here + * avformat - FFmpeg/Libav dependent services + * avsync - services to help test audio/video synchronization + * core - independent MLT services + * decklink - Blackmagick Design SDI/HDMI services + * dgraft - ports of Donald Graft's filters (*) + * dv - libdv dependent services + * effectv - ports of !EffecTV filters (*) + * feeds - templates for use with core's data filters + * frei0r - adapter for frei0r video plugins + * gtk2 - GTK+ pango and pixbuf dependent services + * jackrack - adapter for LADSPA audio plugins and JACK server + * kdenlive - services contributed by Kdenlive project + * kino - DV/AVI demuxer from Kino project (*) + * linsys - DVEO SDI card consumer (*) + * lumas - wipe file generator for core's luma transition + * motion_est - motion estimation-based filters (*) + * normalize - audio normalisation functions (*) + * oldfilm - filters to make pristine video dirty + * opengl - !OpenGL dependent services (*) + * plus - miscellaneous services (pending move to core) + * qt - Qt dependent services (*) + * resample - libresample dependent services (*) + * rotoscoping - spline-based alpha mask filter (*) + * rtaudio - audio consumer based on !RtAudio project code + * sdl - SDL dependent services + * sox - !SoX dependent audio filters + * swfdec - Swfdec dependent producer for Flash files + * videostab - video stabilization filters (*) + * vmfx - services contributed by (defunct) Visual Media FX + * vorbis - vorbis dependenent services + * xine - Xine-derived sources (*) + * xml - XML (de)serialization services + * swig - High level language bindings using SWIG + * tests - Reserved for regression and unit tests + * win32 - Windows-specific helper functions - The directory heirarchy is defined as follows: + Additional subdirectories may be nested below those shown and should be + documented in their parent. - + demo - A selection of samples to show off capabilities. - + docs - Location of all documentation - + src - All project source is provided here - + framework - The mlt media framework - + modules - All services are defined here - + avformat - libavformat dependent services - + core - Independent MLT services - + dv - libdv dependent services - + gtk2 - pango and pixbuf dependent services - + normalize - audio normalisation functions (**) - + plus - throwaway silliness - + resample - libresample dependent services (**) - + sdl - SDL dependent services - + vorbis - vorbis dependenent services - + xml - XML (de)serialization services - + xine - Xine-derived sources (**) - + melt - A media playing test application (**) - + tests - Reserved for regression and unit tests + (*) Contains GPL dependencies or code. - Additional subdirectories may be nested below those shown and should be - documented in their parent. - (*) Not posted to CVS due to licensing issues. - (**) Contains GPL dependencies or code. +---++ Dependencies + The MLT core is dependent on: -Dependencies ------------- + * a C99 compliant C compiler + * posix threading (pthread) + * standard posix libraries (libc) - The MLT core is dependent on: + The MLT applications and libraries provided are all dependent on the core. - * a C99 compliant C compiler - * posix threading - * standard posix libraries + The modules have the following dependencies: - The MLT applications and libraries provided are all dependent on the core. + | *Module* | *Description* | + | avformat | [[http://www.ffmpeg.org][FFmpeg]] or [[http://www.libav.org][libav]] v0.7 or later | + | dv | [[http://libdv.sf.net][libdv]] 0.102 or later | + | gtk2 | [[http://www.gtk.org][GTK2]] and associated dependencies | + | jackrack | [[http://jackaudio.org][JACK]], [[http://www.xmlsoft.org/][libxml2]], and ladspa.h | + | opengl | [[http://git.sesse.net/movit][Movit]] | + | qt | [[http://www.qt-project.org][Qt]] 4.4 or later | + | resample | [[http://www.mega-nerd.com/SRC][libsamplerate]] 0.15 or later | + | sdl | [[http://www.libsdl.org][SDL]] 1.2 or later | + | sox | [[http://sox.sourceforge.net][SoX]] 13 or later | + | swfdec | [[http://github.com/mltframework/swfdec][swfdec]] 0.8 or later | + | vorbis | [[http://www.vorbis.com][libvorbis]] 1.0.1 or later | + | xml | [[http://www.xmlsoft.org][libxml2]] 2.5 or later | - The modules have the following dependencies: - ----------- ---------------------------------------------------------- - MODULE DESCRIPTION - ----------- ---------------------------------------------------------- - avformat Provided from ffmpeg CVS and compiled as a shared library. - URL: http://ffmpeg.sf.net - ----------- ---------------------------------------------------------- - dv libdv 0.102 or later. - URL: http://libdv.sf.net - ----------- ---------------------------------------------------------- - gtk2 GTK2 and associated dependencies. - URL: http://www.gtk.org - ----------- ---------------------------------------------------------- - resample libsamplerate 0.15 or later - URL: http://www.mega-nerd.com/SRC/ (GPL) - ----------- ---------------------------------------------------------- - sdl SDL 1.2 or later. - URL: http://www.libsdl.org - ----------- ---------------------------------------------------------- - vorbis libvorbis 1.0.1 or later. - URL: http://www.vorbis.com/ - ----------- ---------------------------------------------------------- - xml libxml2 2.5 or later. - URL: http://www.xmlsoft.org/ - ----------- ---------------------------------------------------------- +---++ Configuration + Configuration is triggered from the top level directory via a + ./configure script. -Configuration -------------- + Each source bearing subdirectory shown above have their own configure + script which are called automatically from the top level. - Configuration is triggered from the top level directory via a - ./configure script. + Typically, new modules can be introduced without modification to the + configure script and arguments are accepted and passed through to all + subdirectories. - Each source bearing subdirectory shown above have their own configure - script which are called automatically from the top level. + More information on usage is found by running: - Typically, new modules can be introduced without modification to the - configure script and arguments are accepted and passed through to all - subdirectories. + =./configure --help= - More information on usage is found by running: + Note: This script must be run to register new services after a git clone + or subsequent update. - ./configure --help +---++ Compilation - NB: This script must be run to register new services after a CVS checkout - or subsequent update. + Makefiles are generated during configuration and these are based on + a per directory template which must be provided by the developer. - -Compilation ------------ - Makefiles are generated during configuration and these are based on - a per directory template which must be provided by the developer. +---++ Testing + To execute the MLT tools without installation, or to test a new version + on a system with an already installed MLT version in a Bash shell run: -Testing -------- + =source setenv= - To execute the mlt tools without installation, or to test a new version - on a system with an already installed mlt version, you should run: + Note: This applies to your current shell only and it assumes sh or bash. - . setenv - NB: This applies to your current shell only and it assumes sh or bash. +---++ Installation + The install is triggered by running make install from the top level + directory. -Installation ------------- + The framework produces a single shared object which is installed in + $prefix/lib/ and public header files which are installed in + $prefix/include/mlt/framework and $prefix/include/mlt++. - The install is triggered by running make install from the top level - directory. - - The framework produces a single shared object which is installed in - $prefix/lib/ and public header files which are installed in - $prefix/include/mlt/framework. + The modules produce a shared object per module installed to + $prefix/lib/mlt. Also, each module may have support files installed to + $prefix/share/mlt/modules. - The modules produce a shared object per module and update text files - containing a list of modules provided by this build. These are installed - in $prefix/share/mlt/modules. It is at the discretion of the module to - install additional support files. - - To allow the development of external components, mlt-config and scripts - are generated and installed in $prefix/bin. + For the development of modules and applications, pkg-config metadata files + are generated and installed in $prefix/lib/pkgconfig. - After install, only those modules listed are usable by the server. No - module is loaded unless explicitly requested via server configuration - or usage. - External modules are also placed in this $prefix/share/mlt/modules, and the - installation of those must modify the text file accordingly before they - will be considered at runtime. +---++ Development + All compilation in the project has {top-level-dir}/src on the include path. + All headers are included as: -Development ------------ + #include - All compilation in the project has {top-level-dir}/src on the include path. - All headers are included as: + All external modules have {prefix}/include/mlt on the include path. All + headers should also be included as: - #include - - All external modules have {prefix}/include/mlt on the include path. All - headers should also be included as: + #include - #include - - This allows migration of source between external and internal modules. - The configuration and Makefile template requirements will require - attention though. + This allows migration of source between external and internal modules. + The configuration and Makefile template requirements will require + attention though. diff -Nru mlt-0.9.0/docs/melt.1 mlt-0.9.2/docs/melt.1 --- mlt-0.9.0/docs/melt.1 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/docs/melt.1 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.4. -.TH MELT "1" "June 2013" "melt 0.9.0" "User Commands" +.TH MELT "1" "June 2014" "melt 0.9.2" "User Commands" .SH NAME melt \- author, play, and encode multitrack audio/video compositions .SH SYNOPSIS diff -Nru mlt-0.9.0/Doxyfile mlt-0.9.2/Doxyfile --- mlt-0.9.0/Doxyfile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/Doxyfile 2014-06-29 20:23:17.000000000 +0000 @@ -31,7 +31,7 @@ # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.9.0 +PROJECT_NUMBER = 0.9.2 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff -Nru mlt-0.9.0/.gitignore mlt-0.9.2/.gitignore --- mlt-0.9.0/.gitignore 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/.gitignore 2014-06-29 20:23:17.000000000 +0000 @@ -16,3 +16,7 @@ *.dylib src/swig/java/src_swig disable-* +mlt.config +mlt.creator +mlt.files +mlt.includes diff -Nru mlt-0.9.0/Makefile mlt-0.9.2/Makefile --- mlt-0.9.0/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -62,7 +62,7 @@ git archive --format=tar --prefix=mlt-$(version)/ v$(version) | gzip >mlt-$(version).tar.gz validate-yml: - for file in `find src/modules -type f -name \*.yml`; do \ + for file in $$(find src/modules -type f -name \*.yml); do \ echo "validate: $$file"; \ kwalify -f src/framework/metaschema.yaml $$file || exit 1; \ done diff -Nru mlt-0.9.0/NEWS mlt-0.9.2/NEWS --- mlt-0.9.0/NEWS 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/NEWS 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,51 @@ MLT Release Notes ----------------- +Version 0.9.2 - June 29, 2014 + +This is a bugfix and minor enhancement release. + +Framework + * Added "boolean" parameter type and "argument" parameter attribute to + service metadata schema. + * Added mlt_properties_frames_to_time(). + * Added mlt_properties_time_to_frames(). + * Changed mlt_events_fire() to return the number of listeners. + * Added consumer-thread-create and consumer-thread-join events. + * Added LC_NUMERIC handling for Windows. + * Added mlt_playlist_mix_out() and mlt_playlist_mix_in() + * Added mlt_properties_from_utf8() + +Modules + * Renamed "qimage" module to "qt". + * Added support for Qt 5. + * Added qtext producer, which is now prefered by dynamictext filter over pango. + * Added xml-nogl consumer. + * Consolidated dgraft, burningtv, and rotoscoping into new plusgpl module. + * Added vid.stab module with vidstab and deshake filters. + This depends on external vid.stab library unlike its predecessors. + * Rewrote opengl module. + * Added loudness filter based on EBU R128. + * Added rgblut filter to plus module. + * Added luma-only liftgammagain filter to plusgpl module. + * Added lift_gamma_gain filter to plus module. + * Added support new keyframable animated properties to brightness, volume, + panner, boxblur, wave, sepia, charcoal, burn, gamma, grain, dust, lines, + tcolor, oldfilm, + * Added movit.luma transition to opengl module. + * Added cbrts consumer to plusgpl module. + * Removed the "ppm" PPM-pipe producer. + * Added more VITC functionality to decklink module. + * Added matte transition to core module. + +DEPRECATION WARNINGS + * Deprecate videostab module with videostab and videostab filters. + This will not be removed soon in order to keep old scripts and projects + functional. + * Deprecate the dv (libdv), kino, and vorbis modules. These are scheduled + to be removed in next release. + + Version 0.9.0 - June 2, 2013 This is a significant enhancement release. diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_ntsc/DV mlt-0.9.2/presets/consumer/avformat/dv_ntsc/DV --- mlt-0.9.0/presets/consumer/avformat/dv_ntsc/DV 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_ntsc/DV 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_ntsc/DVCPRO50 mlt-0.9.2/presets/consumer/avformat/dv_ntsc/DVCPRO50 --- mlt-0.9.0/presets/consumer/avformat/dv_ntsc/DVCPRO50 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_ntsc/DVCPRO50 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_ntsc_wide/DV mlt-0.9.2/presets/consumer/avformat/dv_ntsc_wide/DV --- mlt-0.9.0/presets/consumer/avformat/dv_ntsc_wide/DV 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_ntsc_wide/DV 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50 mlt-0.9.2/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50 --- mlt-0.9.0/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_pal/DV mlt-0.9.2/presets/consumer/avformat/dv_pal/DV --- mlt-0.9.0/presets/consumer/avformat/dv_pal/DV 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_pal/DV 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_pal/DVCPRO50 mlt-0.9.2/presets/consumer/avformat/dv_pal/DVCPRO50 --- mlt-0.9.0/presets/consumer/avformat/dv_pal/DVCPRO50 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_pal/DVCPRO50 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_pal_wide/DV mlt-0.9.2/presets/consumer/avformat/dv_pal_wide/DV --- mlt-0.9.0/presets/consumer/avformat/dv_pal_wide/DV 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_pal_wide/DV 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format diff -Nru mlt-0.9.0/presets/consumer/avformat/dv_pal_wide/DVCPRO50 mlt-0.9.2/presets/consumer/avformat/dv_pal_wide/DVCPRO50 --- mlt-0.9.0/presets/consumer/avformat/dv_pal_wide/DVCPRO50 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/dv_pal_wide/DVCPRO50 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,7 @@ +f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le +meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV diff -Nru mlt-0.9.0/presets/consumer/avformat/lossless/FFV1 mlt-0.9.2/presets/consumer/avformat/lossless/FFV1 --- mlt-0.9.0/presets/consumer/avformat/lossless/FFV1 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/lossless/FFV1 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -f=mkv +f=matroska acodec=flac vcodec=ffv1 threads=1 diff -Nru mlt-0.9.0/presets/consumer/avformat/lossless/HuffYUV mlt-0.9.2/presets/consumer/avformat/lossless/HuffYUV --- mlt-0.9.0/presets/consumer/avformat/lossless/HuffYUV 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/lossless/HuffYUV 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -f=mkv +f=matroska acodec=flac vcodec=huffyuv diff -Nru mlt-0.9.0/presets/consumer/avformat/MP3 mlt-0.9.2/presets/consumer/avformat/MP3 --- mlt-0.9.0/presets/consumer/avformat/MP3 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/MP3 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,5 @@ f=mp3 -acodec=mp3 +acodec=libmp3lame ab=256k vn=1 video_off=1 diff -Nru mlt-0.9.0/presets/consumer/avformat/MPEG-4 mlt-0.9.2/presets/consumer/avformat/MPEG-4 --- mlt-0.9.0/presets/consumer/avformat/MPEG-4 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/MPEG-4 2014-06-29 20:23:17.000000000 +0000 @@ -14,5 +14,7 @@ subcmp=satd progressive=1 +movflags=+faststart + meta.preset.extension=mp4 meta.preset.note=Part 2 Simple Profile diff -Nru mlt-0.9.0/presets/consumer/avformat/MPEG-4-ASP mlt-0.9.2/presets/consumer/avformat/MPEG-4-ASP --- mlt-0.9.0/presets/consumer/avformat/MPEG-4-ASP 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/MPEG-4-ASP 2014-06-29 20:23:17.000000000 +0000 @@ -11,9 +11,10 @@ trellis=1 cmp=satd subcmp=satd - bf=2 flags=+mv4+aic+qpel +movflags=+faststart + meta.preset.extension=mp4 meta.preset.note=Part 2 Advanced Simple Profile diff -Nru mlt-0.9.0/presets/consumer/avformat/vp9 mlt-0.9.2/presets/consumer/avformat/vp9 --- mlt-0.9.0/presets/consumer/avformat/vp9 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/vp9 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,26 @@ +f=webm + +acodec=vorbis +ar=48000 +ab=128k + +vcodec=libvpx-vp9 +strict=experimental +vb=2M +g=120 +bf=2 +threads=0 +rc_lookahead=16 +quality=good +speed=4 +vprofile=0 +qmax=51 +qmin=11 +slices=4 +arnr_max_frames=7 +arnr_strength=5 +arnr_type=3 + +meta.preset.name=WebM VP9 +meta.preset.extension=webm +meta.preset.note=VP9 video with Ogg Vorbis audio in Matroska container: "Don't be evil" diff -Nru mlt-0.9.0/presets/consumer/avformat/x264-medium mlt-0.9.2/presets/consumer/avformat/x264-medium --- mlt-0.9.0/presets/consumer/avformat/x264-medium 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/x264-medium 2014-06-29 20:23:17.000000000 +0000 @@ -8,5 +8,7 @@ vpre=medium preset=medium +movflags=+faststart + meta.preset.extension=mp4 meta.preset.name=H.264 High Profile diff -Nru mlt-0.9.0/presets/consumer/avformat/x264-medium-baseline mlt-0.9.2/presets/consumer/avformat/x264-medium-baseline --- mlt-0.9.0/presets/consumer/avformat/x264-medium-baseline 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/x264-medium-baseline 2014-06-29 20:23:17.000000000 +0000 @@ -13,5 +13,7 @@ flags2=-wpred-dct8x8 wpredp=0 +movflags=+faststart + meta.preset.extension=mp4 meta.preset.name=H.264 Baseline Profile diff -Nru mlt-0.9.0/presets/consumer/avformat/x264-medium-main mlt-0.9.2/presets/consumer/avformat/x264-medium-main --- mlt-0.9.0/presets/consumer/avformat/x264-medium-main 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/x264-medium-main 2014-06-29 20:23:17.000000000 +0000 @@ -10,5 +10,7 @@ vprofile=main flags2=-dct8x8 +movflags=+faststart + meta.preset.extension=mp4 meta.preset.name=H.264 Main Profile diff -Nru mlt-0.9.0/presets/consumer/avformat/x264-medium-pass1 mlt-0.9.2/presets/consumer/avformat/x264-medium-pass1 --- mlt-0.9.0/presets/consumer/avformat/x264-medium-pass1 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/presets/consumer/avformat/x264-medium-pass1 2014-06-29 20:23:17.000000000 +0000 @@ -15,4 +15,6 @@ an=1 audio_off=1 +movflags=+faststart + meta.preset.hidden=1 diff -Nru mlt-0.9.0/presets/filter/movit.opacity/fade_in mlt-0.9.2/presets/filter/movit.opacity/fade_in --- mlt-0.9.0/presets/filter/movit.opacity/fade_in 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/presets/filter/movit.opacity/fade_in 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,2 @@ +opacity=0=0;:2.0=1.0 +alpha=1 diff -Nru mlt-0.9.0/presets/filter/movit.opacity/fade_in_out mlt-0.9.2/presets/filter/movit.opacity/fade_in_out --- mlt-0.9.0/presets/filter/movit.opacity/fade_in_out 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/presets/filter/movit.opacity/fade_in_out 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,2 @@ +opacity=0=0;:2.0=1.0; :-2.0=1.0; -1=0 +alpha=1 diff -Nru mlt-0.9.0/presets/filter/movit.opacity/fade_out mlt-0.9.2/presets/filter/movit.opacity/fade_out --- mlt-0.9.0/presets/filter/movit.opacity/fade_out 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/presets/filter/movit.opacity/fade_out 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,2 @@ +opacity=:-2.0=1.0; -1=0 +alpha=1 diff -Nru mlt-0.9.0/src/examples/Makefile mlt-0.9.2/src/examples/Makefile --- mlt-0.9.0/src/examples/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/examples/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,5 @@ -CXXFLAGS += -Wall -g `pkg-config mlt++ --cflags` -LDFLAGS += `pkg-config mlt++ --libs` +CXXFLAGS += -Wall -g $(shell pkg-config mlt++ --cflags) +LDFLAGS += $(shell pkg-config mlt++ --libs) CC=c++ all: play diff -Nru mlt-0.9.0/src/framework/Makefile mlt-0.9.2/src/framework/Makefile --- mlt-0.9.0/src/framework/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -81,6 +81,7 @@ SRCS := $(OBJS:.o=.c) ifeq ($(targetos), MinGW) +LDFLAGS += -liconv OBJS += ../win32/win32.o SRCS += ../win32/win32.c endif diff -Nru mlt-0.9.0/src/framework/metaschema.yaml mlt-0.9.2/src/framework/metaschema.yaml --- mlt-0.9.0/src/framework/metaschema.yaml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/metaschema.yaml 2014-06-29 20:23:17.000000000 +0000 @@ -1,5 +1,5 @@ --- # A metadata schema in Kwalify: http://www.kuwata-lab.com/kwalify/ -# Version: 0.1 +# Version: 0.2 type: map mapping: "schema_version": # This should match the version comment above @@ -69,18 +69,22 @@ "type": # An mlt_property_type type: str enum: + - boolean # 0 or 1; not 'true', 'false', 'yes', or 'no' strings at this time - float - geometry - integer - properties # for passing options to encapsulated services - string - - time # currently, mlt_position (frame), soon to be a time value + - time # time string values (clock, SMPTE) can be acccepted in addition to frames "service-name": # for type: properties, a reference to another service type: str # format: type.service, e.g. transition.composite "title": # A UI can use this for a field label type: str "description": # A UI can use this for a tool tip or what's-this type: str + "argument": # If this is also the service constructor argument. + type: bool + default: no "readonly": # If you set this property, it will be ignored type: bool default: no diff -Nru mlt-0.9.0/src/framework/mlt_animation.c mlt-0.9.2/src/framework/mlt_animation.c --- mlt-0.9.0/src/framework/mlt_animation.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_animation.c 2014-06-29 20:23:17.000000000 +0000 @@ -613,7 +613,7 @@ item.is_key = 1; } // Typically, we move from keyframe to keyframe - else if ( item.frame < out ) + else if ( item.frame <= out ) { if ( mlt_animation_next_key( self, &item, item.frame ) ) break; diff -Nru mlt-0.9.0/src/framework/mlt_consumer.c mlt-0.9.2/src/framework/mlt_consumer.c --- mlt-0.9.0/src/framework/mlt_consumer.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_consumer.c 2014-06-29 20:23:17.000000000 +0000 @@ -49,9 +49,10 @@ { int real_time; int ahead; + int preroll; mlt_image_format format; mlt_deque queue; - pthread_t ahead_thread; + void *ahead_thread; pthread_mutex_t queue_mutex; pthread_cond_t queue_cond; pthread_mutex_t put_mutex; @@ -74,11 +75,18 @@ } consumer_private; +typedef void* ( *thread_function_t )( void* ); + static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_consumer_property_changed( mlt_properties owner, mlt_consumer self, char *name ); static void apply_profile_properties( mlt_consumer self, mlt_profile profile, mlt_properties properties ); static void on_consumer_frame_show( mlt_properties owner, mlt_consumer self, mlt_frame frame ); +static void transmit_thread_create( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); +static void mlt_thread_create( mlt_consumer self, thread_function_t function ); +static void transmit_thread_join( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); +static void mlt_thread_join( mlt_consumer self ); +static void consumer_read_ahead_start( mlt_consumer self ); /** Initialize a consumer service. * @@ -140,7 +148,10 @@ mlt_events_register( properties, "consumer-frame-render", ( mlt_transmitter )mlt_consumer_frame_render ); mlt_events_register( properties, "consumer-thread-started", NULL ); mlt_events_register( properties, "consumer-thread-stopped", NULL ); + mlt_events_register( properties, "consumer-stopping", NULL ); mlt_events_register( properties, "consumer-stopped", NULL ); + mlt_events_register( properties, "consumer-thread-create", ( mlt_transmitter )transmit_thread_create ); + mlt_events_register( properties, "consumer-thread-join", ( mlt_transmitter )transmit_thread_join ); mlt_events_listen( properties, self, "consumer-frame-show", ( mlt_listener )on_consumer_frame_show ); // Register a property-changed listener to handle the profile property - @@ -178,7 +189,7 @@ mlt_properties_set_int( properties, "sample_aspect_den", profile->sample_aspect_den ); mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) ); mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num ); - mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num ); + mlt_properties_set_int( properties, "display_aspect_den", profile->display_aspect_den ); mlt_properties_set_int( properties, "colorspace", profile->colorspace ); mlt_event_unblock( priv->event_listener ); } @@ -532,6 +543,12 @@ priv->format = mlt_image_yuv422; } + priv->preroll = 1; +#ifdef WIN32 + if ( priv->real_time == 1 || priv->real_time == -1 ) + consumer_read_ahead_start( self ); +#endif + // Start the service if ( self->start != NULL ) return self->start( self ); @@ -806,6 +823,9 @@ continue; pos = mlt_frame_get_position( frame ); + // WebVfx uses this to setup a consumer-stopping event handler. + mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); + // Increment the counter used for averaging processing cost count ++; @@ -908,7 +928,18 @@ // Remove the last frame mlt_frame_close( frame ); - mlt_events_fire( properties, "consumer-thread-stopped", NULL ); + + // Wipe the queue + pthread_mutex_lock( &priv->queue_mutex ); + while ( mlt_deque_count( priv->queue ) ) + mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); + + // Close the queue + mlt_deque_close( priv->queue ); + priv->queue = NULL; + pthread_mutex_unlock( &priv->queue_mutex ); + + mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", NULL ); return NULL; } @@ -1001,6 +1032,9 @@ if ( frame == NULL ) continue; + // WebVfx uses this to setup a consumer-stopping event handler. + mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); + #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED // All non normal playback frames should be shown if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 ) @@ -1024,7 +1058,6 @@ pthread_cond_broadcast( &priv->done_cond ); pthread_mutex_unlock( &priv->done_mutex ); } - mlt_events_fire( properties, "consumer-thread-stopped", NULL ); return NULL; } @@ -1039,6 +1072,9 @@ { consumer_private *priv = self->local; + if ( priv->started ) + return; + // We're running now priv->ahead = 1; @@ -1052,24 +1088,7 @@ pthread_cond_init( &priv->queue_cond, NULL ); // Create the read ahead - if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( self ), "priority" ) ) - { - struct sched_param priority; - priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self ), "priority" ); - pthread_attr_t thread_attributes; - pthread_attr_init( &thread_attributes ); - pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER ); - pthread_attr_setschedparam( &thread_attributes, &priority ); - pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED ); - pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM ); - if ( pthread_create( &priv->ahead_thread, &thread_attributes, consumer_read_ahead_thread, self ) < 0 ) - pthread_create( &priv->ahead_thread, NULL, consumer_read_ahead_thread, self ); - pthread_attr_destroy( &thread_attributes ); - } - else - { - pthread_create( &priv->ahead_thread, NULL, consumer_read_ahead_thread, self ); - } + mlt_thread_create( self, (thread_function_t) consumer_read_ahead_thread ); priv->started = 1; } @@ -1083,7 +1102,12 @@ { consumer_private *priv = self->local; int n = abs( priv->real_time ); - pthread_t *thread = calloc( 1, sizeof( pthread_t ) * n ); + pthread_t *thread; + + if ( priv->started ) + return; + + thread = calloc( 1, sizeof( pthread_t ) * n ); // We're running now priv->ahead = 1; @@ -1168,6 +1192,7 @@ #endif // Inform thread to stop priv->ahead = 0; + mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", NULL ); // Broadcast to the condition in case it's waiting pthread_mutex_lock( &priv->queue_mutex ); @@ -1180,20 +1205,13 @@ pthread_mutex_unlock( &priv->put_mutex ); // Join the thread - pthread_join( priv->ahead_thread, NULL ); + mlt_thread_join( self ); // Destroy the frame queue mutex pthread_mutex_destroy( &priv->queue_mutex ); // Destroy the condition pthread_cond_destroy( &priv->queue_cond ); - - // Wipe the queue - while ( mlt_deque_count( priv->queue ) ) - mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); - - // Close the queue - mlt_deque_close( priv->queue ); } } @@ -1218,6 +1236,7 @@ #endif // Inform thread to stop priv->ahead = 0; + mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", NULL ); // Broadcast to the queue condition in case it's waiting pthread_mutex_lock( &priv->queue_mutex ); @@ -1258,6 +1277,8 @@ // Close the queues mlt_deque_close( priv->queue ); mlt_deque_close( priv->worker_threads ); + + mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", NULL ); } } @@ -1281,14 +1302,15 @@ pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); - if ( priv->started && priv->real_time ) - pthread_mutex_lock( &priv->queue_mutex ); - if ( self->purge ) self->purge( self ); + if ( priv->started && priv->real_time ) + pthread_mutex_lock( &priv->queue_mutex ); + while ( priv->started && mlt_deque_count( priv->queue ) ) mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); + if ( priv->started && priv->real_time ) { priv->is_purge = 1; @@ -1478,14 +1500,16 @@ { int size = 1; - // Is the read ahead running? - if ( priv->ahead == 0 ) + if ( priv->preroll ) { int buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); +#ifndef WIN32 consumer_read_ahead_start( self ); +#endif if ( buffer > 1 ) size = prefill > 0 && prefill < buffer ? prefill : buffer; + priv->preroll = 0; } // Get frame from queue @@ -1508,7 +1532,12 @@ // This isn't true, but from the consumers perspective it is if ( frame != NULL ) + { mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); + + // WebVfx uses this to setup a consumer-stopping event handler. + mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); + } } return frame; @@ -1551,7 +1580,6 @@ mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping consumer\n" ); // Cancel the read ahead threads - priv->ahead = 0; if ( priv->started ) { // Unblock the consumer calling mlt_consumer_rt_frame @@ -1649,4 +1677,66 @@ { return ( ( consumer_private* ) consumer->local )->position; } - + +static void transmit_thread_create( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) +{ + if ( listener ) + listener( owner, self, + (void**) args[0] /* handle */, (int*) args[1] /* priority */, (thread_function_t) args[2], (void*) args[3] /* data */ ); +} + +static void mlt_thread_create( mlt_consumer self, thread_function_t function ) +{ + consumer_private *priv = self->local; + mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); + + if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( self ), "priority" ) ) + { + struct sched_param priority; + priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self ), "priority" ); + if ( mlt_events_fire( properties, "consumer-thread-create", + &priv->ahead_thread, &priority.sched_priority, function, self, NULL ) < 1 ) + { + pthread_attr_t thread_attributes; + pthread_attr_init( &thread_attributes ); + pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER ); + pthread_attr_setschedparam( &thread_attributes, &priority ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM ); + priv->ahead_thread = malloc( sizeof( pthread_t ) ); + pthread_t *handle = priv->ahead_thread; + if ( pthread_create( ( pthread_t* ) &( *handle ), &thread_attributes, function, self ) < 0 ) + pthread_create( ( pthread_t* ) &( *handle ), NULL, function, self ); + pthread_attr_destroy( &thread_attributes ); + } + } + else + { + int priority = -1; + if ( mlt_events_fire( properties, "consumer-thread-create", + &priv->ahead_thread, &priority, function, self, NULL ) < 1 ) + { + priv->ahead_thread = malloc( sizeof( pthread_t ) ); + pthread_t *handle = priv->ahead_thread; + pthread_create( ( pthread_t* ) &( *handle ), NULL, function, self ); + } + } +} + +static void transmit_thread_join( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) +{ + if ( listener ) + listener( owner, self, (void*) args[0] /* handle */ ); +} + +static void mlt_thread_join( mlt_consumer self ) +{ + consumer_private *priv = self->local; + if ( mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-join", priv->ahead_thread, NULL ) < 1 ) + { + pthread_t *handle = priv->ahead_thread; + pthread_join( *handle, NULL ); + free( priv->ahead_thread ); + } + priv->ahead_thread = NULL; +} diff -Nru mlt-0.9.0/src/framework/mlt_consumer.h mlt-0.9.2/src/framework/mlt_consumer.h --- mlt-0.9.0/src/framework/mlt_consumer.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_consumer.h 2014-06-29 20:23:17.000000000 +0000 @@ -53,8 +53,13 @@ * \event \em consumer-frame-show Subclass implementations fire this immediately after showing a frame * or when a frame should be shown (if audio-only consumer). * \event \em consumer-frame-render The base class fires this immediately before rendering a frame. + * \event \em consumer-thread-create Override the implementation of creating and + * starting a thread by listening and responding to this (real_time 1 or -1 only). + * \event \em consumer-thread-join Override the implementation of waiting and + * joining a terminated thread by listening and responding to this (real_time 1 or -1 only). * \event \em consumer-thread-started The base class fires when beginning execution of a rendering thread. * \event \em consumer-thread-stopped The base class fires when a rendering thread has ended. + * \event \em consumer-stopping This is fired when stop was requested, but before render threads are joined. * \event \em consumer-stopped This is fired when the subclass implementation calls mlt_consumer_stopped(). * \properties \em fps video frames per second as floating point (read only) * \properties \em frame_rate_num the numerator of the video frame rate, overrides \p mlt_profile_s @@ -74,6 +79,8 @@ * Set this to -1 if the consumer does not care about the field order. * \properties \em mlt_image_format the image format to request in rendering threads, defaults to yuv422 * \properties \em mlt_audio_format the audio format to request in rendering threads, defaults to S16 + * \properties \em audio_off set non-zero to disable audio processing + * \properties \em video_off set non-zero to disable video processing */ struct mlt_consumer_s diff -Nru mlt-0.9.0/src/framework/mlt_events.c mlt-0.9.2/src/framework/mlt_events.c --- mlt-0.9.0/src/framework/mlt_events.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_events.c 2014-06-29 20:23:17.000000000 +0000 @@ -181,10 +181,12 @@ * \public \memberof mlt_events_struct * \param self a properties list * \param id the name of an event + * \return the number of listeners */ -void mlt_events_fire( mlt_properties self, const char *id, ... ) +int mlt_events_fire( mlt_properties self, const char *id, ... ) { + int result = 0; mlt_events events = mlt_events_fetch( self ); if ( events != NULL ) { @@ -215,10 +217,12 @@ transmitter( event->listener, event->owner, event->service, args ); else event->listener( event->owner, event->service ); + ++result; } } } } + return result; } /** Register a listener. diff -Nru mlt-0.9.0/src/framework/mlt_events.h mlt-0.9.2/src/framework/mlt_events.h --- mlt-0.9.0/src/framework/mlt_events.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_events.h 2014-06-29 20:23:17.000000000 +0000 @@ -44,7 +44,7 @@ extern void mlt_events_init( mlt_properties self ); extern int mlt_events_register( mlt_properties self, const char *id, mlt_transmitter transmitter ); -extern void mlt_events_fire( mlt_properties self, const char *id, ... ); +extern int mlt_events_fire( mlt_properties self, const char *id, ... ); extern mlt_event mlt_events_listen( mlt_properties self, void *service, const char *id, mlt_listener listener ); extern void mlt_events_block( mlt_properties self, void *service ); extern void mlt_events_unblock( mlt_properties self, void *service ); diff -Nru mlt-0.9.0/src/framework/mlt_factory.c mlt-0.9.2/src/framework/mlt_factory.c --- mlt-0.9.0/src/framework/mlt_factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -408,8 +408,12 @@ { mlt_properties_close( event_object ); event_object = NULL; +#if !defined(WIN32) + // XXX something in here is causing Shotcut/Win32 to not exit completely + // under certain conditions: e.g. play a playlist. mlt_properties_close( global_properties ); global_properties = NULL; +#endif if ( repository ) { mlt_repository_close( repository ); diff -Nru mlt-0.9.0/src/framework/mlt_filter.c mlt-0.9.2/src/framework/mlt_filter.c --- mlt-0.9.0/src/framework/mlt_filter.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_filter.c 2014-06-29 20:23:17.000000000 +0000 @@ -301,19 +301,30 @@ int disable = mlt_properties_get_int( properties, "disable" ); const char *unique_id = mlt_properties_get( properties, "_unique_id" ); mlt_position position = mlt_frame_get_position( frame ); - char name[20]; + char name[30]; // Make the properties key from unique id - snprintf( name, 20, "pos.%s", unique_id ); - name[20 -1] = '\0'; + snprintf( name, sizeof(name), "pos.%s", unique_id ); + name[sizeof(name) -1] = '\0'; // Save the position on the frame mlt_properties_set_position( MLT_FRAME_PROPERTIES( frame ), name, position ); if ( disable || self->process == NULL ) + { return frame; + } else + { + // Add a reference to this filter on the frame + mlt_properties_inc_ref( MLT_FILTER_PROPERTIES(self) ); + snprintf( name, sizeof(name), "filter.%s", unique_id ); + name[sizeof(name) -1] = '\0'; + mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), name, self, 0, + (mlt_destructor) mlt_filter_close, NULL ); + return self->process( self, frame ); + } } /** Get a frame from this filter. diff -Nru mlt-0.9.0/src/framework/mlt_filter.h mlt-0.9.2/src/framework/mlt_filter.h --- mlt-0.9.0/src/framework/mlt_filter.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_filter.h 2014-06-29 20:23:17.000000000 +0000 @@ -33,6 +33,7 @@ * \extends mlt_service_s * \properties \em track the index of the track of a multitrack on which the filter is applied * \properties \em service a reference to the service to which this filter is attached. + * \properties \em disable Set this to disable the filter while keeping it in the object model. * Currently this is not cleared when the filter is detached. */ diff -Nru mlt-0.9.0/src/framework/mlt_frame.c mlt-0.9.2/src/framework/mlt_frame.c --- mlt-0.9.0/src/framework/mlt_frame.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_frame.c 2014-06-29 20:23:17.000000000 +0000 @@ -437,6 +437,10 @@ case mlt_image_yuv420p: if ( bpp ) *bpp = 3 / 2; return width * height * 3 / 2; + case mlt_image_glsl: + case mlt_image_glsl_texture: + if ( bpp ) *bpp = 0; + return 4; default: if ( bpp ) *bpp = 0; return 0; @@ -475,7 +479,7 @@ mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); } } - if ( error && buffer && *format != mlt_image_none ) + if ( error && buffer ) { int size = 0; @@ -505,6 +509,10 @@ if ( *buffer ) memset( *buffer, 255, size ); break; + case mlt_image_none: + case mlt_image_glsl: + case mlt_image_glsl_texture: + *format = mlt_image_yuv422; case mlt_image_yuv422: size *= 2; size += *width * 2; @@ -737,26 +745,11 @@ mlt_properties_set_int( properties, "audio_samples", *samples ); mlt_properties_set_int( properties, "audio_format", *format ); - switch( *format ) - { - case mlt_image_none: - size = 0; - *buffer = NULL; - break; - case mlt_audio_s16: - size = *samples * *channels * sizeof( int16_t ); - break; - case mlt_audio_s32: - size = *samples * *channels * sizeof( int32_t ); - break; - case mlt_audio_float: - size = *samples * *channels * sizeof( float ); - break; - default: - break; - } + size = mlt_audio_format_size( *format, *samples, *channels ); if ( size ) *buffer = mlt_pool_alloc( size ); + else + *buffer = NULL; if ( *buffer ) memset( *buffer, 0, size ); mlt_properties_set_data( properties, "audio", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL ); @@ -764,7 +757,7 @@ } // TODO: This does not belong here - if ( *format == mlt_audio_s16 && mlt_properties_get( properties, "meta.volume" ) ) + if ( *format == mlt_audio_s16 && mlt_properties_get( properties, "meta.volume" ) && *buffer ) { double value = mlt_properties_get_double( properties, "meta.volume" ); @@ -1070,10 +1063,12 @@ data = mlt_properties_get_data( properties, "image", &size ); if ( data ) { + int width = mlt_properties_get_int( properties, "width" ); + int height = mlt_properties_get_int( properties, "height" ); + if ( ! size ) size = mlt_image_format_size( mlt_properties_get_int( properties, "format" ), - mlt_properties_get_int( properties, "width" ), - mlt_properties_get_int( properties, "height" ), NULL ); + width, height, NULL ); copy = mlt_pool_alloc( size ); memcpy( copy, data, size ); mlt_properties_set_data( new_props, "image", copy, size, mlt_pool_release, NULL ); @@ -1082,8 +1077,7 @@ if ( data ) { if ( ! size ) - size = mlt_properties_get_int( properties, "width" ) * - mlt_properties_get_int( properties, "height" ); + size = width * height; copy = mlt_pool_alloc( size ); memcpy( copy, data, size ); mlt_properties_set_data( new_props, "alpha", copy, size, mlt_pool_release, NULL ); diff -Nru mlt-0.9.0/src/framework/mlt_frame.h mlt-0.9.2/src/framework/mlt_frame.h --- mlt-0.9.0/src/framework/mlt_frame.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_frame.h 2014-06-29 20:23:17.000000000 +0000 @@ -157,4 +157,13 @@ u = ((-152*r - 300*g + 450*b) >> 10) + 128;\ v = ((450*r - 377*g - 73*b) >> 10) + 128; +/** This macro scales YUV up into the full gamut of the RGB color space. */ +#define YUV2RGB_601_SCALED( y, u, v, r, g, b ) \ + r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \ + g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 401 * ( u - 128 ) ) >> 10 ); \ + b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \ + r = r < 0 ? 0 : r > 255 ? 255 : r; \ + g = g < 0 ? 0 : g > 255 ? 255 : g; \ + b = b < 0 ? 0 : b > 255 ? 255 : b; + #endif diff -Nru mlt-0.9.0/src/framework/mlt_playlist.c mlt-0.9.2/src/framework/mlt_playlist.c --- mlt-0.9.0/src/framework/mlt_playlist.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_playlist.c 2014-06-29 20:23:17.000000000 +0000 @@ -919,7 +919,9 @@ if ( current == src ) current = dest; - else if ( current > src && current < dest ) + else if ( src < current && current < dest ) + current --; + else if ( dest < current && current < src ) current ++; else if ( current == dest ) current = src; @@ -1151,6 +1153,10 @@ /** Mix consecutive clips for a specified length and apply transition if specified. * + * This version of the mix function does not utilize any frames beyond the out of + * clip A or before the in point of clip B. It takes the frames needed for the length + * of the transition by adjusting the duration of both clips - the out point for clip A + * and the in point for clip B. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry @@ -1169,6 +1175,11 @@ mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new( ); + + mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), + mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); + mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), + mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); // Check length is valid for both clips and resize if necessary. @@ -1252,6 +1263,210 @@ return error; } +/** Mix consecutive clips for a specified length. + * + * This version of the mix function maintains the out point of the clip A by occupying the + * beginning of clip B before its current in point. Therefore, it ends up adjusting the in + * point and duration of clip B without affecting the duration of clip A. + * Also, therefore, there must be enough frames after the out point of clip A. + * \public \memberof mlt_playlist_s + * \param self a playlist + * \param clip the index of the playlist entry + * \param length the number of frames over which to create the mix + * \return true if there was an error + */ + +int mlt_playlist_mix_in( mlt_playlist self, int clip, int length ) +{ + int error = ( clip < 0 || clip + 1 >= self->count ); + if ( error == 0 ) + { + playlist_entry *clip_a = self->list[ clip ]; + playlist_entry *clip_b = self->list[ clip + 1 ]; + mlt_producer track_a = NULL; + mlt_producer track_b = NULL; + mlt_tractor tractor = mlt_tractor_new( ); + + mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), + mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); + mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), + mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); + mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); + + // Check length is valid for both clips and resize if necessary. + int max_size = ( clip_a->frame_out + 1 ) > clip_b->frame_count ? ( clip_a->frame_out + 1 ) : clip_b->frame_count; + length = length > max_size ? max_size : length; + + // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches + if ( length != clip_a->frame_out + 1 ) + track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out + 1, clip_a->frame_out + length ); + else + track_a = clip_a->producer; + + if ( length != clip_b->frame_count ) + track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1 ); + else + track_b = clip_b->producer; + + // Set the tracks on the tractor + mlt_tractor_set_track( tractor, track_a, 0 ); + mlt_tractor_set_track( tractor, track_b, 1 ); + + // Insert the mix object into the playlist + mlt_playlist_insert( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); + + // Close our references to the tracks if we created new cuts above (the tracks can still be used here) + if ( track_a != clip_a->producer ) + mlt_producer_close( track_a ); + if ( track_b != clip_b->producer ) + mlt_producer_close( track_b ); + + // Check if we have anything left on the right hand clip + if ( track_b == clip_b->producer ) + { + clip_b->preservation_hack = 1; + mlt_playlist_remove( self, clip + 2 ); + } + else if ( clip_b->frame_out - clip_b->frame_in >= length ) + { + mlt_playlist_resize_clip( self, clip + 2, clip_b->frame_in + length, clip_b->frame_out ); + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_b->producer ); + mlt_playlist_remove( self, clip + 2 ); + } + + // Check if we have anything left on the left hand clip + if ( track_a == clip_a->producer ) + { + clip_a->preservation_hack = 1; + mlt_playlist_remove( self, clip ); + } + else if ( clip_a->frame_out - clip_a->frame_in > 0 ) + { + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_a->producer ); + mlt_playlist_remove( self, clip ); + } + + // Unblock and force a fire off of change events to listeners + mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); + mlt_playlist_virtual_refresh( self ); + mlt_tractor_close( tractor ); + } + return error; +} + +/** Mix consecutive clips for a specified length. + * + * This version of the mix function maintains the in point of the B clip by occupying the + * end of clip A before its current out point. Therefore, it ends up adjusting the out + * point and duration of clip A without affecting the duration or starting frame of clip B. + * Also, therefore, there must be enough frames before the in point of clip B. + * \public \memberof mlt_playlist_s + * \param self a playlist + * \param clip the index of the playlist entry + * \param length the number of frames over which to create the mix + * \return true if there was an error + */ + +int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ) +{ + int error = ( clip < 0 || clip + 1 >= self->count ); + if ( error == 0 ) + { + playlist_entry *clip_a = self->list[ clip ]; + playlist_entry *clip_b = self->list[ clip + 1 ]; + mlt_producer track_a = NULL; + mlt_producer track_b = NULL; + mlt_tractor tractor = mlt_tractor_new( ); + + mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), + mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); + mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), + mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); + mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); + + // Check length is valid for both clips and resize if necessary. + int max_size = clip_a->frame_count > clip_b->frame_in ? clip_a->frame_count : clip_b->frame_in; + length = length > max_size ? max_size : length; + + // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches + if ( length != clip_a->frame_count ) + track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out ); + else + track_a = clip_a->producer; + + if ( length != clip_b->frame_in ) + track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in - length + 1, clip_b->frame_in ); + else + track_b = clip_b->producer; + + // Set the tracks on the tractor + mlt_tractor_set_track( tractor, track_a, 0 ); + mlt_tractor_set_track( tractor, track_b, 1 ); + + // Insert the mix object into the playlist + mlt_playlist_insert( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); + + // Close our references to the tracks if we created new cuts above (the tracks can still be used here) + if ( track_a != clip_a->producer ) + mlt_producer_close( track_a ); + if ( track_b != clip_b->producer ) + mlt_producer_close( track_b ); + + // Check if we have anything left on the right hand clip + if ( track_b == clip_b->producer ) + { + clip_b->preservation_hack = 1; + mlt_playlist_remove( self, clip + 2 ); + } + else if ( clip_b->frame_out - clip_b->frame_in > 0 ) + { + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_b->producer ); + mlt_playlist_remove( self, clip + 2 ); + } + + // Check if we have anything left on the left hand clip + if ( track_a == clip_a->producer ) + { + clip_a->preservation_hack = 1; + mlt_playlist_remove( self, clip ); + } + else if ( clip_a->frame_out - clip_a->frame_in >= length ) + { + mlt_playlist_resize_clip( self, clip, clip_a->frame_in, clip_a->frame_out - length ); + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL ); + mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL ); + } + else + { + mlt_producer_clear( clip_a->producer ); + mlt_playlist_remove( self, clip ); + } + + // Unblock and force a fire off of change events to listeners + mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); + mlt_playlist_virtual_refresh( self ); + mlt_tractor_close( tractor ); + } + return error; +} + /** Add a transition to an existing mix. * * \public \memberof mlt_playlist_s @@ -1495,7 +1710,7 @@ * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry - * \return true if there was an error + * \return true if \p clip is a "blank" producer */ int mlt_playlist_is_blank( mlt_playlist self, int clip ) @@ -1551,16 +1766,16 @@ * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the new blank section - * \param length the ending time of the new blank section (duration - 1) + * \param out the ending time of the new blank section (duration - 1) */ -void mlt_playlist_insert_blank( mlt_playlist self, int clip, int length ) +void mlt_playlist_insert_blank( mlt_playlist self, int clip, int out ) { - if ( self != NULL && length >= 0 ) + if ( self != NULL && out >= 0 ) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_events_block( properties, properties ); - mlt_playlist_blank( self, length ); + mlt_playlist_blank( self, out ); mlt_playlist_move( self, self->count - 1, clip ); mlt_events_unblock( properties, properties ); mlt_playlist_virtual_refresh( self ); diff -Nru mlt-0.9.0/src/framework/mlt_playlist.h mlt-0.9.2/src/framework/mlt_playlist.h --- mlt-0.9.0/src/framework/mlt_playlist.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_playlist.h 2014-06-29 20:23:17.000000000 +0000 @@ -63,6 +63,9 @@ * which is a way to add filters as a playlist entry - useful only in a multitrack. See FxCut on the wiki. * \properties \em mix_in * \properties \em mix_out + * \properties \em hide Set to 1 to hide the video (make it an audio-only track), + * 2 to hide the audio (make it a video-only track), or 3 to hide audio and video (hidden track). + * This property only applies when using a multitrack or transition. * \event \em playlist-next The playlist fires this when it moves to the next item in the list. * The listener receives one argument that is the index of the entry that just completed. */ @@ -105,6 +108,8 @@ extern int mlt_playlist_split_at( mlt_playlist self, mlt_position position, int left ); extern int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge ); extern int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition transition ); +extern int mlt_playlist_mix_in( mlt_playlist self, int clip, int length ); +extern int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ); extern int mlt_playlist_mix_add( mlt_playlist self, int clip, mlt_transition transition ); extern mlt_producer mlt_playlist_get_clip( mlt_playlist self, int clip ); extern mlt_producer mlt_playlist_get_clip_at( mlt_playlist self, mlt_position position ); @@ -113,7 +118,7 @@ extern void mlt_playlist_consolidate_blanks( mlt_playlist self, int keep_length ); extern int mlt_playlist_is_blank( mlt_playlist self, int clip ); extern int mlt_playlist_is_blank_at( mlt_playlist self, mlt_position position ); -extern void mlt_playlist_insert_blank( mlt_playlist self, int clip, int length ); +extern void mlt_playlist_insert_blank( mlt_playlist self, int clip, int out ); extern void mlt_playlist_pad_blanks( mlt_playlist self, mlt_position position, int length, int find ); extern mlt_producer mlt_playlist_replace_with_blank( mlt_playlist self, int clip ); extern int mlt_playlist_insert_at( mlt_playlist self, mlt_position position, mlt_producer producer, int mode ); diff -Nru mlt-0.9.0/src/framework/mlt_producer.c mlt-0.9.2/src/framework/mlt_producer.c --- mlt-0.9.0/src/framework/mlt_producer.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_producer.c 2014-06-29 20:23:17.000000000 +0000 @@ -613,7 +613,7 @@ clone = clone == NULL ? self : clone; // A properly instatiated producer will have a get_frame method... - if ( self->get_frame == NULL || ( !strcmp( eof, "continue" ) && mlt_producer_position( self ) > mlt_producer_get_out( self ) ) ) + if ( self->get_frame == NULL || ( eof && !strcmp( eof, "continue" ) && mlt_producer_position( self ) > mlt_producer_get_out( self ) ) ) { // Generate a test frame *frame = mlt_frame_init( service ); diff -Nru mlt-0.9.0/src/framework/mlt_profile.c mlt-0.9.2/src/framework/mlt_profile.c --- mlt-0.9.0/src/framework/mlt_profile.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_profile.c 2014-06-29 20:23:17.000000000 +0000 @@ -424,8 +424,15 @@ profile->width = mlt_properties_get_int( p, "meta.media.width" ); profile->height = mlt_properties_get_int( p, "meta.media.height" ); profile->progressive = mlt_properties_get_int( p, "meta.media.progressive" ); - profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" ); - profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" ); + if ( 1000 > mlt_properties_get_double( p, "meta.media.frame_rate_num" ) + / mlt_properties_get_double( p, "meta.media.frame_rate_den" ) ) + { + profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" ); + profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" ); + } else { + profile->frame_rate_num = 60; + profile->frame_rate_den = 1; + } // AVCHD is mis-reported as double frame rate. if ( profile->progressive == 0 && ( profile->frame_rate_num / profile->frame_rate_den == 50 || diff -Nru mlt-0.9.0/src/framework/mlt_properties.c mlt-0.9.2/src/framework/mlt_properties.c --- mlt-0.9.0/src/framework/mlt_properties.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_properties.c 2014-06-29 20:23:17.000000000 +0000 @@ -144,12 +144,15 @@ { property_list *list = self->local; -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__DARWIN__) if ( list->locale ) freelocale( list->locale ); list->locale = newlocale( LC_NUMERIC_MASK, locale, NULL ); +#else + if ( list->locale ) + free( list->locale ); + list->locale = strdup( locale ); #endif - error = list->locale == NULL; } else error = 1; @@ -173,18 +176,23 @@ if ( list->locale ) { #if defined(__DARWIN__) - result = querylocale( LC_NUMERIC, list->locale ); -#elif defined(__linux__) - result = list->locale->__names[ LC_NUMERIC ]; + result = querylocale( LC_NUMERIC, list->locale ); +#elif defined(__GLIBC__) + result = list->locale->__names[ LC_NUMERIC ]; #else - // TODO: not yet sure what to do on other platforms + result = list->locale; #endif - } + } return result; } static int load_properties( mlt_properties self, const char *filename ) { + // Convert filename string encoding. + mlt_properties_set( self, "_mlt_properties_load", filename ); + mlt_properties_from_utf8( self, "_mlt_properties_load", "__mlt_properties_load" ); + filename = mlt_properties_get( self, "__mlt_properties_load" ); + // Open the file FILE *file = fopen( filename, "r" ); @@ -328,11 +336,10 @@ static inline int generate_hash( const char *name ) { - int hash = 0; - int i = 1; + unsigned int hash = 5381; while ( *name ) - hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199; - return hash; + hash = hash * 33 + (unsigned int) ( *name ++ ); + return hash % 199; } /** Copy a serializable property to a properties list that is mirroring this one. @@ -508,13 +515,12 @@ { // Check if we're hashed if ( list->count > 0 && - name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) ) value = list->value[ i ]; // Locate the item for ( i = list->count - 1; value == NULL && i >= 0; i -- ) - if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) ) + if ( !strcmp( list->name[ i ], name ) ) value = list->value[ i ]; } mlt_properties_unlock( self ); @@ -1241,6 +1247,12 @@ { int error = 1; if ( !self || !filename ) return error; + + // Convert filename string encoding. + mlt_properties_set( self, "_mlt_properties_save", filename ); + mlt_properties_from_utf8( self, "_mlt_properties_save", "__mlt_properties_save" ); + filename = mlt_properties_get( self, "__mlt_properties_save" ); + FILE *f = fopen( filename, "w" ); if ( f != NULL ) { @@ -1396,10 +1408,13 @@ free( list->name[ index ] ); } -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__DARWIN__) // Cleanup locale if ( list->locale ) freelocale( list->locale ); +#else + if ( list->locale ) + free( list->locale ); #endif // Clear up the list @@ -1736,6 +1751,11 @@ if ( self ) { + // Convert filename string encoding. + mlt_properties_set( self, "_mlt_properties_parse_yaml", filename ); + mlt_properties_from_utf8( self, "_mlt_properties_parse_yaml", "__mlt_properties_parse_yaml" ); + filename = mlt_properties_get( self, "__mlt_properties_parse_yaml" ); + // Open the file FILE *file = fopen( filename, "r" ); @@ -2092,6 +2112,38 @@ return NULL; } +/** Convert a frame count to a time string. + * + * Do not free the returned string. It's lifetime is controlled by the property. + * \public \memberof mlt_properties_s + * \param self a properties list + * \param frames the frame count to convert + * \param format the time format that you want + * \return the time string or NULL if error, e.g. there is no profile + */ + +char *mlt_properties_frames_to_time( mlt_properties self, mlt_position frames, mlt_time_format format ) +{ + const char *name = "_mlt_properties_time"; + mlt_properties_set_position( self, name, frames ); + return mlt_properties_get_time( self, name, format ); +} + +/** Convert a time string to a frame count. + * + * \public \memberof mlt_properties_s + * \param self a properties list + * \param time the time string to convert + * \return a frame count or a negative value if error, e.g. there is no profile + */ + +mlt_position mlt_properties_time_to_frames( mlt_properties self, const char *time ) +{ + const char *name = "_mlt_properties_time"; + mlt_properties_set( self, name, time ); + return mlt_properties_get_position( self, name ); +} + /** Convert a numeric property to a tuple of color components. * * If the property's string is red, green, blue, white, or black, then it @@ -2477,3 +2529,30 @@ mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN }; return value == NULL ? rect : mlt_property_anim_get_rect( value, fps, list->locale, position, length ); } + +#ifndef WIN32 + +// See win32/win32.c for win32 implementation. + +/** Convert UTF-8 property to the locale-defined encoding. + * + * MLT uses UTF-8 for strings, but Windows cannot accept UTF-8 for a filename. + * Windows uses code pages for the locale encoding. + * \public \memberof mlt_properties_s + * \param self a properties list + * \param name_from the property to read whose value is a UTF-8 string + * \param name_to the name of the new property that will contain converted string + * \return true if error + */ + +int mlt_properties_from_utf8( mlt_properties properties, const char *name_from, const char *name_to ) +{ + // On non-Windows platforms, assume UTF-8 will always work and does not need conversion. + // This function just becomes a pass-through operation. + // This was largely chosen to prevent adding a libiconv dependency to the framework per policy. + // However, for file open operations on Windows, especially when processing XML, a text codec + // dependency is hardly avoidable. + return mlt_properties_set( properties, name_to, mlt_properties_get( properties, name_from ) ); +} + +#endif diff -Nru mlt-0.9.0/src/framework/mlt_properties.h mlt-0.9.2/src/framework/mlt_properties.h --- mlt-0.9.0/src/framework/mlt_properties.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_properties.h 2014-06-29 20:23:17.000000000 +0000 @@ -88,7 +88,11 @@ extern char *mlt_properties_serialise_yaml( mlt_properties self ); extern void mlt_properties_lock( mlt_properties self ); extern void mlt_properties_unlock( mlt_properties self ); + extern char *mlt_properties_get_time( mlt_properties, const char* name, mlt_time_format ); +extern char *mlt_properties_frames_to_time( mlt_properties, mlt_position, mlt_time_format ); +extern mlt_position mlt_properties_time_to_frames( mlt_properties, const char* time ); + extern mlt_color mlt_properties_get_color( mlt_properties, const char* name ); extern int mlt_properties_set_color( mlt_properties, const char* name, mlt_color value ); @@ -105,4 +109,5 @@ extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type ); extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length ); +extern int mlt_properties_from_utf8( mlt_properties properties, const char *name_from, const char *name_to ); #endif diff -Nru mlt-0.9.0/src/framework/mlt_property.c mlt-0.9.2/src/framework/mlt_property.c --- mlt-0.9.0/src/framework/mlt_property.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_property.c 2014-06-29 20:23:17.000000000 +0000 @@ -265,13 +265,14 @@ /** Parse a SMIL clock value. * * \private \memberof mlt_property_s + * \param self a property * \param s the string to parse * \param fps frames per second * \param locale the locale to use for parsing a real number value * \return position in frames */ -static int time_clock_to_frames( const char *s, double fps, locale_t locale ) +static int time_clock_to_frames( mlt_property self, const char *s, double fps, locale_t locale ) { char *pos, *copy = strdup( s ); int hours = 0, minutes = 0; @@ -279,6 +280,22 @@ s = copy; pos = strrchr( s, ':' ); + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + char *orig_localename = NULL; + if ( locale ) + { + // Protect damaging the global locale from a temporary locale on another thread. + pthread_mutex_lock( &self->mutex ); + + // Get the current locale + orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); + + // Set the new locale + setlocale( LC_NUMERIC, locale ); + } +#endif + if ( pos ) { #if defined(__GLIBC__) || defined(__DARWIN__) if ( locale ) @@ -305,6 +322,16 @@ #endif seconds = strtod( s, NULL ); } + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + if ( locale ) { + // Restore the current locale + setlocale( LC_NUMERIC, orig_localename ); + free( orig_localename ); + pthread_mutex_unlock( &self->mutex ); + } +#endif + free( copy ); return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) ); @@ -313,12 +340,13 @@ /** Parse a SMPTE timecode string. * * \private \memberof mlt_property_s + * \param self a property * \param s the string to parse * \param fps frames per second * \return position in frames */ -static int time_code_to_frames( const char *s, double fps ) +static int time_code_to_frames( mlt_property self, const char *s, double fps ) { char *pos, *copy = strdup( s ); int hours = 0, minutes = 0, seconds = 0, frames; @@ -374,13 +402,15 @@ * contains a period or comma character, the string is parsed as a clock value: * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF. * \private \memberof mlt_property_s - * \param value a string to convert + * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant integer */ -static int mlt_property_atoi( const char *value, double fps, locale_t locale ) +static int mlt_property_atoi( mlt_property self, double fps, locale_t locale ) { + const char *value = self->prop_string; + // Parse a hex color value as #RRGGBB or #AARRGGBB. if ( value[0] == '#' ) { @@ -397,9 +427,9 @@ else if ( fps > 0 && strchr( value, ':' ) ) { if ( strchr( value, '.' ) || strchr( value, ',' ) ) - return time_clock_to_frames( value, fps, locale ); + return time_clock_to_frames( self, value, fps, locale ); else - return time_code_to_frames( value, fps ); + return time_code_to_frames( self, value, fps ); } else { @@ -429,7 +459,7 @@ else if ( self->types & mlt_prop_rect && self->data ) return ( int ) ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) - return mlt_property_atoi( self->prop_string, fps, locale ); + return mlt_property_atoi( self, fps, locale ); return 0; } @@ -441,32 +471,58 @@ * If the numeric string ends with '%' then the value is divided by 100 to convert * it into a ratio. * \private \memberof mlt_property_s - * \param value the string to convert + * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant real number */ -static double mlt_property_atof( const char *value, double fps, locale_t locale ) +static double mlt_property_atof( mlt_property self, double fps, locale_t locale ) { - if ( fps > 0 && strchr( value, ':' ) ) + const char *value = self->prop_string; + + if ( fps > 0 && strchr( value, ':' ) ) { if ( strchr( value, '.' ) || strchr( value, ',' ) ) - return time_clock_to_frames( value, fps, locale ); + return time_clock_to_frames( self, value, fps, locale ); else - return time_code_to_frames( value, fps ); + return time_code_to_frames( self, value, fps ); } else { char *end = NULL; double result; + #if defined(__GLIBC__) || defined(__DARWIN__) if ( locale ) result = strtod_l( value, &end, locale ); - else + else +#else + char *orig_localename = NULL; + if ( locale ) { + // Protect damaging the global locale from a temporary locale on another thread. + pthread_mutex_lock( &self->mutex ); + + // Get the current locale + orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); + + // Set the new locale + setlocale( LC_NUMERIC, locale ); + } #endif + result = strtod( value, &end ); if ( end && end[0] == '%' ) result /= 100.0; + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + if ( locale ) { + // Restore the current locale + setlocale( LC_NUMERIC, orig_localename ); + free( orig_localename ); + pthread_mutex_unlock( &self->mutex ); + } +#endif + return result; } } @@ -493,7 +549,7 @@ else if ( self->types & mlt_prop_rect && self->data ) return ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) - return mlt_property_atof( self->prop_string, fps, locale ); + return mlt_property_atof( self, fps, locale ); return 0; } @@ -520,7 +576,7 @@ else if ( self->types & mlt_prop_rect && self->data ) return ( mlt_position ) ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) - return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale ); + return ( mlt_position )mlt_property_atoi( self, fps, locale ); return 0; } @@ -647,8 +703,7 @@ #elif defined(__GLIBC__) const char *localename = locale->__names[ LC_NUMERIC ]; #else - // TODO: not yet sure what to do on other platforms - const char *localename = ""; + const char *localename = locale; #endif // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); @@ -774,10 +829,10 @@ self->destructor = free; self->serialiser = that->serialiser; } - else if ( self->types & mlt_prop_data && self->serialiser != NULL ) + else if ( self->types & mlt_prop_data && that->serialiser != NULL ) { self->types = mlt_prop_string; - self->prop_string = self->serialiser( self->data, self->length ); + self->prop_string = that->serialiser( that->data, that->length ); } pthread_mutex_unlock( &self->mutex ); } @@ -857,7 +912,7 @@ char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale ) { char *orig_localename = NULL; - const char *localename = ""; + const char *localename = "C"; // Optimization for mlt_time_frames if ( format == mlt_time_frames ) @@ -970,12 +1025,36 @@ { double temp; char *p = NULL; + #if defined(__GLIBC__) || defined(__DARWIN__) if ( locale ) temp = strtod_l( self->prop_string, &p, locale ); else +#else + char *orig_localename = NULL; + if ( locale ) { + // Protect damaging the global locale from a temporary locale on another thread. + pthread_mutex_lock( &self->mutex ); + + // Get the current locale + orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); + + // Set the new locale + setlocale( LC_NUMERIC, locale ); + } #endif + temp = strtod( self->prop_string, &p ); + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + if ( locale ) { + // Restore the current locale + setlocale( LC_NUMERIC, orig_localename ); + free( orig_localename ); + pthread_mutex_unlock( &self->mutex ); + } +#endif + result = ( p != self->prop_string ); } return result; @@ -1076,7 +1155,7 @@ } else { - double value; + double value = 0.0; if ( interp == mlt_keyframe_linear ) { double points[2]; @@ -1152,6 +1231,7 @@ double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length ) { + pthread_mutex_lock( &self->mutex ); double result; if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) ) { @@ -1168,6 +1248,7 @@ { result = mlt_property_get_double( self, fps, locale ); } + pthread_mutex_unlock( &self->mutex ); return result; } @@ -1185,6 +1266,7 @@ int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length ) { + pthread_mutex_lock( &self->mutex ); int result; if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) ) { @@ -1201,6 +1283,7 @@ { result = mlt_property_get_int( self, fps, locale ); } + pthread_mutex_unlock( &self->mutex ); return result; } @@ -1218,6 +1301,7 @@ char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length ) { + pthread_mutex_lock( &self->mutex ); char *result; if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) ) { @@ -1228,14 +1312,12 @@ refresh_animation( self, fps, locale, length ); mlt_animation_get_item( self->animation, &item, position ); - pthread_mutex_lock( &self->mutex ); if ( self->prop_string ) free( self->prop_string ); self->prop_string = mlt_property_get_string_l( item.property, locale ); if ( self->prop_string ) self->prop_string = strdup( self->prop_string ); self->types |= mlt_prop_string; - pthread_mutex_unlock( &self->mutex ); result = self->prop_string; mlt_property_close( item.property ); @@ -1244,6 +1326,7 @@ { result = mlt_property_get_string_l( self, locale ); } + pthread_mutex_unlock( &self->mutex ); return result; } @@ -1436,10 +1519,24 @@ rect.x = ( double )self->prop_int64; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) { - //return mlt_property_atof( self->prop_string, fps, locale ); char *value = self->prop_string; char *p = NULL; int count = 0; + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + char *orig_localename = NULL; + if ( locale ) { + // Protect damaging the global locale from a temporary locale on another thread. + pthread_mutex_lock( &self->mutex ); + + // Get the current locale + orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); + + // Set the new locale + setlocale( LC_NUMERIC, locale ); + } +#endif + while ( *value ) { double temp; @@ -1452,8 +1549,15 @@ if ( p != value ) { if ( p[0] == '%' ) + { temp /= 100.0; + p ++; + } + + // Chomp the delimiter. if ( *p ) p ++; + + // Assign the value to appropriate field. switch( count ) { case 0: rect.x = temp; break; @@ -1470,7 +1574,16 @@ value = p; count ++; } - } + +#if !defined(__GLIBC__) && !defined(__DARWIN__) + if ( locale ) { + // Restore the current locale + setlocale( LC_NUMERIC, orig_localename ); + free( orig_localename ); + pthread_mutex_unlock( &self->mutex ); + } +#endif + } return rect; } @@ -1521,6 +1634,7 @@ mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length ) { + pthread_mutex_lock( &self->mutex ); mlt_rect result; if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) ) { @@ -1538,5 +1652,6 @@ { result = mlt_property_get_rect( self, locale ); } + pthread_mutex_unlock( &self->mutex ); return result; } diff -Nru mlt-0.9.0/src/framework/mlt_property.h mlt-0.9.2/src/framework/mlt_property.h --- mlt-0.9.0/src/framework/mlt_property.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_property.h 2014-06-29 20:23:17.000000000 +0000 @@ -35,7 +35,7 @@ #if defined(__GLIBC__) || defined(__DARWIN__) || (__FreeBSD_version >= 900506) #include #else -typedef void* locale_t; +typedef char* locale_t; #endif extern mlt_property mlt_property_init( ); diff -Nru mlt-0.9.0/src/framework/mlt_repository.c mlt-0.9.2/src/framework/mlt_repository.c --- mlt-0.9.0/src/framework/mlt_repository.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_repository.c 2014-06-29 20:23:17.000000000 +0000 @@ -82,6 +82,18 @@ int count = mlt_properties_dir_list( dir, directory, NULL, 0 ); int i; +#ifdef WIN32 + char *syspath = getenv("PATH"); + char *exedir = mlt_environment( "MLT_APPDIR" ); + char *newpath = "PATH="; + newpath = calloc( 1, strlen( newpath )+ strlen( exedir ) + 1 + strlen( syspath ) + 1 ); + strcat( newpath, "PATH=" ); + strcat( newpath, exedir ); + strcat( newpath, ";" ); + strcat( newpath, syspath ); + putenv(newpath); +#endif + // Iterate over files for ( i = 0; i < count; i++ ) { @@ -92,7 +104,7 @@ // TODO: extend repository to allow this to be used on a case by case basis if ( strstr( object_name, "libmltkino" ) ) flags |= RTLD_GLOBAL; - + // Open the shared object void *object = dlopen( object_name, flags ); if ( object != NULL ) diff -Nru mlt-0.9.0/src/framework/mlt_service.c mlt-0.9.2/src/framework/mlt_service.c --- mlt-0.9.0/src/framework/mlt_service.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_service.c 2014-06-29 20:23:17.000000000 +0000 @@ -571,6 +571,9 @@ mlt_properties_set_data( props, "service", self, 0, NULL, NULL ); mlt_events_fire( properties, "service-changed", NULL ); mlt_events_fire( props, "service-changed", NULL ); + mlt_service cp = mlt_properties_get_data( properties, "_cut_parent", NULL ); + if ( cp ) + mlt_events_fire( MLT_SERVICE_PROPERTIES(cp), "service-changed", NULL ); mlt_events_listen( props, self, "service-changed", ( mlt_listener )mlt_service_filter_changed ); mlt_events_listen( props, self, "property-changed", ( mlt_listener )mlt_service_filter_property_changed ); } diff -Nru mlt-0.9.0/src/framework/mlt_service.h mlt-0.9.2/src/framework/mlt_service.h --- mlt-0.9.0/src/framework/mlt_service.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_service.h 2014-06-29 20:23:17.000000000 +0000 @@ -49,7 +49,6 @@ * \properties \em out when to stop * \properties \em _filter_private Set this on a service to ensure that attached filters are handled privately. * See modules/core/filter_region.c and modules/core/filter_watermark.c for examples. - * \properties \em disable Set this on a filter to disable it while keeping it in the object model. * \properties \em _profile stores the mlt_profile for a service * \properties \em _unique_id is a unique identifier * \properties \em _need_previous_next boolean that instructs producers to get diff -Nru mlt-0.9.0/src/framework/mlt_tractor.c mlt-0.9.2/src/framework/mlt_tractor.c --- mlt-0.9.0/src/framework/mlt_tractor.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_tractor.c 2014-06-29 20:23:17.000000000 +0000 @@ -281,6 +281,9 @@ mlt_properties_set_int( properties, "colorspace", mlt_properties_get_int( frame_properties, "colorspace" ) ); mlt_properties_set_int( properties, "force_full_luma", mlt_properties_get_int( frame_properties, "force_full_luma" ) ); mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( frame_properties, "top_field_first" ) ); + mlt_properties_set_data( properties, "movit.convert.fence", + mlt_properties_get_data( frame_properties, "movit.convert.fence", NULL ), + 0, NULL, NULL ); data = mlt_frame_get_alpha_mask( frame ); mlt_properties_get_data( frame_properties, "alpha", &size ); mlt_frame_set_alpha( self, data, size, NULL ); @@ -504,6 +507,7 @@ mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) ); mlt_properties_set_int( frame_properties, "image_count", image_count ); + mlt_properties_set_data( frame_properties, "_producer", mlt_frame_get_original_producer( first_video ), 0, NULL, NULL ); } else { diff -Nru mlt-0.9.0/src/framework/mlt_transition.c mlt-0.9.2/src/framework/mlt_transition.c --- mlt-0.9.0/src/framework/mlt_transition.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_transition.c 2014-06-29 20:23:17.000000000 +0000 @@ -426,7 +426,7 @@ // Initialise temporary store if ( self->frames == NULL ) - self->frames = calloc( sizeof( mlt_frame ), b_track + 1 ); + self->frames = calloc( b_track + 1, sizeof( mlt_frame ) ); // Get all frames between a and b for( i = a_track; i <= b_track; i ++ ) @@ -461,7 +461,7 @@ } // Now handle the non-always active case - if ( active && !always_active ) + if ( active && !always_active && a_frame <= b_track ) { // For non-always-active transitions, we need the current position of the a frame position = mlt_frame_get_position( self->frames[ a_frame ] ); @@ -471,10 +471,12 @@ } // Finally, process the a and b frames - if ( active ) + if ( active && !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "disable" ) ) { - mlt_frame a_frame_ptr = self->frames[ !reverse_order ? a_frame : b_frame ]; - mlt_frame b_frame_ptr = self->frames[ !reverse_order ? b_frame : a_frame ]; + int frame_nb = ( !reverse_order && a_frame <= b_track )? a_frame : b_frame; + mlt_frame a_frame_ptr = self->frames[ frame_nb ]; + frame_nb = ( !reverse_order || a_frame > b_track )? b_frame : a_frame; + mlt_frame b_frame_ptr = self->frames[ frame_nb ]; int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" ); int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" ); if ( !( a_hide & type ) && !( b_hide & type ) ) diff -Nru mlt-0.9.0/src/framework/mlt_transition.h mlt-0.9.2/src/framework/mlt_transition.h --- mlt-0.9.0/src/framework/mlt_transition.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_transition.h 2014-06-29 20:23:17.000000000 +0000 @@ -35,7 +35,8 @@ * \properties \em b_track the track index (0-based) of a multitrack of the second producer * \properties \em accepts_blanks a flag to indicate if the transition should accept blank frames * \properties \em always_active a flag to indicate that the in and out points do not apply - * \properties \em _transition_type 1 for video, 2 for audio + * \properties \em _transition_type 1 for video, 2 for audio, 3 for both audio and video + * \properties \em disable Set this to disable the transition while keeping it in the object model. */ struct mlt_transition_s diff -Nru mlt-0.9.0/src/framework/mlt_types.h mlt-0.9.2/src/framework/mlt_types.h --- mlt-0.9.0/src/framework/mlt_types.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_types.h 2014-06-29 20:23:17.000000000 +0000 @@ -112,7 +112,6 @@ mlt_service_type; /* I don't want to break anyone's applications without warning. -Zach */ -#undef DOUBLE_MLT_POSITION #ifdef DOUBLE_MLT_POSITION #define MLT_POSITION_FMT "%f" #define MLT_POSITION_MOD(A, B) (A - B * ((int)(A / B))) diff -Nru mlt-0.9.0/src/framework/mlt.vers mlt-0.9.2/src/framework/mlt.vers --- mlt-0.9.0/src/framework/mlt.vers 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt.vers 2014-06-29 20:23:17.000000000 +0000 @@ -447,3 +447,11 @@ mlt_service_filter_count; mlt_service_move_filter; } MLT_0.8.8; + +MLT_0.9.2 { + mlt_playlist_mix_in; + mlt_playlist_mix_out; + mlt_properties_frames_to_time; + mlt_properties_time_to_frames; + mlt_properties_from_utf8; +} MLT_0.9.0; diff -Nru mlt-0.9.0/src/framework/mlt_version.h mlt-0.9.2/src/framework/mlt_version.h --- mlt-0.9.0/src/framework/mlt_version.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/framework/mlt_version.h 2014-06-29 20:23:17.000000000 +0000 @@ -29,7 +29,7 @@ #define LIBMLT_VERSION_MAJOR 0 #define LIBMLT_VERSION_MINOR 9 -#define LIBMLT_VERSION_REVISION 0 +#define LIBMLT_VERSION_REVISION 2 #define LIBMLT_VERSION_INT ((LIBMLT_VERSION_MAJOR<<16)+(LIBMLT_VERSION_MINOR<<8)+LIBMLT_VERSION_REVISION) #define LIBMLT_VERSION STRINGIZE(LIBMLT_VERSION_MAJOR.LIBMLT_VERSION_MINOR.LIBMLT_VERSION_REVISION) diff -Nru mlt-0.9.0/src/melt/io.c mlt-0.9.2/src/melt/io.c --- mlt-0.9.0/src/melt/io.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/melt/io.c 2014-06-29 20:23:17.000000000 +0000 @@ -32,6 +32,8 @@ #else // MinGW defines struct timespec in pthread.h #include +// for nanosleep() +#include #endif #include #include @@ -98,8 +100,10 @@ /** This stores the previous settings */ +#ifndef WIN32 static struct termios oldtty; static int mode = 0; +#endif /** This is called automatically on application exit to restore the previous tty settings. diff -Nru mlt-0.9.0/src/melt/Makefile mlt-0.9.2/src/melt/Makefile --- mlt-0.9.0/src/melt/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/melt/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -11,8 +11,8 @@ ifeq ($(targetos), MinGW) ifeq (, $(findstring MELT_NOSDL, $(CFLAGS))) -CFLAGS += `sdl-config --cflags` -LDFLAGS += `sdl-config --libs` +CFLAGS += $(shell sdl-config --cflags) +LDFLAGS += $(shell sdl-config --libs) endif bindir = $(prefix) endif diff -Nru mlt-0.9.0/src/melt/melt.c mlt-0.9.2/src/melt/melt.c --- mlt-0.9.0/src/melt/melt.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/melt/melt.c 2014-06-29 20:23:17.000000000 +0000 @@ -686,8 +686,7 @@ static void on_fatal_error( mlt_properties owner, mlt_consumer consumer ) { - mlt_consumer_stop( consumer ); - exit( EXIT_FAILURE ); + mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "done", 1 ); } int main( int argc, char **argv ) @@ -897,7 +896,7 @@ // Parse the arguments for ( i = 1; i < argc; i ++ ) { - if ( !strcmp( argv[ i ], "-jack" ) ) + if ( !strcmp( argv[ i ], "-jack" ) && consumer ) { setup_jack_transport( consumer, profile ); } @@ -988,8 +987,11 @@ mlt_profile_close( profile ); exit_factory: - + +// Workaround qmelt on OS X from crashing at exit. +#if !defined(__MACH__) || !defined(QT_GUI_LIB) mlt_factory_close( ); +#endif return 0; } diff -Nru mlt-0.9.0/src/mlt++/MltPlaylist.cpp mlt-0.9.2/src/mlt++/MltPlaylist.cpp --- mlt-0.9.0/src/mlt++/MltPlaylist.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltPlaylist.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -226,6 +226,16 @@ return mlt_playlist_mix( get_playlist( ), clip, length, transition == NULL ? NULL : transition->get_transition( ) ); } +int Playlist::mix_in(int clip, int length) +{ + return mlt_playlist_mix_in( get_playlist( ), clip, length ); +} + +int Playlist::mix_out(int clip, int length) +{ + return mlt_playlist_mix_out( get_playlist( ), clip, length ); +} + int Playlist::mix_add( int clip, Transition *transition ) { return mlt_playlist_mix_add( get_playlist( ), clip, transition == NULL ? NULL : transition->get_transition( ) ); @@ -281,9 +291,9 @@ return mlt_playlist_consolidate_blanks( get_playlist( ), keep_length ); } -void Playlist::insert_blank( int clip, int length ) +void Playlist::insert_blank(int clip, int out ) { - mlt_playlist_insert_blank( get_playlist( ), clip, length ); + mlt_playlist_insert_blank( get_playlist( ), clip, out ); } void Playlist::pad_blanks( int position, int length, int find ) diff -Nru mlt-0.9.0/src/mlt++/MltPlaylist.h mlt-0.9.2/src/mlt++/MltPlaylist.h --- mlt-0.9.0/src/mlt++/MltPlaylist.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltPlaylist.h 2014-06-29 20:23:17.000000000 +0000 @@ -86,6 +86,8 @@ int split_at( int position, bool left = true ); int join( int clip, int count = 1, int merge = 1 ); int mix( int clip, int length, Transition *transition = NULL ); + int mix_in( int clip, int length ); + int mix_out( int clip, int length ); int mix_add( int clip, Transition *transition ); int repeat( int clip, int count ); Producer *get_clip( int clip ); @@ -96,7 +98,7 @@ bool is_blank_at( int position ); void consolidate_blanks( int keep_length = 0 ); Producer *replace_with_blank( int clip ); - void insert_blank( int clip, int length ); + void insert_blank( int clip, int out ); void pad_blanks( int position, int length, int find = 0 ); int insert_at( int position, Producer *producer, int mode = 0 ); int insert_at( int position, Producer &producer, int mode = 0 ); diff -Nru mlt-0.9.0/src/mlt++/MltProperties.cpp mlt-0.9.2/src/mlt++/MltProperties.cpp --- mlt-0.9.0/src/mlt++/MltProperties.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltProperties.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -102,9 +102,9 @@ mlt_events_unblock( get_properties( ), object != NULL ? object : get_properties( ) ); } -void Properties::fire_event( const char *event ) +int Properties::fire_event( const char *event ) { - mlt_events_fire( get_properties( ), event, NULL ); + return mlt_events_fire( get_properties( ), event, NULL ); } bool Properties::is_valid( ) @@ -242,22 +242,7 @@ int Properties::save( const char *file ) { -#ifdef WIN32 return mlt_properties_save( get_properties( ), file ); -#else - int error = 0; - FILE *f = fopen( file, "w" ); - if ( f != NULL ) - { - dump( f ); - fclose( f ); - } - else - { - error = 1; - } - return error; -#endif } #if defined( __DARWIN__ ) && GCC_VERSION < 40000 @@ -337,6 +322,16 @@ return mlt_properties_get_time( get_properties(), name, format ); } +char *Properties::frames_to_time( int frames, mlt_time_format format ) +{ + return mlt_properties_frames_to_time( get_properties(), frames, format ); +} + +int Properties::time_to_frames( const char *time ) +{ + return mlt_properties_time_to_frames( get_properties(), time ); +} + mlt_color Properties::get_color( const char *name ) { return mlt_properties_get_color( get_properties(), name ); diff -Nru mlt-0.9.0/src/mlt++/MltProperties.h mlt-0.9.2/src/mlt++/MltProperties.h --- mlt-0.9.0/src/mlt++/MltProperties.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltProperties.h 2014-06-29 20:23:17.000000000 +0000 @@ -53,7 +53,7 @@ void unlock( ); void block( void *object = NULL ); void unblock( void *object = NULL ); - void fire_event( const char *event ); + int fire_event( const char *event ); bool is_valid( ); int count( ); char *get( const char *name ); @@ -97,6 +97,9 @@ int set_lcnumeric( const char *locale ); const char *get_lcnumeric( ); char *get_time( const char *name, mlt_time_format = mlt_time_smpte ); + char *frames_to_time( int, mlt_time_format = mlt_time_smpte ); + int time_to_frames( const char* time ); + mlt_color get_color( const char *name ); int set( const char *name , mlt_color value ); diff -Nru mlt-0.9.0/src/mlt++/MltTokeniser.cpp mlt-0.9.2/src/mlt++/MltTokeniser.cpp --- mlt-0.9.0/src/mlt++/MltTokeniser.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltTokeniser.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -26,7 +26,7 @@ { tokens = mlt_tokeniser_init( ); if ( text != NULL ) - mlt_tokeniser_parse_new( tokens, text, delimiter ); + mlt_tokeniser_parse_new( tokens, text, delimiter? delimiter : " " ); } Tokeniser::~Tokeniser( ) @@ -36,7 +36,7 @@ int Tokeniser::parse( char *text, char *delimiter ) { - return mlt_tokeniser_parse_new( tokens, text, delimiter ); + return mlt_tokeniser_parse_new( tokens, text, delimiter? delimiter : " " ); } int Tokeniser::count( ) diff -Nru mlt-0.9.0/src/mlt++/MltTokeniser.h mlt-0.9.2/src/mlt++/MltTokeniser.h --- mlt-0.9.0/src/mlt++/MltTokeniser.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/MltTokeniser.h 2014-06-29 20:23:17.000000000 +0000 @@ -32,9 +32,9 @@ private: mlt_tokeniser tokens; public: - Tokeniser( char *text = NULL, char *delimiter = " " ); + Tokeniser( char *text = NULL, char *delimiter = NULL ); ~Tokeniser( ); - int parse( char *text, char *delimiter = " " ); + int parse( char *text, char *delimiter = NULL ); int count( ); char *get( int index ); char *input( ); diff -Nru mlt-0.9.0/src/mlt++/mlt++.vers mlt-0.9.2/src/mlt++/mlt++.vers --- mlt-0.9.0/src/mlt++/mlt++.vers 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/mlt++/mlt++.vers 2014-06-29 20:23:17.000000000 +0000 @@ -457,3 +457,13 @@ "Mlt::Service::move_filter(int, int)"; }; } MLTPP_0.8.8; + +MLTPP_0.9.2 { + global: + extern "C++" { + "Mlt::Playlist::mix_in(int, int)"; + "Mlt::Playlist::mix_out(int, int)"; + "Mlt::Properties::frames_to_time(int, mlt_time_format)"; + "Mlt::Properties::time_to_frames(char const*)"; + }; +} MLTPP_0.9.0; diff -Nru mlt-0.9.0/src/modules/avformat/configure mlt-0.9.2/src/modules/avformat/configure --- mlt-0.9.0/src/modules/avformat/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avformat/configure 2014-06-29 20:23:17.000000000 +0000 @@ -109,6 +109,11 @@ fi elif [ "$shared_ffmpeg" != "" ] then + if ! $(pkg-config libswscale${avformat_suffix}); then + echo "- libswscale not found: disabling" + touch ../disable-avformat + exit 0 + fi echo "PREFIX=$shared_ffmpeg" >> config.mak case $targetos in MINGW32_NT-*) @@ -124,6 +129,11 @@ echo "LDFLAGS+=$(pkg-config --libs-only-L libswscale${avformat_suffix})" >> config.mak if [ "$devices" = "true" ] then + if ! $(pkg-config libavdevice${avformat_suffix}); then + echo "- libavdevice not found: disabling" + touch ../disable-avformat + exit 0 + fi echo "CFLAGS+=$(pkg-config --cflags libavdevice${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs-only-L libavdevice${avformat_suffix})" >> config.mak fi diff -Nru mlt-0.9.0/src/modules/avformat/consumer_avformat.c mlt-0.9.2/src/modules/avformat/consumer_avformat.c --- mlt-0.9.0/src/modules/avformat/consumer_avformat.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avformat/consumer_avformat.c 2014-06-29 20:23:17.000000000 +0000 @@ -62,6 +62,8 @@ #define AV_CODEC_ID_NONE CODEC_ID_NONE #define AV_CODEC_ID_AC3 CODEC_ID_AC3 #define AV_CODEC_ID_VORBIS CODEC_ID_VORBIS +#define AV_CODEC_ID_RAWVIDEO CODEC_ID_RAWVIDEO +#define AV_CODEC_ID_MJPEG CODEC_ID_MJPEG #endif #define MAX_AUDIO_STREAMS (8) @@ -194,6 +196,27 @@ return consumer; } +static void recompute_aspect_ratio( mlt_properties properties ) +{ + double ar = mlt_properties_get_double( properties, "aspect" ); + AVRational rational = av_d2q( ar, 255 ); + int width = mlt_properties_get_int( properties, "width" ); + int height = mlt_properties_get_int( properties, "height" ); + + // Update the profile and properties as well since this is an alias + // for mlt properties that correspond to profile settings + mlt_properties_set_int( properties, "display_aspect_num", rational.num ); + mlt_properties_set_int( properties, "display_aspect_den", rational.den ); + + // Now compute the sample aspect ratio + rational = av_d2q( ar * height / FFMAX(width, 1), 255 ); + + // Update the profile and properties as well since this is an alias + // for mlt properties that correspond to profile settings + mlt_properties_set_int( properties, "sample_aspect_num", rational.num ); + mlt_properties_set_int( properties, "sample_aspect_den", rational.den ); +} + static void property_changed( mlt_properties owner, mlt_consumer self, char *name ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); @@ -221,27 +244,12 @@ height = ( height / 2 ) * 2; mlt_properties_set_int( properties, "width", width ); mlt_properties_set_int( properties, "height", height ); + recompute_aspect_ratio( properties ); } // "-aspect" on ffmpeg command line is display aspect ratio - else if ( !strcmp( name, "aspect" ) ) + else if ( !strcmp( name, "aspect" ) || !strcmp( name, "width" ) || !strcmp( name, "height" ) ) { - double ar = mlt_properties_get_double( properties, "aspect" ); - AVRational rational = av_d2q( ar, 255 ); - int width = mlt_properties_get_int( properties, "width" ); - int height = mlt_properties_get_int( properties, "height" ); - - // Update the profile and properties as well since this is an alias - // for mlt properties that correspond to profile settings - mlt_properties_set_int( properties, "display_aspect_num", rational.num ); - mlt_properties_set_int( properties, "display_aspect_den", rational.den ); - - // Now compute the sample aspect ratio - rational = av_d2q( ar * height / FFMAX(width, 1), 255 ); - - // Update the profile and properties as well since this is an alias - // for mlt properties that correspond to profile settings - mlt_properties_set_int( properties, "sample_aspect_num", rational.num ); - mlt_properties_set_int( properties, "sample_aspect_den", rational.den ); + recompute_aspect_ratio( properties ); } // Handle the ffmpeg command line "-r" property for frame rate else if ( !strcmp( name, "r" ) ) @@ -701,6 +709,20 @@ case AV_CODEC_ID_PCM_U16BE: audio_input_frame_size >>= 1; break; +#if LIBAVCODEC_VERSION_INT >= ((54<<16)+(59<<8)+0) + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_U24LE: + case AV_CODEC_ID_PCM_U24BE: + audio_input_frame_size /= 3; + break; + case AV_CODEC_ID_PCM_S32LE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_U32LE: + case AV_CODEC_ID_PCM_U32BE: + audio_input_frame_size >>= 2; + break; +#endif default: break; } @@ -931,25 +953,27 @@ snprintf( logfilename, sizeof(logfilename), "%s_2pass.log", mlt_properties_get( properties, "passlogfile" ) ? mlt_properties_get( properties, "passlogfile" ) : mlt_properties_get( properties, "target" ) ); + mlt_properties_set( properties, "_passlogfile", logfilename ); + mlt_properties_from_utf8( properties, "_passlogfile", "_logfilename" ); + const char *filename = mlt_properties_get( properties, "_logfilename" ); if ( c->flags & CODEC_FLAG_PASS1 ) { - f = fopen( logfilename, "w" ); + f = fopen( filename, "w" ); if ( !f ) - perror( logfilename ); + perror( filename ); else mlt_properties_set_data( properties, "_logfile", f, 0, ( mlt_destructor )fclose, NULL ); } else { /* read the log file */ - f = fopen( logfilename, "r" ); + f = fopen( filename, "r" ); if ( !f ) { - perror(logfilename); + perror( filename ); } else { - mlt_properties_set( properties, "_logfilename", logfilename ); fseek( f, 0, SEEK_END ); size = ftell( f ); fseek( f, 0, SEEK_SET ); @@ -1176,7 +1200,7 @@ int count = 0; // Allocate the context - AVFormatContext *oc = avformat_alloc_context( ); + AVFormatContext *oc = NULL; // Streams AVStream *video_st = NULL; @@ -1192,7 +1216,8 @@ // Determine the format AVOutputFormat *fmt = NULL; - const char *filename = mlt_properties_get( properties, "target" ); + mlt_properties_from_utf8( properties, "target", "_target" ); + const char *filename = mlt_properties_get( properties, "_target" ); char *format = mlt_properties_get( properties, "f" ); char *vcodec = mlt_properties_get( properties, "vcodec" ); char *acodec = mlt_properties_get( properties, "acodec" ); @@ -1230,6 +1255,22 @@ if ( filename == NULL || !strcmp( filename, "" ) ) filename = "pipe:"; +#if defined(FFUDIV) && LIBAVUTIL_VERSION_INT >= ((53<<16)+(2<<8)+0) + avformat_alloc_output_context2( &oc, fmt, format, filename ); +#else + oc = avformat_alloc_context( ); + oc->oformat = fmt; + snprintf( oc->filename, sizeof(oc->filename), "%s", filename ); + + if ( oc->oformat && oc->oformat->priv_class && !oc->priv_data && oc->oformat->priv_data_size ) { + oc->priv_data = av_mallocz( oc->oformat->priv_data_size ); + if ( oc->priv_data ) { + *(const AVClass**)oc->priv_data = oc->oformat->priv_class; + av_opt_set_defaults( oc->priv_data ); + } + } +#endif + // Get the codec ids selected audio_codec_id = fmt->audio_codec; video_codec_id = fmt->video_codec; @@ -1308,9 +1349,6 @@ } } - oc->oformat = fmt; - snprintf( oc->filename, sizeof(oc->filename), "%s", filename ); - // Get a frame now, so we can set some AVOptions from properties. frame = mlt_consumer_rt_frame( consumer ); @@ -1453,10 +1491,15 @@ // Write the stream header. if ( mlt_properties_get_int( properties, "running" ) ) #if LIBAVFORMAT_VERSION_INT >= ((53<<16)+(2<<8)+0) - avformat_write_header( oc, NULL ); + if ( avformat_write_header( oc, NULL ) < 0 ) #else - av_write_header( oc ); + if ( av_write_header( oc ) < 0 ) #endif + { + mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "Could not write header '%s'\n", filename ); + mlt_events_fire( properties, "consumer-fatal-error", NULL ); + goto on_fatal_error; + } } #if LIBAVFORMAT_VERSION_INT < ((53<<16)+(2<<8)+0) else @@ -1478,7 +1521,7 @@ if ( video_st ) converted_avframe = alloc_picture( video_st->codec->pix_fmt, width, height ); -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 54 // Allocate audio AVFrame if ( audio_st[0] ) { @@ -1611,11 +1654,13 @@ else if ( codec->sample_fmt == AV_SAMPLE_FMT_U8P ) p = interleaved_to_planar( samples, channels, p, sizeof( uint8_t ) ); #endif -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 54 audio_avframe->nb_samples = FFMAX( samples, audio_input_nb_samples ); +#if LIBAVCODEC_VERSION_MAJOR >= 55 if ( audio_codec_id == AV_CODEC_ID_VORBIS ) audio_avframe->pts = synth_audio_pts; synth_audio_pts += audio_avframe->nb_samples; +#endif avcodec_fill_audio_frame( audio_avframe, codec->channels, codec->sample_fmt, (const uint8_t*) p, AUDIO_ENCODE_BUFFER_SIZE, 0 ); int got_packet = 0; @@ -1701,11 +1746,13 @@ dest_offset += current_channels; } } -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 54 audio_avframe->nb_samples = FFMAX( samples, audio_input_nb_samples ); +#if LIBAVCODEC_VERSION_MAJOR >= 55 if ( audio_codec_id == AV_CODEC_ID_VORBIS ) audio_avframe->pts = synth_audio_pts; synth_audio_pts += audio_avframe->nb_samples; +#endif avcodec_fill_audio_frame( audio_avframe, codec->channels, codec->sample_fmt, (const uint8_t*) audio_buf_2, AUDIO_ENCODE_BUFFER_SIZE, 0 ); int got_packet = 0; @@ -1874,16 +1921,29 @@ { AVPacket pkt; av_init_packet( &pkt ); - pkt.data = video_outbuf; - pkt.size = video_outbuf_size; + if ( c->codec->id == AV_CODEC_ID_RAWVIDEO ) { + pkt.data = NULL; + pkt.size = 0; + } else { + pkt.data = video_outbuf; + pkt.size = video_outbuf_size; + } // Set the quality converted_avframe->quality = c->global_quality; + converted_avframe->pts = frame_count; // Set frame interlace hints converted_avframe->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" ); converted_avframe->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); - converted_avframe->pts = frame_count; +#if LIBAVCODEC_VERSION_INT >= ((53<<16)+(61<<8)+100) + if ( mlt_properties_get_int( frame_properties, "progressive" ) ) + c->field_order = AV_FIELD_PROGRESSIVE; + else if ( c->codec_id == AV_CODEC_ID_MJPEG ) + c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TT : AV_FIELD_BB; + else + c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TB : AV_FIELD_BT; +#endif // Encode the image #if LIBAVCODEC_VERSION_MAJOR >= 55 @@ -1998,12 +2058,14 @@ else if ( c->sample_fmt == AV_SAMPLE_FMT_U8P ) p = interleaved_to_planar( audio_input_nb_samples, channels, p, sizeof( uint8_t ) ); #endif -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 54 pkt.size = audio_outbuf_size; audio_avframe->nb_samples = FFMAX( samples / channels, audio_input_nb_samples ); +#if LIBAVCODEC_VERSION_MAJOR >= 55 if ( audio_codec_id == AV_CODEC_ID_VORBIS ) audio_avframe->pts = synth_audio_pts; synth_audio_pts += audio_avframe->nb_samples; +#endif avcodec_fill_audio_frame( audio_avframe, c->channels, c->sample_fmt, (const uint8_t*) p, AUDIO_ENCODE_BUFFER_SIZE, 0 ); int got_packet = 0; @@ -2026,7 +2088,7 @@ { // Drain the codec if ( pkt.size <= 0 ) { -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 54 pkt.size = audio_outbuf_size; int got_packet = 0; int ret = avcodec_encode_audio2( c, &pkt, NULL, &got_packet ); @@ -2068,8 +2130,13 @@ AVCodecContext *c = video_st->codec; AVPacket pkt; av_init_packet( &pkt ); - pkt.data = video_outbuf; - pkt.size = video_outbuf_size; + if ( c->codec->id == AV_CODEC_ID_RAWVIDEO ) { + pkt.data = NULL; + pkt.size = 0; + } else { + pkt.data = video_outbuf; + pkt.size = video_outbuf_size; + } // Encode the image #if LIBAVCODEC_VERSION_MAJOR >= 55 @@ -2111,7 +2178,10 @@ } on_fatal_error: - + + if ( frame ) + mlt_frame_close( frame ); + // Write the trailer, if any if ( frames ) av_write_trailer( oc ); @@ -2180,6 +2250,20 @@ free( full ); free( cwd ); remove( "x264_2pass.log.temp" ); + + // Recent versions of libavcodec/x264 support passlogfile and need cleanup if specified. + if ( !mlt_properties_get( properties, "_logfilename" ) && + mlt_properties_get( properties, "passlogfile" ) ) + { + mlt_properties_get( properties, "passlogfile" ); + mlt_properties_from_utf8( properties, "passlogfile", "_passlogfile" ); + file = mlt_properties_get( properties, "_passlogfile" ); + remove( file ); + full = malloc( strlen( file ) + strlen( ".mbtree" ) + 1 ); + sprintf( full, "%s.mbtree", file ); + remove( full ); + free( full ); + } } while ( ( frame = mlt_deque_pop_back( queue ) ) ) diff -Nru mlt-0.9.0/src/modules/avformat/filter_avcolour_space.c mlt-0.9.2/src/modules/avformat/filter_avcolour_space.c --- mlt-0.9.0/src/modules/avformat/filter_avcolour_space.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avformat/filter_avcolour_space.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * filter_avcolour_space.c -- Colour space filter - * Copyright (C) 2004-2005 Ushodaya Enterprises Limited + * Copyright (C) 2004-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include #include #include +#include // ffmpeg Header files #include @@ -68,48 +69,60 @@ return value; } -static void set_luma_transfer( struct SwsContext *context, int colorspace, int use_full_range ) +static int set_luma_transfer( struct SwsContext *context, int src_colorspace, int dst_colorspace, int full_range ) { - int *coefficients; - const int *new_coefficients; - int full_range; - int brightness, contrast, saturation; + const int *src_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + const int *dst_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + int brightness = 0; + int contrast = 1 << 16; + int saturation = 1 << 16; - if ( sws_getColorspaceDetails( context, &coefficients, &full_range, &coefficients, &full_range, - &brightness, &contrast, &saturation ) != -1 ) + switch ( src_colorspace ) { - // Don't change these from defaults unless explicitly told to. - if ( use_full_range >= 0 ) - full_range = use_full_range; - switch ( colorspace ) - { - case 170: - case 470: - case 601: - case 624: - new_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); - break; - case 240: - new_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); - break; - case 709: - new_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); - break; - default: - new_coefficients = coefficients; - break; - } - sws_setColorspaceDetails( context, new_coefficients, full_range, new_coefficients, full_range, - brightness, contrast, saturation ); + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; + } + switch ( dst_colorspace ) + { + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; } + return sws_setColorspaceDetails( context, src_coefficients, full_range, dst_coefficients, full_range, + brightness, contrast, saturation ); } -static void av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, - int width, int height, int colorspace, int use_full_range ) +// returns set_lumage_transfer result +static int av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, + int width, int height, int src_colorspace, int dst_colorspace, int use_full_range ) { AVPicture input; AVPicture output; int flags = SWS_BICUBIC | SWS_ACCURATE_RND; + int error = -1; if ( out_fmt == PIX_FMT_YUYV422 ) flags |= SWS_FULL_CHR_H_INP; @@ -121,6 +134,8 @@ #ifdef USE_SSE flags |= SWS_CPU_CAPS_MMX2; #endif + if ( out_fmt == PIX_FMT_YUV420P && use_full_range ) + out_fmt = PIX_FMT_YUVJ420P; avpicture_fill( &input, in, in_fmt, width, height ); avpicture_fill( &output, out, out_fmt, width, height ); @@ -128,11 +143,15 @@ width, height, out_fmt, flags, NULL, NULL, NULL); if ( context ) { - set_luma_transfer( context, colorspace, use_full_range ); + // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. + if ( out_fmt == PIX_FMT_RGB24 || out_fmt == PIX_FMT_RGBA ) + dst_colorspace = 601; + error = set_luma_transfer( context, src_colorspace, dst_colorspace, use_full_range ); sws_scale( context, (const uint8_t* const*) input.data, input.linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } + return error; } /** Do it :-). @@ -147,12 +166,15 @@ if ( *format != output_format ) { + mlt_profile profile = mlt_service_profile( + MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) ); + int profile_colorspace = profile ? profile->colorspace : 601; int colorspace = mlt_properties_get_int( properties, "colorspace" ); - int force_full_luma = -1; + int force_full_luma = 0; - mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d\n", + mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d->%d\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ), - width, height, colorspace ); + width, height, colorspace, profile_colorspace ); int in_fmt = convert_mlt_to_av_cs( *format ); int out_fmt = convert_mlt_to_av_cs( output_format ); @@ -196,10 +218,16 @@ // By removing the frame property we only permit the luma to skip scaling once. // Thereafter, we let swscale scale the luma range as it pleases since it seems // we do not have control over the RGB to YUV conversion. - force_full_luma = mlt_properties_get_int( properties, "force_full_luma" ); + force_full_luma = mlt_properties_get_int( properties, "force_full_luma" ); mlt_properties_set( properties, "force_full_luma", NULL ); } - av_convert_image( output, *image, out_fmt, in_fmt, width, height, colorspace, force_full_luma ); + if ( !av_convert_image( output, *image, out_fmt, in_fmt, width, height, + colorspace, profile_colorspace, force_full_luma ) ) + { + // The new colorspace is only valid if destination is YUV. + if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) + mlt_properties_set_int( properties, "colorspace", profile_colorspace ); + } *image = output; *format = output_format; mlt_frame_set_image( frame, output, size, mlt_pool_release ); @@ -238,8 +266,7 @@ return error; } -/* TODO: The below is not working because swscale does not have - * adjustable coefficients yet for RGB->YUV */ +/* TODO: Enable this to force colorspace conversion. Cost is heavy due to RGB conversions. */ #if 0 static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { diff -Nru mlt-0.9.0/src/modules/avformat/Makefile mlt-0.9.2/src/modules/avformat/Makefile --- mlt-0.9.0/src/modules/avformat/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avformat/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -27,8 +27,8 @@ endif ifdef VDPAU -CFLAGS += -DVDPAU `pkg-config --cflags x11` -LDFLAGS += $(LIBDL) `pkg-config --libs x11` +CFLAGS += -DVDPAU $(shell pkg-config pkg-config --cflags x11) +LDFLAGS += $(LIBDL) $(shell pkg-config pkg-config --libs x11) endif ifdef CODECS diff -Nru mlt-0.9.0/src/modules/avformat/producer_avformat.c mlt-0.9.2/src/modules/avformat/producer_avformat.c --- mlt-0.9.0/src/modules/avformat/producer_avformat.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avformat/producer_avformat.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_avformat.c -- avformat producer - * Copyright (C) 2003-2012 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Charles Yates * Author: Dan Dennedy * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard @@ -102,7 +102,7 @@ unsigned int invalid_pts_counter; unsigned int invalid_dts_counter; mlt_cache image_cache; - int colorspace; + int yuv_colorspace, color_primaries; int full_luma; pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; @@ -112,6 +112,8 @@ pthread_mutex_t open_mutex; int is_mutex_init; AVRational video_time_base; + mlt_frame last_good_frame; // for video error concealment + int last_good_position; // for video error concealment #ifdef VDPAU struct { @@ -186,7 +188,8 @@ if ( strcmp( service, "avformat-novalidate" ) ) { // Open the file - if ( producer_open( self, profile, file, 1 ) != 0 ) + mlt_properties_from_utf8( properties, "resource", "_resource" ); + if ( producer_open( self, profile, mlt_properties_get( properties, "_resource" ), 1 ) != 0 ) { // Clean up mlt_producer_close( producer ); @@ -566,6 +569,24 @@ return result; } +static mlt_image_format pick_pix_fmt( enum PixelFormat pix_fmt ) +{ + switch ( pix_fmt ) + { + case PIX_FMT_ARGB: + case PIX_FMT_RGBA: + case PIX_FMT_ABGR: + case PIX_FMT_BGRA: + return PIX_FMT_RGBA; +#if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) + case AV_PIX_FMT_BAYER_RGGB16LE: + return PIX_FMT_RGB24; +#endif + default: + return PIX_FMT_YUV422P; + } +} + static int get_basic_info( producer_avformat self, mlt_profile profile, const char *filename ) { int error = 0; @@ -575,10 +596,6 @@ AVFormatContext *format = self->video_format; - // We will treat everything with the producer fps. - // TODO: make this more flexible. - double fps = mlt_profile_fps( profile ); - // Get the duration if ( !mlt_properties_get_int( properties, "_length_computed" ) ) { @@ -587,7 +604,9 @@ if ( format->duration != AV_NOPTS_VALUE ) { // This isn't going to be accurate for all formats - mlt_position frames = ( mlt_position )( ( ( double )format->duration / ( double )AV_TIME_BASE ) * fps ); + // We will treat everything with the producer fps. + mlt_position frames = ( mlt_position )( int )( format->duration * + profile->frame_rate_num / profile->frame_rate_den / AV_TIME_BASE); mlt_properties_set_position( properties, "out", frames - 1 ); mlt_properties_set_position( properties, "length", frames ); mlt_properties_set_int( properties, "_length_computed", 1 ); @@ -642,9 +661,8 @@ get_aspect_ratio( properties, format->streams[ self->video_index ], codec_context ); // Verify that we can convert this to YUV 4:2:2 - // TODO: we can now also return RGB and RGBA and quite possibly more in the future. struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, codec_context->pix_fmt, - codec_context->width, codec_context->height, PIX_FMT_YUYV422, SWS_BILINEAR, NULL, NULL, NULL); + codec_context->width, codec_context->height, pick_pix_fmt( codec_context->pix_fmt ), SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else @@ -662,17 +680,18 @@ int error = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); + if ( !self->is_mutex_init ) + { + pthread_mutex_init( &self->audio_mutex, NULL ); + pthread_mutex_init( &self->video_mutex, NULL ); + pthread_mutex_init( &self->packets_mutex, NULL ); + pthread_mutex_init( &self->open_mutex, NULL ); + self->is_mutex_init = 1; + } + // Lock the service if ( take_lock ) { - if ( !self->is_mutex_init ) - { - pthread_mutex_init( &self->audio_mutex, NULL ); - pthread_mutex_init( &self->video_mutex, NULL ); - pthread_mutex_init( &self->packets_mutex, NULL ); - pthread_mutex_init( &self->open_mutex, NULL ); - self->is_mutex_init = 1; - } pthread_mutex_lock( &self->audio_mutex ); pthread_mutex_lock( &self->video_mutex ); } @@ -1027,43 +1046,53 @@ self->audio_streams, self->audio_max_stream, self->total_channels, self->max_channel ); } -static void set_luma_transfer( struct SwsContext *context, int colorspace, int use_full_range ) +static int set_luma_transfer( struct SwsContext *context, int src_colorspace, int dst_colorspace, int full_range ) { - int *coefficients; - const int *new_coefficients; - int full_range; - int brightness, contrast, saturation; - - if ( sws_getColorspaceDetails( context, &coefficients, &full_range, &coefficients, &full_range, - &brightness, &contrast, &saturation ) != -1 ) - { - // Don't change these from defaults unless explicitly told to. - if ( use_full_range >= 0 ) - full_range = use_full_range; - switch ( colorspace ) - { - case 170: - case 470: - case 601: - case 624: - new_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); - break; - case 240: - new_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); - break; - case 709: - new_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); - break; - default: - new_coefficients = coefficients; - break; - } - sws_setColorspaceDetails( context, new_coefficients, full_range, new_coefficients, full_range, - brightness, contrast, saturation ); + const int *src_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + const int *dst_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); + int brightness = 0; + int contrast = 1 << 16; + int saturation = 1 << 16; + + switch ( src_colorspace ) + { + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; + } + switch ( dst_colorspace ) + { + case 170: + case 470: + case 601: + case 624: + src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + break; + case 240: + src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + break; + case 709: + src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + break; } + return sws_setColorspaceDetails( context, src_coefficients, full_range, dst_coefficients, full_range, + brightness, contrast, saturation ); } -static mlt_image_format pick_pix_format( enum PixelFormat pix_fmt ) +static mlt_image_format pick_image_format( enum PixelFormat pix_fmt ) { switch ( pix_fmt ) { @@ -1083,7 +1112,10 @@ case PIX_FMT_MONOBLACK: case PIX_FMT_RGB8: case PIX_FMT_BGR8: +#if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) + case AV_PIX_FMT_BAYER_RGGB16LE: return mlt_image_rgb24; +#endif default: return mlt_image_yuv422; } @@ -1118,10 +1150,13 @@ } } -static void convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffer, int pix_fmt, +// returns resulting YUV colorspace +static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format *format, int width, int height, uint8_t **alpha ) { int flags = SWS_BICUBIC | SWS_ACCURATE_RND; + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self->parent ) ); + int result = self->yuv_colorspace; #ifdef USE_MMX flags |= SWS_CPU_CAPS_MMX; @@ -1130,6 +1165,10 @@ flags |= SWS_CPU_CAPS_MMX2; #endif + mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "%s @ %dx%d space %d->%d\n", + mlt_image_format_name( *format ), + width, height, self->yuv_colorspace, profile->colorspace ); + // extract alpha from planar formats if ( ( pix_fmt == PIX_FMT_YUVA420P #if defined(FFUDIV) && LIBAVUTIL_VERSION_INT >= ((51<<16)+(35<<8)+101) @@ -1152,7 +1191,8 @@ if ( *format == mlt_image_yuv420p ) { struct SwsContext *context = sws_getContext( width, height, pix_fmt, - width, height, PIX_FMT_YUV420P, flags, NULL, NULL, NULL); + width, height, self->full_luma ? PIX_FMT_YUVJ420P : PIX_FMT_YUV420P, + flags, NULL, NULL, NULL); AVPicture output; output.data[0] = buffer; output.data[1] = buffer + width * height; @@ -1160,7 +1200,8 @@ output.linesize[0] = width; output.linesize[1] = width >> 1; output.linesize[2] = width >> 1; - set_luma_transfer( context, self->colorspace, -1 ); + if ( !set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma ) ) + result = profile->colorspace; sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1171,7 +1212,8 @@ width, height, PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); - set_luma_transfer( context, self->colorspace, self->full_luma ); + // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. + set_luma_transfer( context, self->yuv_colorspace, 601, self->full_luma ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1182,7 +1224,8 @@ width, height, PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); - set_luma_transfer( context, self->colorspace, self->full_luma ); + // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. + set_luma_transfer( context, self->yuv_colorspace, 601, self->full_luma ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); @@ -1193,11 +1236,13 @@ width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); - set_luma_transfer( context, self->colorspace, -1 ); + if ( !set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma ) ) + result = profile->colorspace; sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } + return result; } /** Allocate the image buffer and set it on the frame. @@ -1210,9 +1255,6 @@ if ( codec_context->width == 0 || codec_context->height == 0 ) return size; - if ( *format == mlt_image_glsl ) - *format = pick_pix_format( codec_context->pix_fmt ); - *width = codec_context->width; *height = codec_context->height; size = mlt_image_format_size( *format, *width, *height, NULL ); @@ -1343,12 +1385,20 @@ context = self->video_format; stream = context->streams[ self->video_index ]; codec_context = stream->codec; - if ( *format == mlt_image_none || + if ( *format == mlt_image_none || *format == mlt_image_glsl || codec_context->pix_fmt == PIX_FMT_ARGB || codec_context->pix_fmt == PIX_FMT_RGBA || codec_context->pix_fmt == PIX_FMT_ABGR || codec_context->pix_fmt == PIX_FMT_BGRA ) - *format = pick_pix_format( codec_context->pix_fmt ); + *format = pick_image_format( codec_context->pix_fmt ); +#if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) + else if ( codec_context->pix_fmt == AV_PIX_FMT_BAYER_RGGB16LE ) { + if ( *format == mlt_image_yuv422 ) + *format = mlt_image_yuv420p; + else if ( *format == mlt_image_rgb24a ) + *format = mlt_image_rgb24; + } +#endif // Duplicate the last image if necessary if ( self->video_frame && self->video_frame->linesize[0] @@ -1357,6 +1407,7 @@ // Duplicate it if ( ( image_size = allocate_buffer( frame, codec_context, buffer, format, width, height ) ) ) { + int yuv_colorspace; // Workaround 1088 encodings missing cropping info. if ( *height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) *height = 1080; @@ -1370,13 +1421,14 @@ picture.linesize[0] = codec_context->width; picture.linesize[1] = codec_context->width / 2; picture.linesize[2] = codec_context->width / 2; - convert_image( self, (AVFrame*) &picture, *buffer, + yuv_colorspace = convert_image( self, (AVFrame*) &picture, *buffer, PIX_FMT_YUV420P, format, *width, *height, &alpha ); } else #endif - convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, + yuv_colorspace = convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, format, *width, *height, &alpha ); + mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); got_picture = 1; } } @@ -1431,12 +1483,15 @@ mlt_log_fatal( MLT_PRODUCER_SERVICE(producer), "Exiting with error due to disconnected source.\n" ); exit( EXIT_FAILURE ); } + // Send null packets to drain decoder. + self->pkt.size = 0; + self->pkt.data = NULL; } } pthread_mutex_unlock( &self->packets_mutex ); // We only deal with video from the selected video_index - if ( ret >= 0 && self->pkt.stream_index == self->video_index && self->pkt.size > 0 ) + if ( self->pkt.stream_index == self->video_index ) { int64_t pts = best_pts( self, self->pkt.pts, self->pkt.dts ); if ( pts != AV_NOPTS_VALUE ) @@ -1464,7 +1519,7 @@ self->last_position = int_position; // Decode the image - if ( must_decode || int_position >= req_position ) + if ( must_decode || int_position >= req_position || !self->pkt.data ) { #ifdef VDPAU if ( self->vdpau ) @@ -1484,13 +1539,16 @@ #else ret = avcodec_decode_video( codec_context, self->video_frame, &got_picture, self->pkt.data, self->pkt.size ); #endif + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "decoded packet with size %d => %d\n", self->pkt.size, ret ); // Note: decode may fail at the beginning of MPEGfile (B-frames referencing before first I-frame), so allow a few errors. if ( ret < 0 ) { - if ( ++decode_errors <= 10 ) + if ( ++decode_errors <= 10 ) { ret = 0; - else + } else { mlt_log_warning( MLT_PRODUCER_SERVICE(producer), "video decoding error %d\n", ret ); + self->last_good_position = POSITION_INVALID; + } } else { @@ -1519,7 +1577,11 @@ else if ( int_position >= req_position ) codec_context->skip_loop_filter = AVDISCARD_NONE; } - mlt_log_debug( MLT_PRODUCER_SERVICE(producer), " got_pic %d key %d\n", got_picture, self->pkt.flags & PKT_FLAG_KEY ); + else if ( !self->pkt.data ) // draining decoder with null packets + { + ret = -1; + } + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), " got_pic %d key %d ret %d pkt_pos %d\n", got_picture, self->pkt.flags & PKT_FLAG_KEY, ret, int_position ); } // Now handle the picture if we have one @@ -1527,6 +1589,7 @@ { if ( ( image_size = allocate_buffer( frame, codec_context, buffer, format, width, height ) ) ) { + int yuv_colorspace; // Workaround 1088 encodings missing cropping info. if ( *height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) *height = 1080; @@ -1552,8 +1615,9 @@ VdpStatus status = vdp_surface_get_bits( render->surface, dest_format, planes, pitches ); if ( status == VDP_STATUS_OK ) { - convert_image( self, self->video_frame, *buffer, PIX_FMT_YUV420P, + yuv_colorspace = convert_image( self, self->video_frame, *buffer, PIX_FMT_YUV420P, format, *width, *height, &alpha ); + mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); } else { @@ -1565,12 +1629,14 @@ { mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU error in VdpDecoderRender\n" ); image_size = got_picture = 0; + self->last_good_position = POSITION_INVALID; } } else #endif - convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, + yuv_colorspace = convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, format, *width, *height, &alpha ); + mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); self->top_field_first |= self->video_frame->top_field_first; self->current_position = int_position; } @@ -1591,42 +1657,45 @@ if ( alpha ) mlt_frame_set_alpha( frame, alpha, (*width) * (*height), mlt_pool_release ); - if ( image_size > 0 && self->image_cache ) + if ( image_size > 0 ) { mlt_properties_set_int( frame_properties, "format", *format ); - mlt_cache_put_frame( self->image_cache, frame ); - } + // Cache the image for rapid repeated access. + if ( self->image_cache ) { + mlt_cache_put_frame( self->image_cache, frame ); + } + // Clone frame for error concealment. + if ( self->current_position >= self->last_good_position ) { + self->last_good_position = self->current_position; + if ( self->last_good_frame ) + mlt_frame_close( self->last_good_frame ); + self->last_good_frame = mlt_frame_clone( frame, 1 ); + } + } + else if ( self->last_good_frame ) + { + // Use last known good frame if there was a decoding failure. + mlt_frame original = mlt_frame_clone( self->last_good_frame, 1 ); + mlt_properties orig_props = MLT_FRAME_PROPERTIES( original ); + int size = 0; + + *buffer = mlt_properties_get_data( orig_props, "alpha", &size ); + if (*buffer) + mlt_frame_set_alpha( frame, *buffer, size, NULL ); + *buffer = mlt_properties_get_data( orig_props, "image", &size ); + mlt_frame_set_image( frame, *buffer, size, NULL ); + mlt_properties_set_data( frame_properties, "avformat.conceal_error", original, 0, (mlt_destructor) mlt_frame_close, NULL ); + *format = mlt_properties_get_int( orig_props, "format" ); + + // Set the resolution + *width = codec_context->width; + *height = codec_context->height; - // Try to duplicate last image if there was a decoding failure - // TODO: with multithread decoding a partial frame decoding resulting - // in failure also resets av_frame making test below fail. - if ( !image_size && self->video_frame && self->video_frame->linesize[0] ) - { - // Duplicate it - if ( ( image_size = allocate_buffer( frame, codec_context, buffer, format, width, height ) ) ) - { - // Workaround 1088 encodings missing cropping info. - if ( *height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) - *height = 1080; -#ifdef VDPAU - if ( self->vdpau && self->vdpau->buffer ) - { - AVPicture picture; - picture.data[0] = self->vdpau->buffer; - picture.data[2] = self->vdpau->buffer + codec_context->width * codec_context->height; - picture.data[1] = self->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4; - picture.linesize[0] = codec_context->width; - picture.linesize[1] = codec_context->width / 2; - picture.linesize[2] = codec_context->width / 2; - convert_image( self, (AVFrame*) &picture, *buffer, - PIX_FMT_YUV420P, format, *width, *height, &alpha ); - } - else -#endif - convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, - format, *width, *height, &alpha ); - got_picture = 1; - } + // Workaround 1088 encodings missing cropping info. + if ( *height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) + *height = 1080; + + got_picture = 1; } // Regardless of speed, we expect to get the next frame (cos we ain't too bright) @@ -1776,7 +1845,7 @@ #endif double fps = av_q2d( frame_rate ); -#if LIBAVFORMAT_VERSION_MAJOR < 55 +#if LIBAVFORMAT_VERSION_MAJOR < 55 || defined(FFUDIV) // Verify and sanitize the muxer frame rate. if ( isnan( fps ) || isinf( fps ) || fps == 0 ) { @@ -1784,7 +1853,7 @@ fps = av_q2d( frame_rate ); } #endif -#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(42<<8)+0) && LIBAVFORMAT_VERSION_MAJOR < 55 +#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(42<<8)+0) && ( LIBAVFORMAT_VERSION_MAJOR < 55 || defined(FFUDIV) ) // With my samples when r_frame_rate != 1000 but avg_frame_rate is valid, // avg_frame_rate gives some approximate value that does not well match the media. // Also, on my sample where r_frame_rate = 1000, using avg_frame_rate directly @@ -1823,33 +1892,49 @@ mlt_properties_set_int( properties, "meta.media.frame_rate_den", frame_rate.den ); // Set the YUV colorspace from override or detect - self->colorspace = mlt_properties_get_int( properties, "force_colorspace" ); + self->yuv_colorspace = mlt_properties_get_int( properties, "force_colorspace" ); #if LIBAVCODEC_VERSION_INT > ((52<<16)+(28<<8)+0) - if ( ! self->colorspace ) + if ( ! self->yuv_colorspace ) { switch ( self->video_codec->colorspace ) { case AVCOL_SPC_SMPTE240M: - self->colorspace = 240; + self->yuv_colorspace = 240; break; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: - self->colorspace = 601; + self->yuv_colorspace = 601; break; case AVCOL_SPC_BT709: - self->colorspace = 709; + self->yuv_colorspace = 709; break; default: // This is a heuristic Charles Poynton suggests in "Digital Video and HDTV" - self->colorspace = self->video_codec->width * self->video_codec->height > 750000 ? 709 : 601; + self->yuv_colorspace = self->video_codec->width * self->video_codec->height > 750000 ? 709 : 601; break; } } #endif // Let apps get chosen colorspace - mlt_properties_set_int( properties, "meta.media.colorspace", self->colorspace ); + mlt_properties_set_int( properties, "meta.media.colorspace", self->yuv_colorspace ); - self->full_luma = -1; + switch ( self->video_codec->color_primaries ) + { + case AVCOL_PRI_BT470BG: + self->color_primaries = 601625; + break; + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_SMPTE240M: + self->color_primaries = 601525; + break; + case AVCOL_PRI_BT709: + case AVCOL_PRI_UNSPECIFIED: + default: + self->color_primaries = 709; + break; + } + + self->full_luma = 0; #if LIBAVCODEC_VERSION_INT >= ((52<<16)+(72<<8)+2) mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "color_range %d\n", codec_context->color_range ); if ( codec_context->color_range == AVCOL_RANGE_JPEG ) @@ -1885,8 +1970,9 @@ { unlock_needed = 1; pthread_mutex_lock( &self->video_mutex ); + mlt_properties_from_utf8( properties, "resource", "_resource" ); producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), - mlt_properties_get( properties, "resource" ), 0 ); + mlt_properties_get( properties, "_resource" ), 0 ); context = self->video_format; } @@ -1907,7 +1993,7 @@ } // Update the video properties if the index changed - if ( index != self->video_index ) + if ( context && index > -1 && index != self->video_index ) { // Reset the video properties if the index changed self->video_index = index; @@ -1935,7 +2021,9 @@ mlt_properties_set_int( properties, "meta.media.width", self->video_codec->width ); mlt_properties_set_int( properties, "meta.media.height", self->video_codec->height ); mlt_properties_set_double( frame_properties, "aspect_ratio", aspect_ratio ); - mlt_properties_set_int( frame_properties, "colorspace", self->colorspace ); + mlt_properties_set_int( frame_properties, "colorspace", self->yuv_colorspace ); + mlt_properties_set_int( frame_properties, "color_primaries", self->color_primaries ); + mlt_properties_set_int( frame_properties, "full_luma", self->full_luma ); // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) @@ -2010,31 +2098,33 @@ #endif } -static void planar_to_interleaved( uint8_t *dest, uint8_t *src, int samples, int channels, int bytes_per_sample ) +#if LIBAVCODEC_VERSION_MAJOR >= 55 +static void planar_to_interleaved( uint8_t *dest, AVFrame *src, int samples, int channels, int bytes_per_sample ) { int s, c; for ( s = 0; s < samples; s++ ) { for ( c = 0; c < channels; c++ ) { - memcpy( dest, src + ( c * samples + s ) * bytes_per_sample, bytes_per_sample ); + memcpy( dest, &src->data[c][s * bytes_per_sample], bytes_per_sample ); dest += bytes_per_sample; } } } - -static void planar_to_interleaved2( uint8_t *dest, AVFrame *src, int samples, int channels, int bytes_per_sample ) +#else +static void planar_to_interleaved( uint8_t *dest, uint8_t *src, int samples, int channels, int bytes_per_sample ) { int s, c; for ( s = 0; s < samples; s++ ) { for ( c = 0; c < channels; c++ ) { - memcpy( dest, &src->data[c][s * bytes_per_sample], bytes_per_sample ); + memcpy( dest, src + ( c * samples + s ) * bytes_per_sample, bytes_per_sample ); dest += bytes_per_sample; } } } +#endif static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int channels, int samples, double timecode, double fps ) { @@ -2052,11 +2142,9 @@ uint8_t *decode_buffer = self->decode_buffer[ index ]; int audio_used = self->audio_used[ index ]; - uint8_t *ptr = pkt.data; - int len = pkt.size; int ret = 0; - while ( ptr && ret >= 0 && len > 0 ) + while ( pkt.data && pkt.size > 0 ) { int sizeof_sample = sample_bytes( codec_context ); int data_size = self->audio_buffer_size[ index ]; @@ -2082,8 +2170,9 @@ break; } - pkt.size = len -= ret; - pkt.data = ptr += ret; + // Consume (sometimes partial) data in the packet. + pkt.size -= ret; + pkt.data += ret; // If decoded successfully if ( data_size > 0 ) @@ -2106,7 +2195,7 @@ case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: #if LIBAVCODEC_VERSION_MAJOR >= 55 - planar_to_interleaved2( dest, self->audio_frame, convert_samples, codec_context->channels, sizeof_sample ); + planar_to_interleaved( dest, self->audio_frame, convert_samples, codec_context->channels, sizeof_sample ); #else planar_to_interleaved( dest, decode_buffer, convert_samples, codec_context->channels, sizeof_sample ); #endif @@ -2209,8 +2298,8 @@ index = 0; index_max = FFMIN( MAX_AUDIO_STREAMS, context->nb_streams ); *channels = self->total_channels; - *samples = mlt_sample_calculator( fps, FFMAX( self->max_frequency, *frequency ), position ); - *frequency = FFMAX( self->max_frequency, *frequency ); + *samples = mlt_sample_calculator( fps, self->max_frequency, position ); + *frequency = self->max_frequency; } // Initialize the buffers @@ -2487,8 +2576,9 @@ // Reopen the file if necessary if ( !context && self->audio_index > -1 && index > -1 ) { + mlt_properties_from_utf8( properties, "resource", "_resource" ); producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), - mlt_properties_get( properties, "resource" ), 1 ); + mlt_properties_get( properties, "_resource" ), 1 ); context = self->audio_format; } @@ -2611,7 +2701,8 @@ av_free_packet( &self->pkt ); av_free( self->video_frame ); av_free( self->audio_frame ); - pthread_mutex_lock( &self->open_mutex ); + if ( self->is_mutex_init ) + pthread_mutex_lock( &self->open_mutex ); int i; for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) { @@ -2640,12 +2731,15 @@ if ( self->video_format ) av_close_input_file( self->video_format ); #endif - pthread_mutex_unlock( &self->open_mutex ); + if ( self->is_mutex_init ) + pthread_mutex_unlock( &self->open_mutex ); #ifdef VDPAU vdpau_producer_close( self ); #endif if ( self->image_cache ) mlt_cache_close( self->image_cache ); + if ( self->last_good_frame ) + mlt_frame_close( self->last_good_frame ); // Cleanup the mutexes if ( self->is_mutex_init ) diff -Nru mlt-0.9.0/src/modules/avsync/consumer_blipflash.c mlt-0.9.2/src/modules/avsync/consumer_blipflash.c --- mlt-0.9.0/src/modules/avsync/consumer_blipflash.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/consumer_blipflash.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,427 +0,0 @@ -/* - * consumer_blipflash.c -- a consumer to measure A/V sync from a blip/flash - * source - * Copyright (C) 2013 Brian Matherly - * Author: Brian Matherly - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -// mlt Header files -#include -#include -#include - -// System header files -#include -#include -#include -#include -#include - -// Private constants -#define SAMPLE_FREQ 48000 -#define FLASH_LUMA_THRESHOLD 150 -#define BLIP_THRESHOLD 0.5 - -// Private types -typedef struct -{ - int64_t flash_history[2]; - int flash_history_count; - int64_t blip_history[2]; - int blip_history_count; - int blip_in_progress; - int samples_since_blip; - int blip; - int flash; - int sample_offset; - FILE* out_file; - int report_frames; -} avsync_stats; - -// Forward references. -static int consumer_start( mlt_consumer consumer ); -static int consumer_stop( mlt_consumer consumer ); -static int consumer_is_stopped( mlt_consumer consumer ); -static void *consumer_thread( void *arg ); -static void consumer_close( mlt_consumer consumer ); - -/** Initialize the consumer. -*/ - -mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - // Allocate the consumer - mlt_consumer consumer = mlt_consumer_new( profile ); - mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); - avsync_stats* stats = NULL; - - // If memory allocated and initializes without error - if ( consumer != NULL ) - { - // Set up start/stop/terminated callbacks - consumer->close = consumer_close; - consumer->start = consumer_start; - consumer->stop = consumer_stop; - consumer->is_stopped = consumer_is_stopped; - - stats = mlt_pool_alloc( sizeof( avsync_stats ) ); - stats->flash_history_count = 0; - stats->blip_history_count = 0; - stats->blip_in_progress = 0; - stats->samples_since_blip = 0; - stats->blip = 0; - stats->flash = 0; - stats->sample_offset = INT_MAX; - stats->report_frames = 0; - stats->out_file = stdout; - if ( arg != NULL ) - { - FILE* out_file = fopen( arg, "w" ); - if ( out_file != NULL ) - stats->out_file = out_file; - } - mlt_properties_set_data( consumer_properties, "_stats", stats, 0, NULL, NULL ); - - mlt_properties_set( consumer_properties, "report", "blip" ); - } - - // Return this - return consumer; -} - -/** Start the consumer. -*/ - -static int consumer_start( mlt_consumer consumer ) -{ - // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); - - // Check that we're not already running - if ( !mlt_properties_get_int( properties, "_running" ) ) - { - // Allocate a thread - pthread_t *thread = calloc( 1, sizeof( pthread_t ) ); - - // Assign the thread to properties - mlt_properties_set_data( properties, "_thread", thread, sizeof( pthread_t ), free, NULL ); - - // Set the running state - mlt_properties_set_int( properties, "_running", 1 ); - - // Create the thread - pthread_create( thread, NULL, consumer_thread, consumer ); - } - return 0; -} - -/** Stop the consumer. -*/ - -static int consumer_stop( mlt_consumer consumer ) -{ - // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); - - // Check that we're running - if ( mlt_properties_get_int( properties, "_running" ) ) - { - // Get the thread - pthread_t *thread = mlt_properties_get_data( properties, "_thread", NULL ); - - // Stop the thread - mlt_properties_set_int( properties, "_running", 0 ); - - // Wait for termination - if ( thread ) - pthread_join( *thread, NULL ); - } - - return 0; -} - -/** Determine if the consumer is stopped. -*/ - -static int consumer_is_stopped( mlt_consumer consumer ) -{ - // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); - return !mlt_properties_get_int( properties, "_running" ); -} - -static void detect_flash( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) -{ - int width = 0; - int height = 0; - mlt_image_format format = mlt_image_yuv422; - uint8_t* image = NULL; - int error = mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ); - - if ( !error && format == mlt_image_yuv422 && image != NULL ) - { - int i, j = 0; - int y_accumulator = 0; - - // Add up the luma values from 4 samples in 4 different quadrants. - for( i = 1; i < 3; i++ ) - { - int x = ( width / 3 ) * i; - x = x - x % 2; // Make sure this is a luma sample - for( j = 1; j < 3; j++ ) - { - int y = ( height / 3 ) * j; - y_accumulator += image[ y * height * 2 + x * 2 ]; - } - } - // If the average luma value is > 150, assume it is a flash. - stats->flash = ( y_accumulator / 4 ) > FLASH_LUMA_THRESHOLD; - } - - if( stats->flash ) - { - stats->flash_history[1] = stats->flash_history[0]; - stats->flash_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); - if( stats->flash_history_count < 2 ) - { - stats->flash_history_count++; - } - } -} - -static void detect_blip( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) -{ - int frequency = SAMPLE_FREQ; - int channels = 1; - int samples = mlt_sample_calculator( fps, frequency, pos ); - mlt_audio_format format = mlt_audio_float; - float* buffer = NULL; - int error = mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, &samples ); - - if ( !error && format == mlt_audio_float && buffer != NULL ) - { - int i = 0; - - for( i = 0; i < samples; i++ ) - { - if( !stats->blip_in_progress ) - { - if( buffer[i] > BLIP_THRESHOLD || buffer[i] < -BLIP_THRESHOLD ) - { - // This sample must start a blip - stats->blip_in_progress = 1; - stats->samples_since_blip = 0; - - stats->blip_history[1] = stats->blip_history[0]; - stats->blip_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); - stats->blip_history[0] += i; - if( stats->blip_history_count < 2 ) - { - stats->blip_history_count++; - } - stats->blip = 1; - } - } - else - { - if( buffer[i] > -BLIP_THRESHOLD && buffer[i] < BLIP_THRESHOLD ) - { - if( ++stats->samples_since_blip > frequency / 1000 ) - { - // One ms of silence means the blip is over - stats->blip_in_progress = 0; - stats->samples_since_blip = 0; - } - } - else - { - stats->samples_since_blip = 0; - } - } - } - } -} - -static void calculate_sync( avsync_stats* stats ) -{ - if( stats->blip || stats->flash ) - { - if( stats->flash_history_count > 0 && - stats->blip_history_count > 0 && - stats->blip_history[0] == stats->flash_history[0] ) - { - // The flash and blip occurred at the same time. - stats->sample_offset = 0; - } - if( stats->flash_history_count > 1 && - stats->blip_history_count > 0 && - stats->blip_history[0] <= stats->flash_history[0] && - stats->blip_history[0] >= stats->flash_history[1] ) - { - // The latest blip occurred between two flashes - if( stats->flash_history[0] - stats->blip_history[0] > - stats->blip_history[0] - stats->flash_history[1] ) - { - // Blip is closer to the previous flash. - // F1---B0--------F0 - // ^----^ - // Video leads audio (negative number). - stats->sample_offset = (int)(stats->flash_history[1] - stats->blip_history[0] ); - } - else - { - // Blip is closer to the current flash. - // F1--------B0---F0 - // ^----^ - // Audio leads video (positive number). - stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0]); - } - } - else if( stats->blip_history_count > 1 && - stats->flash_history_count > 0 && - stats->flash_history[0] <= stats->blip_history[0] && - stats->flash_history[0] >= stats->blip_history[1] ) - { - // The latest flash occurred between two blips - if( stats->blip_history[0] - stats->flash_history[0] > - stats->flash_history[0] - stats->blip_history[1] ) - { - // Flash is closer to the previous blip. - // B1---F0--------B0 - // ^----^ - // Audio leads video (positive number). - stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[1]); - } - else - { - // Flash is closer to the latest blip. - // B1--------F0---B0 - // ^----^ - // Video leads audio (negative number). - stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0] ); - } - } - } -} - -static void report_results( avsync_stats* stats, mlt_position pos ) -{ - if( stats->report_frames || stats->blip ) - { - if( stats->sample_offset == INT_MAX ) - { - fprintf( stats->out_file, MLT_POSITION_FMT "\t??\n", pos ); - } - else - { - // Convert to milliseconds. - double ms_offset = (double)stats->sample_offset * 1000.0 / (double)SAMPLE_FREQ; - fprintf( stats->out_file, MLT_POSITION_FMT "\t%02.02f\n", pos, ms_offset ); - } - } - stats->blip = 0; - stats->flash = 0; -} - -/** The main thread - the argument is simply the consumer. -*/ - -static void *consumer_thread( void *arg ) -{ - // Map the argument to the object - mlt_consumer consumer = arg; - - // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); - - // Convenience functionality - int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); - int terminated = 0; - - // Frame and size - mlt_frame frame = NULL; - - // Loop while running - while( !terminated && mlt_properties_get_int( properties, "_running" ) ) - { - // Get the frame - frame = mlt_consumer_rt_frame( consumer ); - - // Check for termination - if ( terminate_on_pause && frame != NULL ) - terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; - - // Check that we have a frame to work with - if ( frame ) - { - avsync_stats* stats = mlt_properties_get_data( properties, "_stats", NULL ); - double fps = mlt_properties_get_double( properties, "fps" ); - mlt_position pos = mlt_frame_get_position( frame ); - - if( !strcmp( mlt_properties_get( properties, "report" ), "frame" ) ) - { - stats->report_frames = 1; - } - else - { - stats->report_frames = 0; - } - - detect_flash( frame, pos, fps, stats ); - detect_blip( frame, pos, fps, stats ); - calculate_sync( stats ); - report_results( stats, pos ); - - // Close the frame - mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); - mlt_frame_close( frame ); - } - } - - // Indicate that the consumer is stopped - mlt_properties_set_int( properties, "_running", 0 ); - mlt_consumer_stopped( consumer ); - - return NULL; -} - -/** Close the consumer. -*/ - -static void consumer_close( mlt_consumer consumer ) -{ - mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); - avsync_stats* stats = mlt_properties_get_data( consumer_properties, "_stats", NULL ); - - // Stop the consumer - mlt_consumer_stop( consumer ); - - // Close the file - if( stats->out_file != stdout ) - { - fclose( stats->out_file ); - } - - // Clean up memory - mlt_pool_release( stats ); - - // Close the parent - mlt_consumer_close( consumer ); - - // Free the memory - free( consumer ); -} diff -Nru mlt-0.9.0/src/modules/avsync/consumer_blipflash.yml mlt-0.9.2/src/modules/avsync/consumer_blipflash.yml --- mlt-0.9.0/src/modules/avsync/consumer_blipflash.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/consumer_blipflash.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -schema_version: 0.1 -type: consumer -identifier: blipflash -title: Blip Flash -version: 1 -copyright: Brian Matherly -creator: Brian Matherly -license: LGPLv2.1 -language: en -tags: - - Video - - Audio -description: > - Calculate the A/V sync for a blip flash source. - Sync can be recalculated whenever a blip or a flash is detected. -parameters: - - identifier: argument - title: Report File - type: string - description: > - The file to report the results to. If empty, the results will be reported to standard out. - required: no - widget: filesave - - identifier: report - title: Report Style - type: string - description: > - When to report sync - every frame or only when blips occur. - default: blip - values: - - blip - - frame - mutable: yes - widget: combo diff -Nru mlt-0.9.0/src/modules/avsync/factory.c mlt-0.9.2/src/modules/avsync/factory.c --- mlt-0.9.0/src/modules/avsync/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/factory.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * factory.c -- the factory method interfaces - * Copyright (C) 2013 Brian Matherly - * Author: Brian Matherly - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include - -extern mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); -extern mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); - -static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) -{ - char file[ PATH_MAX ]; - snprintf( file, PATH_MAX, "%s/avsync/%s", mlt_environment( "MLT_DATA" ), (char*) data ); - return mlt_properties_parse_yaml( file ); -} - -MLT_REPOSITORY -{ - MLT_REGISTER( consumer_type, "blipflash", consumer_blipflash_init ); - MLT_REGISTER( producer_type, "blipflash", producer_blipflash_init ); - - MLT_REGISTER_METADATA( consumer_type, "blipflash", metadata, "consumer_blipflash.yml" ); - MLT_REGISTER_METADATA( producer_type, "blipflash", metadata, "producer_blipflash.yml" ); -} diff -Nru mlt-0.9.0/src/modules/avsync/Makefile mlt-0.9.2/src/modules/avsync/Makefile --- mlt-0.9.0/src/modules/avsync/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -CFLAGS += -I../.. - -LDFLAGS += -L../../framework -lmlt -lm -lpthread - -include ../../../config.mak - -TARGET = ../libmltavsync$(LIBSUF) - -OBJS = factory.o \ - producer_blipflash.o \ - consumer_blipflash.o - -ASM_OBJS = - -SRCS := $(OBJS:.o=.c) - -all: $(TARGET) - -$(TARGET): $(OBJS) $(ASM_OBJS) - $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS) - -depend: $(SRCS) - $(CC) -MM $(CFLAGS) $^ 1>.depend - -distclean: clean - rm -f .depend - -clean: - rm -f $(OBJS) $(ASM_OBJS) $(TARGET) - -install: all - install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" - install -d "$(DESTDIR)$(mltdatadir)/avsync" - install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/avsync" - -ifneq ($(wildcard .depend),) -include .depend -endif diff -Nru mlt-0.9.0/src/modules/avsync/producer_blipflash.c mlt-0.9.2/src/modules/avsync/producer_blipflash.c --- mlt-0.9.0/src/modules/avsync/producer_blipflash.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/producer_blipflash.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,305 +0,0 @@ -/* - * producer_blipflash.c -- blip/flash generating producer - * Copyright (C) 2013 Brian Matherly - * Author: Brian Matherly - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -/** Fill an audio buffer with 1kHz "blip" samples. -*/ - -static void fill_blip( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) -{ - int new_size = samples * channels * sizeof( float ); - int old_size = 0; - float* blip = mlt_properties_get_data( producer_properties, "_blip", &old_size ); - - if( !blip || new_size > old_size ) - { - blip = mlt_pool_alloc( new_size ); - - // Fill the blip buffer - if ( blip != NULL ) - { - int s = 0; - int c = 0; - - for( s = 0; s < samples; s++ ) - { - float f = 1000.0; - float t = (float)s/(float)frequency; - // Add 90 deg so the blip always starts at 1 for easy detection. - float phase = M_PI / 2; - float value = sin( 2*M_PI*f*t + phase ); - - for( c = 0; c < channels; c++ ) - { - float* sample_ptr = ((float*) blip) + (c * samples) + s; - *sample_ptr = value; - } - } - } - // Cache the audio blip to save from regenerating it with every blip. - mlt_properties_set_data( producer_properties, "_blip", blip, new_size, mlt_pool_release, NULL ); - }; - - if( blip ) memcpy( buffer, blip, new_size ); -} - -static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) -{ - mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - int size = *samples * *channels * sizeof( float ); - double fps = mlt_producer_get_fps( producer ); - int frames = mlt_frame_get_position( frame ) + mlt_properties_get_int( producer_properties, "offset" ); - int seconds = frames / fps; - - // Correct the returns if necessary - *format = mlt_audio_float; - *frequency = *frequency <= 0 ? 48000 : *frequency; - *channels = *channels <= 0 ? 2 : *channels; - *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, frames ) : *samples; - - // Allocate the buffer - *buffer = mlt_pool_alloc( size ); - - // Determine if this should be a blip or silence. - frames = frames % lrint( fps ); - seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); - if( seconds == 0 && frames == 0 ) - { - fill_blip( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); - } - else - { - // Fill silence. - memset( *buffer, 0, size ); - } - - // Set the buffer for destruction - mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); - - return 0; -} - -/** Fill an image buffer with either white (flash) or black as requested. -*/ - -static void fill_image( mlt_properties producer_properties, char* color, uint8_t* buffer, mlt_image_format format, int width, int height ) -{ - - int new_size = mlt_image_format_size( format, width, height, NULL ); - int old_size = 0; - uint8_t* image = mlt_properties_get_data( producer_properties, color, &old_size ); - - if( !image || new_size > old_size ) - { - // Need to create a new cached image. - image = mlt_pool_alloc( new_size ); - - if ( image != NULL ) - { - uint8_t r, g, b; - uint8_t* p = image; - - if( !strcmp( color, "_flash" ) ) - { - r = g = b = 255; // White - } - else - { - r = g = b = 0; // Black - } - - switch( format ) - { - default: - case mlt_image_yuv422: - { - int uneven = width % 2; - int count = ( width - uneven ) / 2 + 1; - uint8_t y, u, v; - - RGB2YUV_601_SCALED( r, g, b, y, u, v ); - int i = height + 1; - while ( --i ) - { - int j = count; - while ( --j ) - { - *p ++ = y; - *p ++ = u; - *p ++ = y; - *p ++ = v; - } - if ( uneven ) - { - *p ++ = y; - *p ++ = u; - } - } - break; - } - case mlt_image_rgb24: - { - int i = width * height + 1; - while ( --i ) - { - *p ++ = r; - *p ++ = g; - *p ++ = b; - } - break; - } - case mlt_image_rgb24a: - { - int i = width * height + 1; - while ( --i ) - { - *p ++ = r; - *p ++ = g; - *p ++ = b; - *p ++ = 255; // alpha - } - break; - } - } - // Cache the image to save from regenerating it with every frame. - mlt_properties_set_data( producer_properties, color, image, new_size, mlt_pool_release, NULL ); - } - } - - if( image ) memcpy( buffer, image, new_size ); -} - -static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable ) -{ - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - int size = 0; - double fps = mlt_producer_get_fps( producer ); - int frames = mlt_frame_get_position( frame ); - int seconds = frames / fps; - - mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); - - // Correct the returns if necessary - if( *format != mlt_image_yuv422 && *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) - *format = mlt_image_yuv422; - if( *width <= 0 ) - *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; - if ( *height <= 0 ) - *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; - - // Allocate the buffer - size = mlt_image_format_size( *format, *width, *height, NULL ); - *buffer = mlt_pool_alloc( size ); - - // Determine if this should be a flash or black. - frames = frames % lrint( fps ); - seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); - if( seconds == 0 && frames == 0 ) - { - fill_image( producer_properties, "_flash", *buffer, *format, *width, *height ); - } - else - { - fill_image( producer_properties, "_black", *buffer, *format, *width, *height ); - } - - mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); - - // Create the alpha channel - int alpha_size = *width * *height; - uint8_t *alpha = mlt_pool_alloc( alpha_size ); - if ( alpha ) - memset( alpha, 255, alpha_size ); - - // Update the frame - mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); - mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); - mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) ); - mlt_properties_set_int( properties, "progressive", 1 ); - mlt_properties_set_int( properties, "meta.media.width", *width ); - mlt_properties_set_int( properties, "meta.media.height", *height ); - - return 0; -} - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) -{ - // Generate a frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - - if ( *frame != NULL ) - { - // Obtain properties of frame - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); - - // Save the producer to be used later - mlt_properties_set_data( frame_properties, "_producer_blipflash", producer, 0, NULL, NULL ); - - // Update time code on the frame - mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - - // Configure callbacks - mlt_frame_push_get_image( *frame, producer_get_image ); - mlt_frame_push_audio( *frame, producer_get_audio ); - } - - // Calculate the next time code - mlt_producer_prepare_next( producer ); - - return 0; -} - -static void producer_close( mlt_producer this ) -{ - this->close = NULL; - mlt_producer_close( this ); - free( this ); -} - -/** Initialize. -*/ - -mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - // Create a new producer object - mlt_producer producer = mlt_producer_new( profile ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - - // Initialize the producer - if ( producer ) - { - mlt_properties_set_int( producer_properties, "period", 1 ); - mlt_properties_set_int( producer_properties, "offset", 0 ); - - // Callback registration - producer->get_frame = producer_get_frame; - producer->close = ( mlt_destructor )producer_close; - } - - return producer; -} diff -Nru mlt-0.9.0/src/modules/avsync/producer_blipflash.yml mlt-0.9.2/src/modules/avsync/producer_blipflash.yml --- mlt-0.9.0/src/modules/avsync/producer_blipflash.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/avsync/producer_blipflash.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -schema_version: 0.1 -type: producer -identifier: blipflash -title: Blip Flash -version: 1 -copyright: Brian Matherly -creator: Brian Matherly -license: LGPLv2.1 -language: en -tags: - - Audio - - Video -description: > - Generate periodic synchronized audio blips and video flashes. - Blips are a 1kHz tone and last the duration of the flash frame. -parameters: - - identifier: period - title: Flash Period - type: integer - description: > - The period between flashes in seconds. - default: 1 - readonly: no - mutable: yes - widget: spinner - - identifier: offset - title: Audio Offset - type: integer - description: > - The number of frames to offset the audio. - A positive number results in audio earlier than video. - An negative number results in audio later than video. - default: 0 - readonly: no - mutable: yes - widget: spinner diff -Nru mlt-0.9.0/src/modules/configure mlt-0.9.2/src/modules/configure --- mlt-0.9.0/src/modules/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/configure 2014-06-29 20:23:17.000000000 +0000 @@ -17,7 +17,7 @@ # Iterate through each of the components for i in * do - if [ -d $i -a \( "$help" = "1" -o ! -f disable-$i \) ] + if [ -d $i -a \( "$help" = "1" -o \( ! -f disable-$i -a ! -f $i/deprecated \) \) ] then if [ "$gpl" = "true" -o ! -f $i/gpl -o "$help" = "1" ] then diff -Nru mlt-0.9.0/src/modules/core/composite_line_yuv_sse2_simple.c mlt-0.9.2/src/modules/core/composite_line_yuv_sse2_simple.c --- mlt-0.9.0/src/modules/core/composite_line_yuv_sse2_simple.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/composite_line_yuv_sse2_simple.c 2014-06-29 20:23:17.000000000 +0000 @@ -25,11 +25,16 @@ { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00 }; + const static unsigned char const2[] = + { + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 + }; __asm__ volatile ( "pxor %%xmm0, %%xmm0 \n\t" /* clear zero register */ "movdqu (%4), %%xmm9 \n\t" /* load const1 */ + "movdqu (%7), %%xmm10 \n\t" /* load const2 */ "movd %0, %%xmm1 \n\t" /* load weight and decompose */ "movlhps %%xmm1, %%xmm1 \n\t" "pshuflw $0, %%xmm1, %%xmm1 \n\t" @@ -68,6 +73,10 @@ "movdqa %%xmm9, %%xmm4 \n\t" "psubw %%xmm3, %%xmm4 \n\t" "pmullw %%xmm2, %%xmm4 \n\t" + "movdqa %%xmm4, %%xmm5 \n\t" + "psrlw $8, %%xmm4 \n\t" + "paddw %%xmm5, %%xmm4 \n\t" + "paddw %%xmm10, %%xmm4 \n\t" "psrlw $8, %%xmm4 \n\t" "paddw %%xmm4, %%xmm3 \n\t" "packuswb %%xmm0, %%xmm3 \n\t" @@ -147,7 +156,15 @@ "pmullw %%xmm9, %%xmm6 \n\t" "paddw %%xmm3, %%xmm4 \n\t" /* dst = dst + src */ "paddw %%xmm5, %%xmm6 \n\t" - "psrlw $8, %%xmm4 \n\t" /* dst = dst >> 8 */ + "movdqa %%xmm4, %%xmm3 \n\t" /* dst = ((dst >> 8) + dst + 128) >> 8 */ + "movdqa %%xmm6, %%xmm5 \n\t" + "psrlw $8, %%xmm4 \n\t" + "psrlw $8, %%xmm6 \n\t" + "paddw %%xmm3, %%xmm4 \n\t" + "paddw %%xmm5, %%xmm6 \n\t" + "paddw %%xmm10, %%xmm4 \n\t" + "paddw %%xmm10, %%xmm6 \n\t" + "psrlw $8, %%xmm4 \n\t" "psrlw $8, %%xmm6 \n\t" // "pminsw %%xmm9, %%xmm4 \n\t" /* clamp values */ // "pminsw %%xmm9, %%xmm6 \n\t" @@ -182,7 +199,7 @@ "jnz loop_start \n\t" : - : "r" (weight >> 8), "r" (alpha_b), "r" (src), "r" (dest), "r" (const1) , "r" (alpha_a), "r" (width / 8) + : "r" (weight >> 8), "r" (alpha_b), "r" (src), "r" (dest), "r" (const1) , "r" (alpha_a), "r" (width / 8), "r" (const2) //: "xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7","xmm8","xmm9", "memory" ); }; diff -Nru mlt-0.9.0/src/modules/core/consumer_multi.c mlt-0.9.2/src/modules/core/consumer_multi.c --- mlt-0.9.0/src/modules/core/consumer_multi.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/consumer_multi.c 2014-06-29 20:23:17.000000000 +0000 @@ -259,12 +259,23 @@ if ( ( s = mlt_properties_get( properties, key ) ) ) { mlt_properties p = mlt_properties_new(); - int i, count; - if ( !p ) break; - mlt_properties_set( p, "mlt_service", mlt_properties_get( properties, key ) ); + + // Terminate mlt_service value at the argument delimiter if supplied. + // Needed here instead of just relying upon create_consumer() so that + // a properties preset is picked up correctly. + char *service = strdup( mlt_properties_get( properties, key ) ); + char *arg = strchr( service, ':' ); + if ( arg ) { + *arg ++ = '\0'; + mlt_properties_set( p, "target", arg ); + } + mlt_properties_set( p, "mlt_service", service ); + free( service ); + snprintf( key, sizeof(key), "%d.", index ); + int i, count; count = mlt_properties_count( properties ); for ( i = 0; i < count; i++ ) { @@ -365,6 +376,7 @@ // put ideal number of samples into cloned frame int deeply = index > 1 ? 1 : 0; mlt_frame clone_frame = mlt_frame_clone( frame, deeply ); + mlt_properties clone_props = MLT_FRAME_PROPERTIES( clone_frame ); int nested_samples = mlt_sample_calculator( nested_fps, frequency, nested_pos ); // -10 is an optimization to avoid tiny amounts of leftover samples nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples; @@ -380,15 +392,21 @@ nested_size = 0; } mlt_frame_set_audio( clone_frame, prev_buffer, format, nested_size, mlt_pool_release ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_samples", nested_samples ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_frequency", frequency ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_channels", channels ); + mlt_properties_set_int( clone_props, "audio_samples", nested_samples ); + mlt_properties_set_int( clone_props, "audio_frequency", frequency ); + mlt_properties_set_int( clone_props, "audio_channels", channels ); // chomp the audio current_samples -= nested_samples; current_size -= nested_size; buffer += nested_size; + // Fix some things + mlt_properties_set_int( clone_props, "meta.media.width", + mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "width" ) ); + mlt_properties_set_int( clone_props, "meta.media.height", + mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "height" ) ); + // send frame to nested consumer mlt_consumer_put_frame( nested, clone_frame ); mlt_properties_set_position( nested_props, "_multi_position", ++nested_pos ); diff -Nru mlt-0.9.0/src/modules/core/consumer_null.c mlt-0.9.2/src/modules/core/consumer_null.c --- mlt-0.9.0/src/modules/core/consumer_null.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/consumer_null.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * consumer_null.c -- a null consumer - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -29,11 +29,11 @@ #include // Forward references. -static int consumer_start( mlt_consumer this ); -static int consumer_stop( mlt_consumer this ); -static int consumer_is_stopped( mlt_consumer this ); +static int consumer_start( mlt_consumer consumer ); +static int consumer_stop( mlt_consumer consumer ); +static int consumer_is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); -static void consumer_close( mlt_consumer this ); +static void consumer_close( mlt_consumer consumer ); /** Initialise the dv consumer. */ @@ -41,31 +41,31 @@ mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the consumer - mlt_consumer this = mlt_consumer_new( profile ); + mlt_consumer consumer = mlt_consumer_new( profile ); // If memory allocated and initialises without error - if ( this != NULL ) + if ( consumer != NULL ) { // Assign close callback - this->close = consumer_close; + consumer->close = consumer_close; // Set up start/stop/terminated callbacks - this->start = consumer_start; - this->stop = consumer_stop; - this->is_stopped = consumer_is_stopped; + consumer->start = consumer_start; + consumer->stop = consumer_stop; + consumer->is_stopped = consumer_is_stopped; } - // Return this - return this; + // Return consumer + return consumer; } /** Start the consumer. */ -static int consumer_start( mlt_consumer this ) +static int consumer_start( mlt_consumer consumer ) { // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( this ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Check that we're not already running if ( !mlt_properties_get_int( properties, "running" ) ) @@ -81,7 +81,7 @@ mlt_properties_set_int( properties, "joined", 0 ); // Create the thread - pthread_create( thread, NULL, consumer_thread, this ); + pthread_create( thread, NULL, consumer_thread, consumer ); } return 0; } @@ -89,10 +89,10 @@ /** Stop the consumer. */ -static int consumer_stop( mlt_consumer this ) +static int consumer_stop( mlt_consumer consumer ) { // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( this ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Check that we're running if ( !mlt_properties_get_int( properties, "joined" ) ) @@ -115,10 +115,10 @@ /** Determine if the consumer is stopped. */ -static int consumer_is_stopped( mlt_consumer this ) +static int consumer_is_stopped( mlt_consumer consumer ) { // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( this ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); return !mlt_properties_get_int( properties, "running" ); } @@ -128,10 +128,10 @@ static void *consumer_thread( void *arg ) { // Map the argument to the object - mlt_consumer this = arg; + mlt_consumer consumer = arg; // Get the properties - mlt_properties properties = MLT_CONSUMER_PROPERTIES( this ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Convenience functionality int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); @@ -144,7 +144,7 @@ while( !terminated && mlt_properties_get_int( properties, "running" ) ) { // Get the frame - frame = mlt_consumer_rt_frame( this ); + frame = mlt_consumer_rt_frame( consumer ); // Check for termination if ( terminate_on_pause && frame != NULL ) @@ -161,7 +161,7 @@ // Indicate that the consumer is stopped mlt_properties_set_int( properties, "running", 0 ); - mlt_consumer_stopped( this ); + mlt_consumer_stopped( consumer ); return NULL; } @@ -169,14 +169,14 @@ /** Close the consumer. */ -static void consumer_close( mlt_consumer this ) +static void consumer_close( mlt_consumer consumer ) { // Stop the consumer - mlt_consumer_stop( this ); + mlt_consumer_stop( consumer ); // Close the parent - mlt_consumer_close( this ); + mlt_consumer_close( consumer ); // Free the memory - free( this ); + free( consumer ); } diff -Nru mlt-0.9.0/src/modules/core/factory.c mlt-0.9.2/src/modules/core/factory.c --- mlt-0.9.0/src/modules/core/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -53,10 +53,10 @@ extern mlt_producer producer_melt_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_melt_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv ); extern mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); -extern mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #include "transition_composite.h" extern mlt_transition transition_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_transition transition_matte_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #include "transition_region.h" static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) @@ -103,10 +103,10 @@ MLT_REGISTER( producer_type, "melt", producer_melt_init ); MLT_REGISTER( producer_type, "melt_file", producer_melt_file_init ); MLT_REGISTER( producer_type, "noise", producer_noise_init ); - MLT_REGISTER( producer_type, "ppm", producer_ppm_init ); MLT_REGISTER( transition_type, "composite", transition_composite_init ); MLT_REGISTER( transition_type, "luma", transition_luma_init ); MLT_REGISTER( transition_type, "mix", transition_mix_init ); + MLT_REGISTER( transition_type, "matte", transition_matte_init ); MLT_REGISTER( transition_type, "region", transition_region_init ); MLT_REGISTER_METADATA( consumer_type, "multi", metadata, "consumer_multi.yml" ); @@ -124,6 +124,7 @@ MLT_REGISTER_METADATA( filter_type, "mirror", metadata, "filter_mirror.yml" ); MLT_REGISTER_METADATA( filter_type, "mono", metadata, "filter_mono.yml" ); MLT_REGISTER_METADATA( filter_type, "obscure", metadata, "filter_obscure.yml" ); + MLT_REGISTER_METADATA( filter_type, "panner", metadata, "filter_panner.yml" ); MLT_REGISTER_METADATA( filter_type, "region", metadata, "filter_region.yml" ); MLT_REGISTER_METADATA( filter_type, "rescale", metadata, "filter_rescale.yml" ); MLT_REGISTER_METADATA( filter_type, "resize", metadata, "filter_resize.yml" ); @@ -140,5 +141,6 @@ MLT_REGISTER_METADATA( transition_type, "composite", metadata, "transition_composite.yml" ); MLT_REGISTER_METADATA( transition_type, "luma", metadata, "transition_luma.yml" ); MLT_REGISTER_METADATA( transition_type, "mix", metadata, "transition_mix.yml" ); + MLT_REGISTER_METADATA( transition_type, "matte", metadata, "transition_matte.yml" ); MLT_REGISTER_METADATA( transition_type, "region", metadata, "transition_region.yml" ); } diff -Nru mlt-0.9.0/src/modules/core/filter_audioconvert.c mlt-0.9.2/src/modules/core/filter_audioconvert.c --- mlt-0.9.0/src/modules/core/filter_audioconvert.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_audioconvert.c 2014-06-29 20:23:17.000000000 +0000 @@ -25,6 +25,10 @@ #include #include +#ifndef CLAMP +#define CLAMP( x, min, max ) (x) < (min) ? (min) : (x) > (max) ? (max) : (x) +#endif + static int convert_audio( mlt_frame frame, void **audio, mlt_audio_format *format, mlt_audio_format requested_format ) { int error = 1; @@ -102,8 +106,7 @@ while ( --i ) { float f = (float)( *q++ ) / 32768.0; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = f; + *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; @@ -176,8 +179,7 @@ for ( c = 0; c < channels; c++ ) { float f = (float)( *( q + c * samples + s ) ) / 2147483648.0; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = f; + *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; @@ -213,7 +215,7 @@ for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; + f = CLAMP( f, -1.0f, 1.0f ); *p++ = 32767 * f; } *audio = buffer; @@ -229,8 +231,9 @@ while ( --i ) { float f = *q++; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = ( f > 0 ? 2147483647LL : 2147483648LL ) * f; + f = CLAMP( f, -1.0f, 1.0f ); + int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; + *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; @@ -246,8 +249,9 @@ for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = ( f > 0 ? 2147483647LL : 2147483648LL ) * f; + f = CLAMP( f, -1.0f, 1.0f ); + int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; + *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; @@ -276,7 +280,7 @@ for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; + f = CLAMP( f, -1.0f, 1.0f ); *p++ = ( 127 * f ) + 128; } *audio = buffer; @@ -380,7 +384,7 @@ while ( --i ) { float f = *q++; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; + f = CLAMP( f, -1.0f , 1.0f ); *p++ = 32767 * f; } *audio = buffer; @@ -418,8 +422,9 @@ while ( --i ) { float f = *q; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = ( f > 0 ? 2147483647LL : 2147483648LL ) * f; + f = CLAMP( f, -1.0f , 1.0f ); + int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; + *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); q += channels; } } @@ -436,8 +441,9 @@ while ( --i ) { float f = *q++; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = ( f > 0 ? 2147483647LL : 2147483648LL ) * f; + f = CLAMP( f, -1.0f , 1.0f ); + int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; + *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; @@ -452,7 +458,7 @@ while ( --i ) { float f = *q++; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; + f = CLAMP( f, -1.0f , 1.0f ); *p++ = ( 127 * f ) + 128; } *audio = buffer; @@ -537,8 +543,7 @@ while ( --i ) { float f = ( (float) *q++ - 128 ) / 256.0f; - f = f > 1.0 ? 1.0 : f < -1.0 ? -1.0 : f; - *p++ = f; + *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; @@ -563,7 +568,7 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { frame->convert_audio = convert_audio; return frame; @@ -574,8 +579,9 @@ mlt_filter filter_audioconvert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) ); - if ( mlt_filter_init( this, this ) == 0 ) - this->process = filter_process; - return this; + mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); + if ( mlt_filter_init( filter, filter ) == 0 ) + filter->process = filter_process; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_audiowave.c mlt-0.9.2/src/modules/core/filter_audiowave.c --- mlt-0.9.0/src/modules/core/filter_audiowave.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_audiowave.c 2014-06-29 20:23:17.000000000 +0000 @@ -27,13 +27,13 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int size = *width * *height * 2; *format = mlt_image_yuv422; *image = mlt_pool_alloc( size ); - mlt_frame_set_image( this, *image, size, mlt_pool_release ); - uint8_t *wave = mlt_frame_get_waveform( this, *width, *height ); + mlt_frame_set_image( frame, *image, size, mlt_pool_release ); + uint8_t *wave = mlt_frame_get_waveform( frame, *width, *height ); if ( wave ) { uint8_t *p = *image; @@ -52,7 +52,7 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -63,9 +63,9 @@ mlt_filter filter_audiowave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - this->process = filter_process; - return this; + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + filter->process = filter_process; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_brightness.c mlt-0.9.2/src/modules/core/filter_brightness.c --- mlt-0.9.0/src/modules/core/filter_brightness.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_brightness.c 2014-06-29 20:23:17.000000000 +0000 @@ -30,17 +30,42 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + // Get the image *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { - // Get the brightness level - double level = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "brightness" ); + double level = 1.0; + + // Use animated "level" property only if it has been set since init + char* level_property = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "level" ); + if ( level_property != NULL ) + { + level = mlt_properties_anim_get_double( properties, "level", position, length ); + } + else + { + // Get level using old "start,"end" mechanics + // Get the starting brightness level + level = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "start" ) ); + + // If there is an end adjust gain to the range + if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "end" ) != NULL ) + { + // Determine the time position of this frame in the transition duration + double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "end" ) ); + level += ( end - level ) * mlt_filter_get_progress( filter, frame ); + } + } // Only process if level is something other than 1 if ( level != 1.0 ) @@ -65,21 +90,9 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - // Get the starting brightness level - double level = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "start" ) ); - - // If there is an end adjust gain to the range - if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL ) - { - // Determine the time position of this frame in the transition duration - double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "end" ) ); - level += ( end - level ) * mlt_filter_get_progress( this, frame ); - } - - // Push the frame filter - mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "brightness", level ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -90,12 +103,13 @@ mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "1" : arg ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "1" : arg ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "level", NULL ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_brightness.yml mlt-0.9.2/src/modules/core/filter_brightness.yml --- mlt-0.9.0/src/modules/core/filter_brightness.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_brightness.yml 2014-06-29 20:23:17.000000000 +0000 @@ -2,7 +2,7 @@ type: filter identifier: brightness title: Brightness -version: 1 +version: 2 copyright: Ushodaya Enterprises Limited creator: Dan Dennedy license: LGPLv2.1 @@ -29,3 +29,9 @@ minimum: 0.0 maximum: 15.0 default: 1.0 + - identifier: level + title: Level + type: float + minimum: 0.0 + maximum: 15.0 + default: unset diff -Nru mlt-0.9.0/src/modules/core/filter_channelcopy.c mlt-0.9.2/src/modules/core/filter_channelcopy.c --- mlt-0.9.0/src/modules/core/filter_channelcopy.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_channelcopy.c 2014-06-29 20:23:17.000000000 +0000 @@ -155,9 +155,9 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); // Propogate the parameters @@ -166,7 +166,7 @@ mlt_properties_set_int( frame_props, "channelcopy.swap", mlt_properties_get_int( properties, "swap" ) ); // Override the get_audio method - mlt_frame_push_audio( frame, this ); + mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; @@ -177,16 +177,17 @@ mlt_filter filter_channelcopy_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; + filter->process = filter_process; if ( arg != NULL ) - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", atoi( arg ) ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "to", atoi( arg ) ); else - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", 1 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "to", 1 ); if ( strcmp(id, "channelswap") == 0 ) - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "swap", 1 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "swap", 1 ); } - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_channelcopy.yml mlt-0.9.2/src/modules/core/filter_channelcopy.yml --- mlt-0.9.0/src/modules/core/filter_channelcopy.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_channelcopy.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -schema_version: 0.1 +schema_version: 0.2 type: filter identifier: channelcopy title: Copy Channels @@ -11,15 +11,10 @@ - Audio description: Copy one audio channel to another. parameters: - - identifier: argument - title: To - type: integer - minimum: 0 - maximum: 15 - default: 1 - identifier: to title: To type: integer + argument: true minimum: 0 maximum: 15 default: 1 diff -Nru mlt-0.9.0/src/modules/core/filter_crop.c mlt-0.9.2/src/modules/core/filter_crop.c --- mlt-0.9.0/src/modules/core/filter_crop.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_crop.c 2014-06-29 20:23:17.000000000 +0000 @@ -87,7 +87,7 @@ int bpp; // Subsampled YUV is messy and less precise. - if ( *format == mlt_image_yuv422 && frame->convert_image ) + if ( *format == mlt_image_yuv422 && frame->convert_image && ( left & 1 ) ) { mlt_image_format requested_format = mlt_image_rgb24; frame->convert_image( frame, image, format, requested_format ); diff -Nru mlt-0.9.0/src/modules/core/filter_data_feed.c mlt-0.9.2/src/modules/core/filter_data_feed.c --- mlt-0.9.0/src/modules/core/filter_data_feed.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_data_feed.c 2014-06-29 20:23:17.000000000 +0000 @@ -51,10 +51,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Get the filter properties - mlt_properties filter_properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); @@ -66,8 +66,8 @@ char *type = mlt_properties_get( filter_properties, "type" ); // Get the in and out points of this filter - int in = mlt_filter_get_in( this ); - int out = mlt_filter_get_out( this ); + int in = mlt_filter_get_in( filter ); + int out = mlt_filter_get_out( filter ); // Create the data queue if it doesn't exist if ( data_queue == NULL ) @@ -157,21 +157,21 @@ mlt_filter filter_data_feed_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the filter - mlt_filter this = mlt_filter_new( ); + mlt_filter filter = mlt_filter_new( ); // Initialise it - if ( this != NULL ) + if ( filter != NULL ) { // Get the properties - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the argument (default to titles) mlt_properties_set( properties, "type", arg == NULL ? "titles" : arg ); // Specify the processing method - this->process = filter_process; + filter->process = filter_process; } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_data_show.c mlt-0.9.2/src/modules/core/filter_data_show.c --- mlt-0.9.0/src/modules/core/filter_data_show.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_data_show.c 2014-06-29 20:23:17.000000000 +0000 @@ -322,10 +322,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the filter - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); // Register the get image method mlt_frame_push_get_image( frame, filter_get_image ); @@ -340,21 +340,21 @@ mlt_filter filter_data_show_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) { // Create the filter - mlt_filter this = mlt_filter_new( ); + mlt_filter filter = mlt_filter_new( ); // Initialise it - if ( this != NULL ) + if ( filter != NULL ) { // Get the properties - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the argument (default to titles) mlt_properties_set( properties, "resource", arg == NULL ? NULL : arg ); // Specify the processing method - this->process = filter_process; + filter->process = filter_process; } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_gamma.c mlt-0.9.2/src/modules/core/filter_gamma.c --- mlt-0.9.0/src/modules/core/filter_gamma.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_gamma.c 2014-06-29 20:23:17.000000000 +0000 @@ -28,15 +28,20 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 ) { // Get the gamma value - double gamma = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "gamma" ); + double gamma = mlt_properties_anim_get_double( properties, "gamma", position, length ); if ( gamma != 1.0 ) { @@ -65,12 +70,11 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - double gamma = mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "gamma" ); - gamma = gamma <= 0 ? 1 : gamma; - mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "gamma", gamma ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); + return frame; } @@ -79,11 +83,12 @@ mlt_filter filter_gamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "gamma", arg == NULL ? "1" : arg ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gamma", arg == NULL ? "1" : arg ); } - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_greyscale.c mlt-0.9.2/src/modules/core/filter_greyscale.c --- mlt-0.9.0/src/modules/core/filter_greyscale.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_greyscale.c 2014-06-29 20:23:17.000000000 +0000 @@ -27,10 +27,10 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 ) { uint8_t *p = *image; @@ -44,7 +44,7 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -55,9 +55,9 @@ mlt_filter filter_greyscale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - this->process = filter_process; - return this; + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + filter->process = filter_process; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_imageconvert.c mlt-0.9.2/src/modules/core/filter_imageconvert.c --- mlt-0.9.0/src/modules/core/filter_imageconvert.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_imageconvert.c 2014-06-29 20:23:17.000000000 +0000 @@ -37,15 +37,6 @@ u = u > 240 ? 240 : u;\ v = v > 240 ? 240 : v -/** This macro scales YUV up into the full gamut of the RGB color space. */ -#define YUV2RGB_601_SCALED( y, u, v, r, g, b ) \ - r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \ - g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 401 * ( u - 128 ) ) >> 10 ); \ - b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \ - r = r < 0 ? 0 : r > 255 ? 255 : r; \ - g = g < 0 ? 0 : g > 255 ? 255 : g; \ - b = b < 0 ? 0 : b > 255 ? 255 : b; - /** This macro converts a YUV value to the RGB color space. */ #define YUV2RGB_601_UNSCALED( y, u, v, r, g, b ) \ r = ((1024 * y + 1404 * ( v - 128 ) ) >> 10 ); \ @@ -370,7 +361,7 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { if ( !frame->convert_image ) frame->convert_image = convert_image; @@ -382,10 +373,11 @@ mlt_filter filter_imageconvert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) ); - if ( mlt_filter_init( this, this ) == 0 ) + mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); + if ( mlt_filter_init( filter, filter ) == 0 ) { - this->process = filter_process; + filter->process = filter_process; } - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_luma.c mlt-0.9.2/src/modules/core/filter_luma.c --- mlt-0.9.0/src/modules/core/filter_luma.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_luma.c 2014-06-29 20:23:17.000000000 +0000 @@ -32,10 +32,10 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); @@ -46,7 +46,7 @@ int out = mlt_properties_get_int( properties, "period" ); int cycle = mlt_properties_get_int( properties, "cycle" ); int duration = mlt_properties_get_int( properties, "duration" ); - mlt_position position = mlt_filter_get_position( filter, this ); + mlt_position position = mlt_filter_get_position( filter, frame ); out = out? out + 1 : 25; if ( cycle ) @@ -84,19 +84,19 @@ { mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma ); mlt_properties_pass( luma_properties, properties, "luma." ); - int in = position / out * out + mlt_frame_get_position( this ) - position; + int in = position / out * out + mlt_frame_get_position( frame ) - position; mlt_properties_set_int( luma_properties, "in", in ); mlt_properties_set_int( luma_properties, "out", in + duration - 1 ); - mlt_transition_process( luma, this, b_frame ); + mlt_transition_process( luma, frame, b_frame ); } - error = mlt_frame_get_image( this, image, format, width, height, 1 ); + error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // We only need a copy of the last frame in the cycle, but we could miss it // with realtime frame-dropping, so we copy the last several frames of the cycle. if ( error == 0 && modulo_pos > out - duration ) { - mlt_properties a_props = MLT_FRAME_PROPERTIES( this ); + mlt_properties a_props = MLT_FRAME_PROPERTIES( frame ); int size = 0; uint8_t *src = mlt_properties_get_data( a_props, "image", &size ); uint8_t *dst = mlt_pool_alloc( size ); @@ -121,10 +121,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the filter on to the stack - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); // Push the get_image on to the stack mlt_frame_push_get_image( frame, filter_get_image ); @@ -137,13 +137,14 @@ mlt_filter filter_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); - this->process = filter_process; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + filter->process = filter_process; if ( arg != NULL ) mlt_properties_set( properties, "resource", arg ); } - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_mirror.c mlt-0.9.2/src/modules/core/filter_mirror.c --- mlt-0.9.0/src/modules/core/filter_mirror.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_mirror.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * filter_mirror.c -- mirror filter - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -31,10 +31,10 @@ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Pop the mirror filter from the stack - mlt_filter this = mlt_frame_pop_service( frame ); + mlt_filter filter = mlt_frame_pop_service( frame ); // Get the mirror type - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Get the properties char *mirror = mlt_properties_get( properties, "mirror" ); @@ -298,10 +298,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the service on to the stack - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); // Push the filter method on to the stack mlt_frame_push_service( frame, filter_get_image ); @@ -315,22 +315,22 @@ mlt_filter filter_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Construct a new filter - mlt_filter this = mlt_filter_new( ); + mlt_filter filter = mlt_filter_new( ); // If we have a filter, initialise it - if ( this != NULL ) + if ( filter != NULL ) { // Get the properties - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Set the default mirror type mlt_properties_set_or_default( properties, "mirror", arg, "horizontal" ); // Assign the process method - this->process = filter_process; + filter->process = filter_process; } // Return the filter - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_mono.c mlt-0.9.2/src/modules/core/filter_mono.c --- mlt-0.9.0/src/modules/core/filter_mono.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_mono.c 2014-06-29 20:23:17.000000000 +0000 @@ -144,9 +144,9 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); // Propogate the parameters @@ -163,14 +163,15 @@ mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; + filter->process = filter_process; if ( arg != NULL ) - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", atoi( arg ) ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channels", atoi( arg ) ); else - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", -1 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channels", -1 ); } - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_panner.c mlt-0.9.2/src/modules/core/filter_panner.c --- mlt-0.9.0/src/modules/core/filter_panner.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_panner.c 2014-06-29 20:23:17.000000000 +0000 @@ -184,7 +184,7 @@ v = 0; for ( in = 0; in < *channels && in < 6; in++ ) v += factors[in][out] * src[ i * *channels + in ]; -// dest[ i * *channels + out ] = (int16_t) ( v < -32767 ? -32767 : v > 32768 ? 32768 : v ); + v = v < -32767 ? -32767 : v > 32768 ? 32768 : v; vp[out] = dest[ i * *channels + out ] = (int16_t) ( v * A + vp[ out ] * B ); } @@ -228,7 +228,16 @@ // Use constant mix level if only start else if ( mlt_properties_get( properties, "start" ) != NULL ) { - mix = mlt_properties_get_double( properties, "start" ); + mix = mlt_properties_get_double( properties, "start" ); + } + + // Use animated property "split" to get mix level if property is set + char* split_property = mlt_properties_get( properties, "split" ); + if ( split_property ) + { + mlt_position pos = mlt_filter_get_position( filter, frame ); + mlt_position len = mlt_filter_get_length2( filter, frame ); + mix = mlt_properties_anim_get_double( properties, "split", pos, len ); } // Convert it from [0, 1] to [-1, 1] @@ -299,6 +308,7 @@ if ( arg != NULL ) mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "start", atof( arg ) ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channel", -1 ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "split", NULL ); } return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_panner.yml mlt-0.9.2/src/modules/core/filter_panner.yml --- mlt-0.9.0/src/modules/core/filter_panner.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_panner.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,74 @@ +schema_version: 0.2 +type: filter +identifier: panner +title: Audio Pan +version: 2 +copyright: Ushodaya Enterprises Limited +creator: Dan Dennedy +license: LGPLv2.1 +language: en +tags: + - Audio +description: Pan an audio channel, adjust balance, or adjust fade. +notes: Only handles up to 6 channels. Needs more work balance for surround and other channel layouts. +parameters: + - identifier: start + title: Start + description: > + The position of the audio relative to its neighbor channel. For example, + when channel is set to 0 for left, then start 0 is full left, 0.5 is + center, and 1.0 is full right. + + If value for property "split" is set value of this property is discarded. + type: float + argument: true + mutable: yes + minimum: 0 + maximum: 1 + + - identifier: end + title: End + description: > + The ending value of the audio position. It will be interpolated from + start to end over the in-out range. + + If value for property "split" is set value of this property is discarded. + type: float + mutable: yes + minimum: 0 + maximum: 1 + + - identifier: channel + title: Channel + type: integer + description: > + For stereo: 0 for front left, 1 for front right, -1 for front balance. + For quad: 2 for back left, 3 for back right, -2 for rear balance, + -3 for left fade, -4 for right fade. + minimum: -4 + maximum: 5 + default: -1 + + - identifier: gang + title: Gang + description: > + Whether to gang together the front and back when using balance or to + gang together the left and right when using fade. + type: integer + minimum: 0 + maximum: 1 + default: 0 + widget: checkbox + + - identifier: split + title: Split + description: > + The animated position of the audio relative to its neighbor channel. For example, + when channel is set to 0 for left, then start 0 is full left, 0.5 is + center, and 1.0 is full right. + + If this value is set, values for properties "start" and "end" are discarded. + type: float + mutable: yes + minimum: 0 + maximum: 1 diff -Nru mlt-0.9.0/src/modules/core/filter_region.c mlt-0.9.2/src/modules/core/filter_region.c --- mlt-0.9.0/src/modules/core/filter_region.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_region.c 2014-06-29 20:23:17.000000000 +0000 @@ -71,9 +71,9 @@ return mlt_frame_get_image( frame, image, format, width, height, writable ); } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -85,16 +85,16 @@ mlt_filter filter_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new filter - mlt_filter this = mlt_filter_new( ); + mlt_filter filter = mlt_filter_new( ); // Further initialisation - if ( this != NULL ) + if ( filter != NULL ) { // Get the properties from the filter - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the filter process method - this->process = filter_process; + filter->process = filter_process; // Resource defines the shape of the region mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg ); @@ -104,5 +104,6 @@ } // Return the filter - return this; + return filter; } + diff -Nru mlt-0.9.0/src/modules/core/filter_transition.c mlt-0.9.2/src/modules/core/filter_transition.c --- mlt-0.9.0/src/modules/core/filter_transition.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_transition.c 2014-06-29 20:23:17.000000000 +0000 @@ -27,41 +27,41 @@ NB: Not all transitions will accept a and b frames being the same... */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - mlt_transition transition = mlt_frame_pop_service( this ); - if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "image_count" ) >= 1 ) - mlt_transition_process( transition, this, this ); - return mlt_frame_get_image( this, image, format, width, height, writable ); + mlt_transition transition = mlt_frame_pop_service( frame ); + if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "image_count" ) >= 1 ) + mlt_transition_process( transition, frame, frame ); + return mlt_frame_get_image( frame, image, format, width, height, writable ); } /** Get the audio via the transition. NB: Not all transitions will accept a and b frames being the same... */ -static int filter_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Obtain the transition instance - mlt_transition transition = mlt_frame_pop_audio( this ); - mlt_transition_process( transition, this, this ); - return mlt_frame_get_audio( this, buffer, format, frequency, channels, samples ); + mlt_transition transition = mlt_frame_pop_audio( frame ); + mlt_transition_process( transition, frame, frame ); + return mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); } /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Obtain the transition instance - mlt_transition transition = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "instance", NULL ); + mlt_transition transition = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "instance", NULL ); // If we haven't created the instance, do it now if ( transition == NULL ) { - char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "transition" ); - mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) ); + char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "transition" ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); transition = mlt_factory_transition( profile, name, NULL ); - mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "instance", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); + mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "instance", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); } // We may still not have a transition... @@ -71,11 +71,11 @@ int type = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type" ); // Set the basic info - mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "in" ) ); - mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "out" ) ); + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "in" ) ); + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "out" ) ); // Refresh with current user values - mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), MLT_FILTER_PROPERTIES( this ), "transition." ); + mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), MLT_FILTER_PROPERTIES( filter ), "transition." ); if ( type & 1 && !mlt_frame_is_test_card( frame ) && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "hide" ) & 1 ) ) { @@ -93,7 +93,7 @@ } else { - mlt_properties_debug( MLT_FILTER_PROPERTIES( this ), "no transition", stderr ); + mlt_properties_debug( MLT_FILTER_PROPERTIES( filter ), "no transition", stderr ); } return frame; @@ -104,12 +104,12 @@ mlt_filter filter_transition_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "transition", arg ); - this->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "transition", arg ); + filter->process = filter_process; } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/filter_transition.yml mlt-0.9.2/src/modules/core/filter_transition.yml --- mlt-0.9.0/src/modules/core/filter_transition.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_transition.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -schema_version: 0.1 +schema_version: 0.2 type: filter identifier: transition title: Transition as Filter @@ -8,4 +8,22 @@ license: LGPLv2.1 language: en tags: + - Audio - Video +description: > + Use a transition as a filter. The filters supplies the same frame as + both the A and B clip to the transition. + +parameters: + - identifier: transition + argument: yes + type: string + title: Transition + description: The MLT name of a transition. + required: yes + + - identifier: transition.* + type: properties + description: > + Properties may be set on the encapsulated composite transition. + e.g.: transition.valign=c diff -Nru mlt-0.9.0/src/modules/core/filter_watermark.c mlt-0.9.2/src/modules/core/filter_watermark.c --- mlt-0.9.0/src/modules/core/filter_watermark.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/filter_watermark.c 2014-06-29 20:23:17.000000000 +0000 @@ -37,12 +37,12 @@ int error = 0; // Get the watermark filter object - mlt_filter this = mlt_frame_pop_service( frame ); + mlt_filter filter = mlt_frame_pop_service( frame ); // Get the properties of the filter - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_service_lock( MLT_FILTER_SERVICE( this ) ); + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Get the producer from the filter mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL ); @@ -60,7 +60,7 @@ if ( composite == NULL ) { // Create composite via the factory - mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); composite = mlt_factory_transition( profile, "composite", NULL ); // Register the composite for reuse/destruction @@ -91,7 +91,7 @@ char *factory = mlt_properties_get( properties, "factory" ); // Create the producer - mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); producer = mlt_factory_producer( profile, factory, resource ); // If we have one @@ -117,7 +117,7 @@ mlt_properties_pass( producer_properties, properties, "producer." ); } - mlt_service_unlock( MLT_FILTER_SERVICE( this ) ); + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Only continue if we have both producer and composite if ( composite != NULL && producer != NULL ) @@ -129,7 +129,7 @@ mlt_frame b_frame = NULL; // Get the original producer position - mlt_position position = mlt_filter_get_position( this, frame ); + mlt_position position = mlt_filter_get_position( filter, frame ); // Make sure the producer is in the correct position mlt_producer_seek( producer, position ); @@ -166,7 +166,7 @@ if ( mlt_properties_get_int( properties, "reverse" ) == 0 ) { // Apply all filters that are attached to this filter to the b frame - mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 ); + mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 ); // Process the frame mlt_transition_process( composite, frame, b_frame ); @@ -187,7 +187,7 @@ mlt_properties_set_int( b_props, "consumer_deinterlace", 1 ); mlt_properties_set( a_props, "rescale.interp", rescale ); mlt_properties_set( b_props, "rescale.interp", rescale ); - mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 ); + mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 ); error = mlt_frame_get_image( b_frame, image, format, width, height, 1 ); alpha = mlt_frame_get_alpha_mask( b_frame ); mlt_frame_set_image( frame, *image, *width * *height * 2, NULL ); @@ -218,16 +218,16 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Get the properties of the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Assign the frame out point to the filter (just in case we need it later) - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_out", mlt_properties_get_int( properties, "out" ) ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_out", mlt_properties_get_int( properties, "out" ) ); // Push the filter on to the stack - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); // Push the get_image on to the stack mlt_frame_push_get_image( frame, filter_get_image ); @@ -240,17 +240,17 @@ mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); - this->process = filter_process; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + filter->process = filter_process; mlt_properties_set( properties, "factory", mlt_environment( "MLT_PRODUCER" ) ); if ( arg != NULL ) mlt_properties_set( properties, "resource", arg ); // Ensure that attached filters are handled privately mlt_properties_set_int( properties, "_filter_private", 1 ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/core/loader.dict mlt-0.9.2/src/modules/core/loader.dict --- mlt-0.9.0/src/modules/core/loader.dict 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/loader.dict 2014-06-29 20:23:17.000000000 +0000 @@ -41,7 +41,7 @@ *.tga=pixbuf,qimage *.tif=pixbuf,qimage *.tiff=pixbuf,qimage -*.txt=pango +*.txt=pango,qtext *.vob=mcmpeg,avformat *.wav=avformat *.wmv=avformat diff -Nru mlt-0.9.0/src/modules/core/Makefile mlt-0.9.2/src/modules/core/Makefile --- mlt-0.9.0/src/modules/core/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -13,7 +13,6 @@ producer_loader.o \ producer_melt.o \ producer_noise.o \ - producer_ppm.o \ filter_audiochannels.o \ filter_audioconvert.o \ filter_audiowave.o \ @@ -40,6 +39,7 @@ transition_luma.o \ transition_mix.o \ transition_region.o \ + transition_matte.o \ consumer_multi.o \ consumer_null.o diff -Nru mlt-0.9.0/src/modules/core/producer_colour.c mlt-0.9.2/src/modules/core/producer_colour.c --- mlt-0.9.0/src/modules/core/producer_colour.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_colour.c 2014-06-29 20:23:17.000000000 +0000 @@ -27,11 +27,6 @@ #include -typedef struct -{ - uint8_t r, g, b, a; -} rgba_color; - static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); static void producer_close( mlt_producer parent ); @@ -58,45 +53,6 @@ return NULL; } -rgba_color parse_color( char *color, unsigned int color_int ) -{ - rgba_color result = { 0xff, 0xff, 0xff, 0xff }; - - if ( !strcmp( color, "red" ) ) - { - result.r = 0xff; - result.g = 0x00; - result.b = 0x00; - } - else if ( !strcmp( color, "green" ) ) - { - result.r = 0x00; - result.g = 0xff; - result.b = 0x00; - } - else if ( !strcmp( color, "blue" ) ) - { - result.r = 0x00; - result.g = 0x00; - result.b = 0xff; - } - else if ( !strcmp( color, "black" ) ) - { - result.r = 0x00; - result.g = 0x00; - result.b = 0x00; - } - else if ( strcmp( color, "white" ) ) - { - result.r = ( color_int >> 24 ) & 0xff; - result.g = ( color_int >> 16 ) & 0xff; - result.b = ( color_int >> 8 ) & 0xff; - result.a = ( color_int ) & 0xff; - } - - return result; -} - static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { // Obtain properties of frame @@ -129,10 +85,10 @@ free( now ); now = mlt_properties_get( producer_props, "resource" ); } - rgba_color color = parse_color( now, mlt_properties_get_int( producer_props, "resource" ) ); + mlt_color color = mlt_properties_get_color( producer_props, "resource" ); // Choose suitable out values if nothing specific requested - if ( *format == mlt_image_none ) + if ( *format == mlt_image_none || *format == mlt_image_glsl ) *format = mlt_image_rgb24a; if ( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; @@ -140,7 +96,7 @@ *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; // See if we need to regenerate - if ( strcmp( now, then ) || *width != current_width || *height != current_height || *format != current_format ) + if ( !now || ( then && strcmp( now, then ) ) || *width != current_width || *height != current_height || *format != current_format ) { // Color the image int i = *width * *height + 1; @@ -195,7 +151,12 @@ *p ++ = color.b; } break; + case mlt_image_glsl: + case mlt_image_glsl_texture: + memset(p, 0, size); + break; default: + *format = mlt_image_rgb24a; while ( --i ) { *p ++ = color.r; diff -Nru mlt-0.9.0/src/modules/core/producer_consumer.c mlt-0.9.2/src/modules/core/producer_consumer.c --- mlt-0.9.0/src/modules/core/producer_consumer.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_consumer.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_consumer.c -- produce as a consumer of an encapsulated producer - * Copyright (C) 2008 Ushodaya Enterprises Limited + * Copyright (C) 2008-2014 Ushodaya Enterprises Limited * Author: Dan Dennedy * * This library is free software; you can redistribute it and/or @@ -26,7 +26,7 @@ #include struct context_s { - mlt_producer this; + mlt_producer self; mlt_producer producer; mlt_consumer consumer; mlt_profile profile; @@ -79,8 +79,8 @@ if ( mlt_frame_get_position( nested_frame ) != cx->audio_position ) { double fps = mlt_profile_fps( cx->profile ); - if ( mlt_producer_get_fps( cx->this ) < fps ) - fps = mlt_producer_get_fps( cx->this ); + if ( mlt_producer_get_fps( cx->self ) < fps ) + fps = mlt_producer_get_fps( cx->self ); *samples = mlt_sample_calculator( fps, *frequency, cx->audio_counter++ ); result = mlt_frame_get_audio( nested_frame, buffer, format, frequency, channels, samples ); int size = mlt_audio_format_size( *format, *samples, *channels ); @@ -101,9 +101,9 @@ return result; } -static int get_frame( mlt_producer this, mlt_frame_ptr frame, int index ) +static int get_frame( mlt_producer self, mlt_frame_ptr frame, int index ) { - mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); + mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data( properties, "context", NULL ); if ( !cx ) @@ -112,11 +112,11 @@ cx = mlt_pool_alloc( sizeof( struct context_s ) ); memset( cx, 0, sizeof( *cx ) ); mlt_properties_set_data( properties, "context", cx, 0, mlt_pool_release, NULL ); - cx->this = this; + cx->self = self; char *profile_name = mlt_properties_get( properties, "profile" ); if ( !profile_name ) profile_name = mlt_properties_get( properties, "mlt_profile" ); - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) ); + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ); if ( profile_name ) { @@ -159,17 +159,17 @@ } // Generate a frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) ); + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( self ) ); if ( *frame ) { // Seek the producer to the correct place // Calculate our positions - double actual_position = (double) mlt_producer_frame( this ); - if ( mlt_producer_get_speed( this ) != 0 ) - actual_position *= mlt_producer_get_speed( this ); + double actual_position = (double) mlt_producer_frame( self ); + if ( mlt_producer_get_speed( self ) != 0 ) + actual_position *= mlt_producer_get_speed( self ); mlt_position need_first = floor( actual_position ); mlt_producer_seek( cx->producer, - lrint( need_first * mlt_profile_fps( cx->profile ) / mlt_producer_get_fps( this ) ) ); + lrint( need_first * mlt_profile_fps( cx->profile ) / mlt_producer_get_fps( self ) ) ); // Get the nested frame mlt_frame nested_frame = mlt_consumer_rt_frame( cx->consumer ); @@ -183,7 +183,7 @@ mlt_frame_push_audio( *frame, get_audio ); // Give the returned frame temporal identity - mlt_frame_set_position( *frame, mlt_producer_position( this ) ); + mlt_frame_set_position( *frame, mlt_producer_position( self ) ); // Store the nested frame on the produced frame for destruction mlt_properties frame_props = MLT_FRAME_PROPERTIES( *frame ); @@ -199,14 +199,14 @@ } // Calculate the next timecode - mlt_producer_prepare_next( this ); + mlt_producer_prepare_next( self ); return 0; } -static void producer_close( mlt_producer this ) +static void producer_close( mlt_producer self ) { - context cx = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( this ), "context", NULL ); + context cx = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( self ), "context", NULL ); // Shut down all the encapsulated services if ( cx ) @@ -217,28 +217,28 @@ mlt_profile_close( cx->profile ); } - this->close = NULL; - mlt_producer_close( this ); - free( this ); + self->close = NULL; + mlt_producer_close( self ); + free( self ); } mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_producer this = mlt_producer_new( profile ); + mlt_producer self = mlt_producer_new( profile ); // Encapsulate the real producer mlt_profile temp_profile = mlt_profile_clone( profile ); temp_profile->is_explicit = 0; mlt_producer real_producer = mlt_factory_producer( temp_profile, NULL, arg ); - if ( this && real_producer ) + if ( self && real_producer ) { // Override some producer methods - this->close = ( mlt_destructor )producer_close; - this->get_frame = get_frame; + self->close = ( mlt_destructor )producer_close; + self->get_frame = get_frame; // Get the properties of this producer - mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); mlt_properties_set( properties, "resource", arg ); mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "out, length" ); @@ -247,13 +247,13 @@ } else { - if ( this ) - mlt_producer_close( this ); + if ( self ) + mlt_producer_close( self ); if ( real_producer ) mlt_producer_close( real_producer ); - this = NULL; + self = NULL; } mlt_profile_close( temp_profile ); - return this; + return self; } diff -Nru mlt-0.9.0/src/modules/core/producer_hold.c mlt-0.9.2/src/modules/core/producer_hold.c --- mlt-0.9.0/src/modules/core/producer_hold.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_hold.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_hold.c -- frame holding producer - * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -25,8 +25,8 @@ #include // Forward references -static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ); -static void producer_close( mlt_producer this ); +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); +static void producer_close( mlt_producer producer ); /** Constructor for the frame holding producer. Basically, all this producer does is provide a producer wrapper for the requested producer, allows the specifcation of @@ -37,16 +37,16 @@ mlt_producer producer_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Construct a new holding producer - mlt_producer this = mlt_producer_new( profile ); + mlt_producer self = mlt_producer_new( profile ); // Construct the requested producer via loader mlt_producer producer = mlt_factory_producer( profile, NULL, arg ); // Initialise the frame holding capabilities - if ( this != NULL && producer != NULL ) + if ( self != NULL && producer != NULL ) { // Get the properties of this producer - mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // Store the producer mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); @@ -60,23 +60,23 @@ mlt_properties_set( properties, "method", "onefield" ); // Override the get_frame method - this->get_frame = producer_get_frame; - this->close = ( mlt_destructor )producer_close; + self->get_frame = producer_get_frame; + self->close = ( mlt_destructor )producer_close; } else { // Clean up (not sure which one failed, can't be bothered to find out, so close both) - if ( this ) - mlt_producer_close( this ); + if ( self ) + mlt_producer_close( self ); if ( producer ) mlt_producer_close( producer ); // Make sure we return NULL - this = NULL; + self = NULL; } // Return this producer - return this; + return self; } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) @@ -135,16 +135,16 @@ return 0; } -static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ) +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Get the properties of this producer - mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Construct a new frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) ); + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // If we have a frame, then stack the producer itself and the get_image method - if ( *frame != NULL ) + if ( *frame ) { // Define the real frame mlt_frame real_frame = mlt_properties_get_data( properties, "real_frame", NULL ); @@ -187,15 +187,15 @@ } // Move to the next position - mlt_producer_prepare_next( this ); + mlt_producer_prepare_next( producer ); return 0; } -static void producer_close( mlt_producer this ) +static void producer_close( mlt_producer producer ) { - this->close = NULL; - mlt_producer_close( this ); - free( this ); + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); } diff -Nru mlt-0.9.0/src/modules/core/producer_hold.yml mlt-0.9.2/src/modules/core/producer_hold.yml --- mlt-0.9.0/src/modules/core/producer_hold.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_hold.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -schema_version: 0.1 +schema_version: 0.2 type: producer identifier: hold title: Still @@ -9,3 +9,19 @@ language: en tags: - Video +description: Repeat the same frame for the entirety of the clip. + +parameters: + - identifier: resource + argument: yes + type: string + title: File/URL + description: A file name specification, URL, or producer name:argument. + required: yes + + - identifier: frame + type: integer + title: Frame number + description: The frame number of the frame to repeat + default: 0 + minimum: 0 diff -Nru mlt-0.9.0/src/modules/core/producer_melt.c mlt-0.9.2/src/modules/core/producer_melt.c --- mlt-0.9.0/src/modules/core/producer_melt.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_melt.c 2014-06-29 20:23:17.000000000 +0000 @@ -24,34 +24,48 @@ #include +#define MELT_FILE_MAX_LINES (100000) +#define MELT_FILE_MAX_LENGTH (2048) + mlt_producer producer_melt_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv ); mlt_producer producer_melt_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *file ) { + // Convert file name string encoding. + mlt_properties properties = mlt_properties_new(); + mlt_properties_set( properties, "filename", file ); + mlt_properties_from_utf8( properties, "filename", "_filename" ); + file = mlt_properties_get( properties, "_filename" ); + FILE *input = fopen( file, "r" ); - char **args = calloc( sizeof( char * ), 1000 ); + char **args = calloc( sizeof( char * ), MELT_FILE_MAX_LINES ); int count = 0; - char temp[ 2048 ]; + char temp[ MELT_FILE_MAX_LENGTH ]; if ( input != NULL ) { - while( fgets( temp, 2048, input ) ) + while( fgets( temp, MELT_FILE_MAX_LENGTH, input ) && count < MELT_FILE_MAX_LINES ) { + if ( temp[ strlen( temp ) - 1 ] != '\n' ) + mlt_log_warning( NULL, "Exceeded maximum line length (%d) while reading a melt file.\n", MELT_FILE_MAX_LENGTH ); temp[ strlen( temp ) - 1 ] = '\0'; if ( strcmp( temp, "" ) ) args[ count ++ ] = strdup( temp ); } fclose( input ); + if ( count == MELT_FILE_MAX_LINES ) + mlt_log_warning( NULL, "Reached the maximum number of lines (%d) while reading a melt file.\n" + "Consider using MLT XML.\n", MELT_FILE_MAX_LINES ); } mlt_producer result = producer_melt_init( profile, type, id, args ); if ( result != NULL ) { - mlt_properties properties = MLT_PRODUCER_PROPERTIES( result ); - mlt_properties_set( properties, "resource", file ); + mlt_properties_set( MLT_PRODUCER_PROPERTIES( result ), "resource", file ); } + mlt_properties_close( properties ); while( count -- ) free( args[ count ] ); free( args ); diff -Nru mlt-0.9.0/src/modules/core/producer_melt_file.yml mlt-0.9.2/src/modules/core/producer_melt_file.yml --- mlt-0.9.0/src/modules/core/producer_melt_file.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_melt_file.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,4 +1,4 @@ -schema_version: 0.1 +schema_version: 0.2 type: producer identifier: melt_file title: Melt @@ -11,3 +11,11 @@ tags: - Audio - Video +notes: Limited to files with 100000 or less lines and maximum line size of 2048 bytes. + +parameters: + - identifier: resource + argument: yes + title: File + description: The name of the melt text file. + required: yes diff -Nru mlt-0.9.0/src/modules/core/producer_noise.c mlt-0.9.2/src/modules/core/producer_noise.c --- mlt-0.9.0/src/modules/core/producer_noise.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_noise.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_noise.c -- noise generating producer - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Charles Yates * * This library is free software; you can redistribute it and/or @@ -29,20 +29,31 @@ /** Random number generator */ -static unsigned int seed_x = 521288629; -static unsigned int seed_y = 362436069; +typedef struct +{ + unsigned int x; + unsigned int y; +} rand_seed; + +static void init_seed( rand_seed* seed, int init ) +{ + // Use the initial value to initialize the seed to arbitrary values. + // This causes the algorithm to produce consistent results each time for the same frame number. + seed->x = 521288629 + init - ( init << 16 ); + seed->y = 362436069 - init + ( init << 16 ); +} -static inline unsigned int fast_rand( ) +static inline unsigned int fast_rand( rand_seed* seed ) { - static unsigned int a = 18000, b = 30903; - seed_x = a * ( seed_x & 65535 ) + ( seed_x >> 16 ); - seed_y = b * ( seed_y & 65535 ) + ( seed_y >> 16 ); - return ( ( seed_x << 16 ) + ( seed_y & 65535 ) ); + static unsigned int a = 18000, b = 30903; + seed->x = a * ( seed->x & 65535 ) + ( seed->x >> 16 ); + seed->y = b * ( seed->y & 65535 ) + ( seed->y >> 16 ); + return ( ( seed->x << 16 ) + ( seed->y & 65535 ) ); } // Foward declarations -static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ); -static void producer_close( mlt_producer this ); +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); +static void producer_close( mlt_producer producer ); /** Initialise. */ @@ -50,17 +61,17 @@ mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new producer object - mlt_producer this = mlt_producer_new( profile ); + mlt_producer producer = mlt_producer_new( profile ); // Initialise the producer - if ( this != NULL ) + if ( producer != NULL ) { // Callback registration - this->get_frame = producer_get_frame; - this->close = ( mlt_destructor )producer_close; + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; } - return this; + return producer; } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) @@ -92,10 +103,14 @@ // Value to hold a random number uint32_t value; + // Initialize seed from the frame number. + rand_seed seed; + init_seed( &seed, mlt_frame_get_position( frame ) ); + // Generate random noise while ( p != *buffer ) { - value = fast_rand( ) & 0xff; + value = fast_rand( &seed ) & 0xff; *( -- p ) = 128; *( -- p ) = value < 16 ? 16 : value > 240 ? 240 : value; } @@ -124,8 +139,10 @@ if ( *buffer != NULL ) { int16_t *p = *buffer + size / 2; - while ( p != *buffer ) - *( -- p ) = fast_rand( ) & 0x0f00; + rand_seed seed; + init_seed( &seed, mlt_frame_get_position( frame ) ); + while ( p != *buffer ) + *( -- p ) = fast_rand( &seed ) & 0x0f00; } // Set the buffer for destruction @@ -134,10 +151,10 @@ return 0; } -static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ) +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Generate a frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) ); + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // Check that we created a frame and initialise it if ( *frame != NULL ) @@ -146,14 +163,14 @@ mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); // Aspect ratio is whatever it needs to be - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) ); + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); // Set producer-specific frame properties mlt_properties_set_int( properties, "progressive", 1 ); // Update timecode on the frame we're creating - mlt_frame_set_position( *frame, mlt_producer_position( this ) ); + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Push the get_image method mlt_frame_push_get_image( *frame, producer_get_image ); @@ -163,15 +180,15 @@ } // Calculate the next timecode - mlt_producer_prepare_next( this ); + mlt_producer_prepare_next( producer ); return 0; } -static void producer_close( mlt_producer this ) +static void producer_close( mlt_producer producer ) { - this->close = NULL; - mlt_producer_close( this ); - free( this ); + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); } diff -Nru mlt-0.9.0/src/modules/core/producer_ppm.c mlt-0.9.2/src/modules/core/producer_ppm.c --- mlt-0.9.0/src/modules/core/producer_ppm.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/producer_ppm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -/* - * producer_ppm.c -- simple ppm test case - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited - * Author: Charles Yates - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - -#include -#include - -typedef struct producer_ppm_s *producer_ppm; - -struct producer_ppm_s -{ - struct mlt_producer_s parent; - char *command; - FILE *video; - FILE *audio; - uint64_t expected; -}; - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); -static void producer_close( mlt_producer parent ); - -mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *command ) -{ - producer_ppm this = calloc( 1, sizeof( struct producer_ppm_s ) ); - if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) - { - mlt_producer producer = &this->parent; - mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); - - producer->get_frame = producer_get_frame; - producer->close = ( mlt_destructor )producer_close; - - if ( command != NULL ) - { - mlt_properties_set( properties, "resource", command ); - this->command = strdup( command ); - } - else - { - mlt_properties_set( properties, "resource", "ppm test" ); - } - - return producer; - } - free( this ); - return NULL; -} - -static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) -{ - // Get the frames properties - mlt_properties properties = MLT_FRAME_PROPERTIES( this ); - - if ( mlt_properties_get_int( properties, "has_image" ) ) - { - // Get the RGB image - *buffer = mlt_properties_get_data( properties, "image", NULL ); - *width = mlt_properties_get_int( properties, "width" ); - *height = mlt_properties_get_int( properties, "height" ); - *format = mlt_image_rgb24; - } - else - { - mlt_frame_get_image( this, buffer, format, width, height, writable ); - } - - return 0; -} - -FILE *producer_ppm_run_video( producer_ppm this ) -{ - if ( this->video == NULL ) - { - if ( this->command == NULL ) - { - this->video = popen( "image2raw -k -r 25 -ppm /usr/share/pixmaps/*.png", "r" ); - } - else - { - char command[ 1024 ]; - float fps = mlt_producer_get_fps( &this->parent ); - float position = mlt_producer_position( &this->parent ); - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f image2pipe -r %f -vcodec ppm - 2>/dev/null", this->command, position, fps ); - this->video = popen( command, "r" ); - } - } - return this->video; -} - -FILE *producer_ppm_run_audio( producer_ppm this ) -{ - if ( this->audio == NULL ) - { - if ( this->command != NULL ) - { - char command[ 1024 ]; - float position = mlt_producer_position( &this->parent ); - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", this->command, position ); - this->audio = popen( command, "r" ); - } - } - return this->audio; -} - -static void producer_ppm_position( producer_ppm this, uint64_t requested ) -{ - if ( requested != this->expected ) - { - if ( this->video != NULL ) - pclose( this->video ); - this->video = NULL; - if ( this->audio != NULL ) - pclose( this->audio ); - this->audio = NULL; - } - - // This is the next frame we expect - this->expected = mlt_producer_frame( &this->parent ) + 1; - - // Open the pipe - this->video = producer_ppm_run_video( this ); - - // Open the audio pipe - this->audio = producer_ppm_run_audio( this ); - -} - -static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) -{ - // Get the frames properties - mlt_properties properties = MLT_FRAME_PROPERTIES( this ); - - FILE *pipe = mlt_properties_get_data( properties, "audio.pipe", NULL ); - - *frequency = 48000; - *channels = 2; - *samples = 1920; - - // Size - int size = *samples * *channels * 2; - - // Allocate an image - *buffer = malloc( size ); - - // Read it - if ( pipe != NULL ) - size = fread( *buffer, size, 1, pipe ); - else - memset( *buffer, 0, size ); - - // Pass the data on the frame properties - mlt_frame_set_audio( this, *buffer, *format, size, free ); - - return 0; -} - -static int read_ppm_header( FILE *video, int *width, int *height ) -{ - int count = 0; - { - char temp[ 132 ]; - char *ignore = fgets( temp, 132, video ); - ignore = fgets( temp, 132, video ); - count += sscanf( temp, "%d %d", width, height ); - ignore = fgets( temp, 132, video ); - } - return count; -} - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) -{ - producer_ppm this = producer->child; - int width; - int height; - - // Construct a test frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - - // Are we at the position expected? - producer_ppm_position( this, mlt_producer_frame( producer ) ); - - // Get the frames properties - mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); - - FILE *video = this->video; - FILE *audio = this->audio; - - // Read the video - if ( video != NULL && read_ppm_header( video, &width, &height ) == 2 ) - { - // Allocate an image - uint8_t *image = mlt_pool_alloc( width * ( height + 1 ) * 3 ); - - // Read it - size_t ignore; - ignore = fread( image, width * height * 3, 1, video ); - - // Pass the data on the frame properties - mlt_frame_set_image( *frame, image, width * ( height + 1 ) * 3, mlt_pool_release ); - mlt_properties_set_int( properties, "width", width ); - mlt_properties_set_int( properties, "height", height ); - mlt_properties_set_int( properties, "has_image", 1 ); - mlt_properties_set_int( properties, "progressive", 1 ); - mlt_properties_set_double( properties, "aspect_ratio", 1 ); - - // Push the image callback - mlt_frame_push_get_image( *frame, producer_get_image ); - } - else - { - // Push the image callback - mlt_frame_push_get_image( *frame, producer_get_image ); - } - - // Set the audio pipe - mlt_properties_set_data( properties, "audio.pipe", audio, 0, NULL, NULL ); - - // Hmm - register audio callback - mlt_frame_push_audio( *frame, producer_get_audio ); - - // Update timecode on the frame we're creating - mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - - // Calculate the next timecode - mlt_producer_prepare_next( producer ); - - return 0; -} - -static void producer_close( mlt_producer parent ) -{ - producer_ppm this = parent->child; - if ( this->video ) - pclose( this->video ); - if ( this->audio ) - pclose( this->audio ); - free( this->command ); - parent->close = NULL; - mlt_producer_close( parent ); - free( this ); -} diff -Nru mlt-0.9.0/src/modules/core/transition_composite.c mlt-0.9.2/src/modules/core/transition_composite.c --- mlt-0.9.0/src/modules/core/transition_composite.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/transition_composite.c 2014-06-29 20:23:17.000000000 +0000 @@ -673,8 +673,12 @@ // See if it is a PGM if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 ) { + // Convert file name string encoding. + mlt_properties_set( properties, "_luma_utf8", resource ); + mlt_properties_from_utf8( properties, "_luma_utf8", "_luma_local8" ); + // Open PGM - FILE *f = fopen( resource, "r" ); + FILE *f = fopen( mlt_properties_get( properties, "_luma_local8" ), "rb" ); if ( f != NULL ) { // Load from PGM diff -Nru mlt-0.9.0/src/modules/core/transition_luma.c mlt-0.9.2/src/modules/core/transition_luma.c --- mlt-0.9.0/src/modules/core/transition_luma.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/transition_luma.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * transition_luma.c -- a generic dissolve/wipe processor - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Dan Dennedy * * Adapted from Kino Plugin Timfx, which is @@ -30,7 +30,7 @@ #include #include "transition_composite.h" -static inline int dissolve_yuv( mlt_frame this, mlt_frame that, float weight, int width, int height ) +static inline int dissolve_yuv( mlt_frame frame, mlt_frame that, float weight, int width, int height ) { int ret = 0; int i = height + 1; @@ -41,10 +41,10 @@ uint8_t *alpha_dst; int mix = weight * ( 1 << 16 ); - if ( mlt_properties_get( &this->parent, "distort" ) ) - mlt_properties_set( &that->parent, "distort", mlt_properties_get( &this->parent, "distort" ) ); - mlt_frame_get_image( this, &p_dest, &format, &width, &height, 1 ); - alpha_dst = mlt_frame_get_alpha_mask( this ); + if ( mlt_properties_get( &frame->parent, "distort" ) ) + mlt_properties_set( &that->parent, "distort", mlt_properties_get( &frame->parent, "distort" ) ); + mlt_frame_get_image( frame, &p_dest, &format, &width, &height, 1 ); + alpha_dst = mlt_frame_get_alpha_mask( frame ); mlt_frame_get_image( that, &p_src, &format, &width_src, &height_src, 0 ); alpha_src = mlt_frame_get_alpha_mask( that ); @@ -366,8 +366,12 @@ // See if it is a PGM if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 ) { + // Convert file name string encoding. + mlt_properties_set( properties, "_resource_utf8", resource ); + mlt_properties_from_utf8( properties, "_resource_utf8", "_resource_local8" ); + // Open PGM - FILE *f = fopen( resource, "r" ); + FILE *f = fopen( mlt_properties_get( properties, "_resource_local8" ), "rb" ); if ( f != NULL ) { // Load from PGM diff -Nru mlt-0.9.0/src/modules/core/transition_matte.c mlt-0.9.2/src/modules/core/transition_matte.c --- mlt-0.9.0/src/modules/core/transition_matte.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/core/transition_matte.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,151 @@ +/* + * transition_matte.c -- replace alpha channel of track + * + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited + * Author: Maksym Veremeyenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include +#include +#include +#include +#include + +typedef void ( *copy_luma_fn )(uint8_t* alpha_a, int stride_a, uint8_t* image_b, int stride_b, int width, int height); + +static void copy_Y_to_A_full_luma(uint8_t* alpha_a, int stride_a, uint8_t* image_b, int stride_b, int width, int height) +{ + int i, j; + + for(j = 0; j < height; j++) + { + for(i = 0; i < width; i++) + alpha_a[i] = image_b[2*i]; + alpha_a += stride_a; + image_b += stride_b; + }; +}; + +static void copy_Y_to_A_scaled_luma(uint8_t* alpha_a, int stride_a, uint8_t* image_b, int stride_b, int width, int height) +{ + int i, j; + + for(j = 0; j < height; j++) + { + for(i = 0; i < width; i++) + { + unsigned int p = image_b[2*i]; + + if(p < 16) + p = 16; + if(p > 235) + p = 235; + p = (p - 16) * 255 / 219; + + alpha_a[i] = p; + }; + + alpha_a += stride_a; + image_b += stride_b; + }; +}; + +/** Get the image. +*/ + +static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + // Get the b frame from the stack + mlt_frame b_frame = mlt_frame_pop_frame( a_frame ); + + mlt_frame_get_image( a_frame, image, format, width, height, 1 ); + + // Get the properties of the a frame + mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); + + // Get the properties of the b frame + mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); + + int + width_a = mlt_properties_get_int( a_props, "width" ), + width_b = mlt_properties_get_int( b_props, "width" ), + height_a = mlt_properties_get_int( a_props, "height" ), + height_b = mlt_properties_get_int( b_props, "height" ); + + uint8_t *alpha_a, *image_b; + + copy_luma_fn copy_luma = mlt_properties_get_int(b_props, "full_luma")? + copy_Y_to_A_full_luma:copy_Y_to_A_scaled_luma; + + // This transition is yuv422 only + *format = mlt_image_yuv422; + + // Get the image from the a frame + mlt_frame_get_image( b_frame, &image_b, format, &width_b, &height_b, 1 ); + alpha_a = mlt_frame_get_alpha_mask( a_frame ); + + // copy data + copy_luma + ( + alpha_a, width_a, image_b, width_b * 2, + (width_a > width_b)?width_b:width_a, + (height_a > height_b)?height_b:height_a + ); + + // Extract the a_frame image info + *width = mlt_properties_get_int( a_props, "width" ); + *height = mlt_properties_get_int( a_props, "height" ); + *image = mlt_properties_get_data( a_props, "image", NULL ); + + return 0; +} + + +/** Matte transition processing. +*/ + +static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + // Push the b_frame on to the stack + mlt_frame_push_frame( a_frame, b_frame ); + + // Push the transition method + mlt_frame_push_get_image( a_frame, transition_get_image ); + + return a_frame; +} + +/** Constructor for the filter. +*/ + +mlt_transition transition_matte_init( mlt_profile profile, mlt_service_type type, const char *id, char *lumafile ) +{ + mlt_transition transition = mlt_transition_new( ); + if ( transition != NULL ) + { + // Set the methods + transition->process = transition_process; + + // Inform apps and framework that this is a video only transition + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); + + return transition; + } + return NULL; +} diff -Nru mlt-0.9.0/src/modules/core/transition_matte.yml mlt-0.9.2/src/modules/core/transition_matte.yml --- mlt-0.9.0/src/modules/core/transition_matte.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/core/transition_matte.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,50 @@ +schema_version: 0.1 +type: transition +identifier: matte +title: Matte +version: 1 +copyright: Ushodaya Enterprises Limited +creator: Maksym Veremeyenko +license: LGPLv2.1 +language: en +tags: + - Video +description: > + Replace the alpha channel of track A with the luma channel from track B. +notes: | + Please note that the transition automatically detects if the frame + from track B has scaled or non-scaled luma. The frame property + "full_luma" should indicate it. + + If you need to prepare fill and key (matte) files, you can use melt or + ffmpeg to extract alpha channel from existing video: + + melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ + avformat:sg_gm_2013_clip_title.matte_scaled.mp4 crf=10 preset=placebo an=1 + + ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ + yuv422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_scaled.mp4 + + Because the example above provides a scaled luma output, the transition + performs scaling from [16,235] -> [0, 255]. + It is possible to create unscaled (full) range: + + melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ + avformat:sg_gm_2013_clip_title.matte_full.mp4 crf=10 preset=placebo an=1 \ + mlt_image_format=rgb24a pix_fmt=yuvj422p + + ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ + yuvj422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_full.mp4 + + The fill can be converted from rgba to yuv422: + melt sg_gm_2013_clip_title.avi -consumer avformat:sg_gm_2013_clip_title.fill.mp4 \ + crf=10 preset=placebo an=1 + + ffmpeg -i sg_gm_2013_clip_title.avi -pix_fmt yuv422p -preset placebo -crf 10 -y \ + sg_gm_2013_clip_title.fill.mp4 + + Putting it all together: + + melt sg_gm_2013_clip_title.matte_full.mp4 -track noise: -track \ + sg_gm_2013_clip_title.fill.mp4 -transition matte a_track=2 \ + b_track=0 -transition composite a_track=1 b_track=2 diff -Nru mlt-0.9.0/src/modules/core/transition_mix.c mlt-0.9.2/src/modules/core/transition_mix.c --- mlt-0.9.0/src/modules/core/transition_mix.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/core/transition_mix.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * transition_mix.c -- mix two audio streams - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Dan Dennedy * * This library is free software; you can redistribute it and/or @@ -27,7 +27,7 @@ #include -static int mix_audio( mlt_frame this, mlt_frame that, float weight_start, float weight_end, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +static int mix_audio( mlt_frame frame, mlt_frame that, float weight_start, float weight_end, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { int ret = 0; int16_t *src, *dest; @@ -38,10 +38,10 @@ double d = 0, s = 0; mlt_frame_get_audio( that, (void**) &src, format, &frequency_src, &channels_src, &samples_src ); - mlt_frame_get_audio( this, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); + mlt_frame_get_audio( frame, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); - int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 ); + int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "silent_audio" ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "silent_audio", 0 ); if ( silent ) memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) ); @@ -97,7 +97,7 @@ // Replacement for broken mlt_frame_audio_mix - this filter uses an inline low pass filter // to allow mixing without volume hacking -static int combine_audio( mlt_frame this, mlt_frame that, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +static int combine_audio( mlt_frame frame, mlt_frame that, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { int ret = 0; int16_t *src, *dest; @@ -108,14 +108,14 @@ double vp[ 6 ]; double b_weight = 1.0; - if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "meta.mixdown" ) ) - b_weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "meta.volume" ); + if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.mixdown" ) ) + b_weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "meta.volume" ); mlt_frame_get_audio( that, (void**) &src, format, &frequency_src, &channels_src, &samples_src ); - mlt_frame_get_audio( this, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); + mlt_frame_get_audio( frame, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); - int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 ); + int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "silent_audio" ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "silent_audio", 0 ); if ( silent ) memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) ); @@ -204,9 +204,9 @@ /** Mix transition processing. */ -static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame ) +static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { - mlt_properties properties = MLT_TRANSITION_PROPERTIES( this ); + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); // Only if mix is specified, otherwise a producer may set the mix @@ -216,9 +216,9 @@ mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL ); mlt_position in = mlt_properties_get_int( props, "in" ); mlt_position out = mlt_properties_get_int( props, "out" ); - int length = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "length" ); + int length = mlt_properties_get_int( properties, "length" ); mlt_position time = mlt_properties_get_int( props, "_frame" ); - double mix = mlt_transition_get_progress( this, b_frame ); + double mix = mlt_transition_get_progress( transition, b_frame ); if ( mlt_properties_get_int( properties, "always_active" ) ) mix = ( double ) ( time - in ) / ( double ) ( out - in + 1 ); @@ -283,7 +283,7 @@ } // Override the get_audio method - mlt_frame_push_audio( a_frame, this ); + mlt_frame_push_audio( a_frame, transition ); mlt_frame_push_audio( a_frame, b_frame ); mlt_frame_push_audio( a_frame, transition_get_audio ); @@ -295,15 +295,15 @@ mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_transition this = calloc( 1, sizeof( struct mlt_transition_s ) ); - if ( this != NULL && mlt_transition_init( this, NULL ) == 0 ) + mlt_transition transition = calloc( 1, sizeof( struct mlt_transition_s ) ); + if ( transition != NULL && mlt_transition_init( transition, NULL ) == 0 ) { - this->process = transition_process; + transition->process = transition_process; if ( arg != NULL ) - mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( this ), "start", atof( arg ) ); + mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( transition ), "start", atof( arg ) ); // Inform apps and framework that this is an audio only transition - mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( this ), "_transition_type", 2 ); + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 2 ); } - return this; + return transition; } diff -Nru mlt-0.9.0/src/modules/decklink/common.cpp mlt-0.9.2/src/modules/decklink/common.cpp --- mlt-0.9.0/src/modules/decklink/common.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/decklink/common.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -19,6 +19,7 @@ #include "common.h" #include +#include #ifdef __DARWIN__ @@ -89,3 +90,46 @@ #endif + +void swab2( const void *from, void *to, int n ) +{ +#if defined(USE_SSE) +#define SWAB_STEP 16 + __asm__ volatile + ( + "loop_start: \n\t" + + /* load */ + "movdqa 0(%[from]), %%xmm0 \n\t" + "add $0x10, %[from] \n\t" + + /* duplicate to temp registers */ + "movdqa %%xmm0, %%xmm1 \n\t" + + /* shift right temp register */ + "psrlw $8, %%xmm1 \n\t" + + /* shift left main register */ + "psllw $8, %%xmm0 \n\t" + + /* compose them back */ + "por %%xmm0, %%xmm1 \n\t" + + /* save */ + "movdqa %%xmm1, 0(%[to]) \n\t" + "add $0x10, %[to] \n\t" + + "dec %[cnt] \n\t" + "jnz loop_start \n\t" + + : + : [from]"r"(from), [to]"r"(to), [cnt]"r"(n / SWAB_STEP) + //: "xmm0", "xmm1" + ); + + from = (unsigned char*) from + n - (n % SWAB_STEP); + to = (unsigned char*) to + n - (n % SWAB_STEP); + n = (n % SWAB_STEP); +#endif + swab((char*) from, (char*) to, n); +}; diff -Nru mlt-0.9.0/src/modules/decklink/common.h mlt-0.9.2/src/modules/decklink/common.h --- mlt-0.9.0/src/modules/decklink/common.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/decklink/common.h 2014-06-29 20:23:17.000000000 +0000 @@ -38,5 +38,6 @@ char* getCString( DLString aDLString ); void freeCString( char* aCString ); void freeDLString( DLString aDLString ); +void swab2( const void *from, void *to, int n ); #endif // DECKLINK_COMMON_H diff -Nru mlt-0.9.0/src/modules/decklink/consumer_decklink.cpp mlt-0.9.2/src/modules/decklink/consumer_decklink.cpp --- mlt-0.9.0/src/modules/decklink/consumer_decklink.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/decklink/consumer_decklink.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -100,7 +100,7 @@ m_decklinkFrame = NULL; } - ~DeckLinkConsumer() + virtual ~DeckLinkConsumer() { SAFE_RELEASE( m_displayMode ); SAFE_RELEASE( m_deckLinkKeyer ); @@ -246,7 +246,8 @@ } // Set the video output mode - if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault ) ) + if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), + (BMDVideoOutputFlags) (bmdVideoOutputFlagDefault | bmdVideoOutputRP188 | bmdVideoOutputVITC) ) ) { mlt_log_error( getConsumer(), "Failed to enable video output\n" ); return false; @@ -416,9 +417,9 @@ // Normal non-keyer playout - needs byte swapping if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst ) // convert lower field first to top field first - swab( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) ); + swab2( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) ); else - swab( (char*) image, (char*) buffer, stride * height ); + swab2( (char*) image, (char*) buffer, stride * height ); } else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) ) { @@ -454,7 +455,28 @@ } } if ( m_decklinkFrame ) + { + char* vitc; + + // set timecode + vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup" ); + if( vitc ) + { + int h, m, s, f; + if ( 4 == sscanf( vitc, "%d:%d:%d:%d", &h, &m, &s, &f ) ) + m_decklinkFrame->SetTimecodeFromComponents(bmdTimecodeVITC, + h, m, s, f, bmdTimecodeFlagDefault); + } + + // set userbits + vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" ); + if( vitc ) + m_decklinkFrame->SetTimecodeUserBits(bmdTimecodeVITC, + mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" )); + + m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale ); + } if ( !rendered ) mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped ); diff -Nru mlt-0.9.0/src/modules/decklink/producer_decklink.cpp mlt-0.9.2/src/modules/decklink/producer_decklink.cpp --- mlt-0.9.0/src/modules/decklink/producer_decklink.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/decklink/producer_decklink.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -43,6 +43,7 @@ int m_colorspace; int m_vancLines; mlt_cache m_cache; + bool m_reprio; BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines ) { @@ -92,7 +93,7 @@ m_decklinkInput = NULL; } - ~DeckLinkProducer() + virtual ~DeckLinkProducer() { if ( m_queue ) { @@ -357,6 +358,45 @@ IDeckLinkVideoInputFrame* video, IDeckLinkAudioInputPacket* audio ) { + mlt_frame frame = NULL; + + if( !m_reprio ) + { + mlt_properties properties = MLT_PRODUCER_PROPERTIES( getProducer() ); + + if ( mlt_properties_get( properties, "priority" ) ) + { + int r; + pthread_t thread; + pthread_attr_t tattr; + struct sched_param param; + + pthread_attr_init(&tattr); + pthread_attr_setschedpolicy(&tattr, SCHED_FIFO); + + if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) ) + param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; + else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) ) + param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1; + else + param.sched_priority = mlt_properties_get_int( properties, "priority" ); + + pthread_attr_setschedparam(&tattr, ¶m); + + thread = pthread_self(); + + r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); + if( r ) + mlt_log_verbose( getProducer(), + "VideoInputFrameArrived: pthread_setschedparam returned %d\n", r); + else + mlt_log_verbose( getProducer(), + "VideoInputFrameArrived: param.sched_priority=%d\n", param.sched_priority); + }; + + m_reprio = true; + }; + if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "preview" ) && mlt_producer_get_speed( getProducer() ) == 0.0 && !mlt_deque_count( m_queue )) { @@ -364,20 +404,39 @@ return S_OK; } - // Create mlt_frame - mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) ); // Copy video if ( video ) { + IDeckLinkTimecode* timecode = 0; + if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) ) { + int vitc_in = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vitc_in" ); + if ( vitc_in && ( S_OK == video->GetTimecode( bmdTimecodeRP188, &timecode ) || + S_OK == video->GetTimecode( bmdTimecodeVITC, &timecode )) && timecode ) + { + int vitc = timecode->GetBCD(); + SAFE_RELEASE( timecode ); + + mlt_log_verbose( getProducer(), + "VideoInputFrameArrived: vitc=%.8X vitc_in=%.8X\n", vitc, vitc_in); + + if ( vitc < vitc_in ) + { + pthread_cond_broadcast( &m_condition ); + return S_OK; + } + + mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vitc_in", 0 ); + } + int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines ); void* image = mlt_pool_alloc( size ); void* buffer = 0; unsigned char* p = (unsigned char*) image; int n = size / 2; -\ + // Initialize VANC lines to nominal black while ( --n ) { @@ -394,7 +453,7 @@ for ( int i = 1; i < m_vancLines + 1; i++ ) { if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK ) - swab( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() ); + swab2( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() ); else mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i ); } @@ -407,24 +466,22 @@ if ( image && buffer ) { size = video->GetRowBytes() * video->GetHeight(); - swab( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size ); + swab2( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size ); + frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) ); mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release ); } else if ( image ) { - mlt_log_verbose( getProducer(), "no video\n" ); + mlt_log_verbose( getProducer(), "no video image\n" ); mlt_pool_release( image ); } } else { mlt_log_verbose( getProducer(), "no signal\n" ); - mlt_frame_close( frame ); - frame = 0; } // Get timecode - IDeckLinkTimecode* timecode = 0; if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode ) { DLString timecodeString = 0; @@ -443,8 +500,6 @@ else { mlt_log_verbose( getProducer(), "no video\n" ); - mlt_frame_close( frame ); - frame = 0; } // Copy audio @@ -465,7 +520,7 @@ } else { - mlt_log_verbose( getProducer(), "no audio\n" ); + mlt_log_verbose( getProducer(), "no audio samples\n" ); mlt_pool_release( pcm ); } } @@ -488,7 +543,7 @@ { mlt_frame_close( frame ); mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped ); - mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped ); + mlt_log_warning( getProducer(), "buffer overrun, frame dropped %d\n", m_dropped ); } pthread_mutex_unlock( &m_mutex ); } diff -Nru mlt-0.9.0/src/modules/decklink/producer_decklink.yml mlt-0.9.2/src/modules/decklink/producer_decklink.yml --- mlt-0.9.0/src/modules/decklink/producer_decklink.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/decklink/producer_decklink.yml 2014-06-29 20:23:17.000000000 +0000 @@ -96,3 +96,20 @@ description: The model name of each device that accepts input. type: string readonly: yes + + - identifier: priority + title: Thread priority + description: Set the DeckLink thread's scheduling class to realtime and its priority. + type: integer + minimum: 1 + maximum: 99 + default: 20 + + - identifier: vitc_in + title: Start timecode + type: integer + description: > + The vertical interval timecode (VITC) in binary-coded decimal (BCD) format. + It skips frames that has VITC timecode less then specified. + After reaching first frame with timecode greater or equal then specified this + property is reset to zero. diff -Nru mlt-0.9.0/src/modules/dgraft/factory.c mlt-0.9.2/src/modules/dgraft/factory.c --- mlt-0.9.0/src/modules/dgraft/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dgraft/factory.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/* - * factory.c -- the factory method interfaces - * Copyright (C) 2008 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -extern mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); - -MLT_REPOSITORY -{ - MLT_REGISTER( filter_type, "telecide", filter_telecide_init ); -} diff -Nru mlt-0.9.0/src/modules/dgraft/filter_telecide.c mlt-0.9.2/src/modules/dgraft/filter_telecide.c --- mlt-0.9.0/src/modules/dgraft/filter_telecide.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dgraft/filter_telecide.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1229 +0,0 @@ -/* - * filter_telecide.c -- Donald Graft's Inverse Telecine Filter - * Copyright (C) 2003 Donald A. Graft - * Copyright (C) 2008 Dan Dennedy - * Author: Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include - -#include -#include -#include - -//#define DEBUG_PATTERN_GUIDANCE - -#define MAX_CYCLE 6 -#define BLKSIZE 24 -#define BLKSIZE_TIMES2 (2 * BLKSIZE) -#define GUIDE_32 1 -#define GUIDE_22 2 -#define GUIDE_32322 3 -#define AHEAD 0 -#define BEHIND 1 -#define POST_METRICS 1 -#define POST_FULL 2 -#define POST_FULL_MAP 3 -#define POST_FULL_NOMATCH 4 -#define POST_FULL_NOMATCH_MAP 5 -#define CACHE_SIZE 100000 -#define P 0 -#define C 1 -#define N 2 -#define PBLOCK 3 -#define CBLOCK 4 - -#define NO_BACK 0 -#define BACK_ON_COMBED 1 -#define ALWAYS_BACK 2 - -struct CACHE_ENTRY -{ - unsigned int frame; - unsigned int metrics[5]; - unsigned int chosen; -}; - -struct PREDICTION -{ - unsigned int metric; - unsigned int phase; - unsigned int predicted; - unsigned int predicted_metric; -}; - -struct context_s { - int is_configured; - mlt_properties image_cache; - int out; - - int tff, chroma, blend, hints, show, debug; - float dthresh, gthresh, vthresh, vthresh_saved, bthresh; - int y0, y1, nt, guide, post, back, back_saved; - int pitch, dpitch, pitchover2, pitchtimes4; - int w, h, wover2, hover2, hplus1over2, hminus2; - int xblocks, yblocks; -#ifdef WINDOWED_MATCH - unsigned int *matchc, *matchp, highest_matchc, highest_matchp; -#endif - unsigned int *sumc, *sump, highest_sumc, highest_sump; - int vmetric; - unsigned int *overrides, *overrides_p; - int film, override, inpattern, found; - int force; - - // Used by field matching. - unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp; -// unsigned char *fprpU, *fcrpU, *fcrp_savedU, *fnrpU; -// unsigned char *fprpV, *fcrpV, *fcrp_savedV, *fnrpV; - unsigned char *dstp, *finalp; -// unsigned char *dstpU, *dstpV; - int chosen; - unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric; - unsigned int np, nc, npblock, ncblock, nframe; - float mismatch; - int pframe, x, y; - unsigned char *crp, *prp; - unsigned char *crpU, *prpU; - unsigned char *crpV, *prpV; - int hard; - char status[80]; - - // Metrics cache. - struct CACHE_ENTRY *cache; - - // Pattern guidance data. - int cycle; - struct PREDICTION pred[MAX_CYCLE+1]; -}; -typedef struct context_s *context; - - -static inline -void BitBlt(uint8_t* dstp, int dst_pitch, const uint8_t* srcp, - int src_pitch, int row_size, int height) -{ - uint32_t y; - for(y=0;ychosen == P) use = 'p'; - else if (cx->chosen == C) use = 'c'; - else use = 'n'; - snprintf(buf, sizeof(buf), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); - if ( cx->post ) - snprintf(buf, sizeof(buf), "%sTelecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", buf, frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); - if ( cx->guide ) - snprintf(buf, sizeof(buf), "%spattern mismatch=%0.2f%%\n", buf, cx->mismatch); - snprintf(buf, sizeof(buf), "%sTelecide: frame %d: [%s %c]%s %s\n", buf, frame, cx->found ? "forcing" : "using", use, - cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", - cx->guide ? cx->status : ""); - mlt_properties_set( properties, "meta.attr.telecide.markup", buf ); -} - -static void Debug(context cx, int frame) -{ - char use; - - if (cx->chosen == P) use = 'p'; - else if (cx->chosen == C) use = 'c'; - else use = 'n'; - fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); - if ( cx->post ) - fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); - if ( cx->guide ) - fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch); - fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use, - cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", - cx->guide ? cx->status : ""); -} - -static void WriteHints(int film, int inpattern, mlt_properties frame_properties) -{ - mlt_properties_set_int( frame_properties, "telecide.progressive", film); - mlt_properties_set_int( frame_properties, "telecide.in_pattern", inpattern); -} - -static void PutChosen(context cx, int frame, unsigned int chosen) -{ - int f = frame % CACHE_SIZE; - if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame) - return; - cx->cache[f].chosen = chosen; -} - -static void CacheInsert(context cx, int frame, unsigned int p, unsigned int pblock, - unsigned int c, unsigned int cblock) -{ - int f = frame % CACHE_SIZE; - if (frame < 0 || frame > cx->out) - fprintf( stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame); - cx->cache[f].frame = frame; - cx->cache[f].metrics[P] = p; - if (f) cx->cache[f-1].metrics[N] = p; - cx->cache[f].metrics[C] = c; - cx->cache[f].metrics[PBLOCK] = pblock; - cx->cache[f].metrics[CBLOCK] = cblock; - cx->cache[f].chosen = 0xff; -} - -static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock, - unsigned int *c, unsigned int *cblock) -{ - int f; - - f = frame % CACHE_SIZE; - if (frame < 0 || frame > cx->out) - fprintf( stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame); - if (cx->cache[f].frame != frame) - { - return 0; - } - *p = cx->cache[f].metrics[P]; - *c = cx->cache[f].metrics[C]; - *pblock = cx->cache[f].metrics[PBLOCK]; - *cblock = cx->cache[f].metrics[CBLOCK]; - return 1; -} - -static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric) -{ - // Look for pattern in the actual delivered matches of the previous cycle of frames. - // If a pattern is found, use that to predict the current match. - if ( cx->guide == GUIDE_22 ) - { - if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff || - cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen == 0xff) - return 0; - switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 4) + - (cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen)) - { - case 0x11: - *predicted = C; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; - break; - case 0x22: - *predicted = N; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; - break; - default: return 0; - } - } - else if ( cx->guide == GUIDE_32 ) - { - if (cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen == 0xff || - cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen == 0xff) - return 0; - - switch ((cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen << 16) + - (cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen << 12) + - (cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen << 8) + - (cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen << 4) + - (cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen)) - { - case 0x11122: - case 0x11221: - case 0x12211: - case 0x12221: - case 0x21122: - case 0x11222: - *predicted = C; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; - break; - case 0x22111: - case 0x21112: - case 0x22112: - case 0x22211: - *predicted = N; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; - break; - default: return 0; - } - } - else if ( cx->guide == GUIDE_32322 ) - { - if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff || - cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen == 0xff || - cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen == 0xff) - return 0; - - switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 20) + - (cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen << 16) + - (cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen << 12) + - (cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen << 8) + - (cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen << 4) + - (cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen)) - { - case 0x111122: - case 0x111221: - case 0x112211: - case 0x122111: - case 0x111222: - case 0x112221: - case 0x122211: - case 0x222111: - *predicted = C; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; - break; - case 0x221111: - case 0x211112: - - case 0x221112: - case 0x211122: - *predicted = N; - *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; - break; - default: return 0; - } - } -#ifdef DEBUG_PATTERN_GUIDANCE - fprintf( stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted); -#endif - return 1; -} - -static struct PREDICTION *PredictSoftYUY2(context cx, int frame ) -{ - // Use heuristics to look forward for a match. - int i, j, y, c, n, phase; - unsigned int metric; - - cx->pred[0].metric = 0xffffffff; - if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred; - - // Look at the next cycle of frames. - for (y = frame + 1; y <= frame + cx->cycle; y++) - { - // Look for a frame where the current and next match values are - // very close. Those are candidates to predict the phase, because - // that condition should occur only once per cycle. Store the candidate - // phases and predictions in a list sorted by goodness. The list will - // be used by the caller to try the phases in order. - c = cx->cache[y%CACHE_SIZE].metrics[C]; - n = cx->cache[y%CACHE_SIZE].metrics[N]; - if (c == 0) c = 1; - metric = (100 * abs (c - n)) / c; - phase = y % cx->cycle; - if (metric < 5) - { - // Place the new candidate phase in sorted order in the list. - // Find the insertion point. - i = 0; - while (metric > cx->pred[i].metric) i++; - // Find the end-of-list marker. - j = 0; - while (cx->pred[j].metric != 0xffffffff) j++; - // Shift all items below the insertion point down by one to make - // room for the insertion. - j++; - for (; j > i; j--) - { - cx->pred[j].metric = cx->pred[j-1].metric; - cx->pred[j].phase = cx->pred[j-1].phase; - cx->pred[j].predicted = cx->pred[j-1].predicted; - cx->pred[j].predicted_metric = cx->pred[j-1].predicted_metric; - } - // Insert the new candidate data. - cx->pred[j].metric = metric; - cx->pred[j].phase = phase; - if ( cx->guide == GUIDE_32 ) - { - switch ((frame % cx->cycle) - phase) - { - case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - } - } - else if ( cx->guide == GUIDE_32322 ) - { - switch ((frame % cx->cycle) - phase) - { - case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; - case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; - } - } - } -#ifdef DEBUG_PATTERN_GUIDANCE - fprintf( stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase); -#endif - } - return cx->pred; -} - -static -void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV, - unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV) -{ - int x, y, p, c, tmp1, tmp2, skip; - int vc; - unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2; - unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4; - unsigned char *a0, *a2, *b0, *b2, *b4; - unsigned int diff, index; -# define T 4 - - /* Clear the block sums. */ - for (y = 0; y < cx->yblocks; y++) - { - for (x = 0; x < cx->xblocks; x++) - { -#ifdef WINDOWED_MATCH - matchp[y*xblocks+x] = 0; - matchc[y*xblocks+x] = 0; -#endif - cx->sump[y * cx->xblocks + x] = 0; - cx->sumc[y * cx->xblocks + x] = 0; - } - } - - /* Find the best field match. Subsample the frames for speed. */ - currbot0 = fcrp + cx->pitch; - currbot2 = fcrp + 3 * cx->pitch; - currtop0 = fcrp; - currtop2 = fcrp + 2 * cx->pitch; - currtop4 = fcrp + 4 * cx->pitch; - prevbot0 = fprp + cx->pitch; - prevbot2 = fprp + 3 * cx->pitch; - prevtop0 = fprp; - prevtop2 = fprp + 2 * cx->pitch; - prevtop4 = fprp + 4 * cx->pitch; - if ( cx->tff ) - { - a0 = prevbot0; - a2 = prevbot2; - b0 = currtop0; - b2 = currtop2; - b4 = currtop4; - } - else - { - a0 = currbot0; - a2 = currbot2; - b0 = prevtop0; - b2 = prevtop2; - b4 = prevtop4; - } - p = c = 0; - - // Calculate the field match and film/video metrics. -// if (vi.IsYV12()) skip = 1; -// else - skip = 1 + ( !cx->chroma ); - for (y = 0, index = 0; y < cx->h - 4; y+=4) - { - /* Exclusion band. Good for ignoring subtitles. */ - if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1) - { - for (x = 0; x < cx->w;) - { -// if (vi.IsYV12()) -// index = (y/BLKSIZE)*xblocks + x/BLKSIZE; -// else - index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2; - - // Test combination with current frame. - tmp1 = ((long)currbot0[x] + (long)currbot2[x]); -// diff = abs((long)currtop0[x] - (tmp1 >> 1)); - diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1); - if (diff > cx->nt) - { - c += diff; -#ifdef WINDOWED_MATCH - matchc[index] += diff; -#endif - } - - tmp1 = currbot0[x] + T; - tmp2 = currbot0[x] - T; - vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) || - (tmp2 > currtop0[x] && tmp2 > currtop2[x]); - if (vc) - { - cx->sumc[index]++; - } - - // Test combination with previous frame. - tmp1 = ((long)a0[x] + (long)a2[x]); - diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1); - if (diff > cx->nt) - { - p += diff; -#ifdef WINDOWED_MATCH - matchp[index] += diff; -#endif - } - - tmp1 = a0[x] + T; - tmp2 = a0[x] - T; - vc = (tmp1 < b0[x] && tmp1 < b2[x]) || - (tmp2 > b0[x] && tmp2 > b2[x]); - if (vc) - { - cx->sump[index]++; - } - - x += skip; - if (!(x&3)) x += 4; - } - } - currbot0 += cx->pitchtimes4; - currbot2 += cx->pitchtimes4; - currtop0 += cx->pitchtimes4; - currtop2 += cx->pitchtimes4; - currtop4 += cx->pitchtimes4; - a0 += cx->pitchtimes4; - a2 += cx->pitchtimes4; - b0 += cx->pitchtimes4; - b2 += cx->pitchtimes4; - b4 += cx->pitchtimes4; - } - -// if (vi.IsYV12() && chroma == true) -// { -// int z; -// -// for (z = 0; z < 2; z++) -// { -// // Do the same for the U plane. -// if (z == 0) -// { -// currbot0 = fcrpU + pitchover2; -// currbot2 = fcrpU + 3 * pitchover2; -// currtop0 = fcrpU; -// currtop2 = fcrpU + 2 * pitchover2; -// currtop4 = fcrpU + 4 * pitchover2; -// prevbot0 = fprpU + pitchover2; -// prevbot2 = fprpU + 3 * pitchover2; -// prevtop0 = fprpU; -// prevtop2 = fprpU + 2 * pitchover2; -// prevtop4 = fprpU + 4 * pitchover2; -// } -// else -// { -// currbot0 = fcrpV + pitchover2; -// currbot2 = fcrpV + 3 * pitchover2; -// currtop0 = fcrpV; -// currtop2 = fcrpV + 2 * pitchover2; -// currtop4 = fcrpV + 4 * pitchover2; -// prevbot0 = fprpV + pitchover2; -// prevbot2 = fprpV + 3 * pitchover2; -// prevtop0 = fprpV; -// prevtop2 = fprpV + 2 * pitchover2; -// prevtop4 = fprpV + 4 * pitchover2; -// } -// if (tff == true) -// { -// a0 = prevbot0; -// a2 = prevbot2; -// b0 = currtop0; -// b2 = currtop2; -// b4 = currtop4; -// } -// else -// { -// a0 = currbot0; -// a2 = currbot2; -// b0 = prevtop0; -// b2 = prevtop2; -// b4 = prevtop4; -// } -// -// for (y = 0, index = 0; y < hover2 - 4; y+=4) -// { -// /* Exclusion band. Good for ignoring subtitles. */ -// if (y0 == y1 || y < y0/2 || y > y1/2) -// { -// for (x = 0; x < wover2;) -// { -// if (vi.IsYV12()) -// index = (y/BLKSIZE)*xblocks + x/BLKSIZE; -// else -// index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2; -// -// // Test combination with current frame. -// tmp1 = ((long)currbot0[x] + (long)currbot2[x]); -// diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1); -// if (diff > nt) -// { -// c += diff; -//#ifdef WINDOWED_MATCH -// matchc[index] += diff; -//#endif -// } -// -// tmp1 = currbot0[x] + T; -// tmp2 = currbot0[x] - T; -// vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) || -// (tmp2 > currtop0[x] && tmp2 > currtop2[x]); -// if (vc) -// { -// sumc[index]++; -// } -// -// // Test combination with previous frame. -// tmp1 = ((long)a0[x] + (long)a2[x]); -// diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1); -// if (diff > nt) -// { -// p += diff; -//#ifdef WINDOWED_MATCH -// matchp[index] += diff; -//#endif -// } -// -// tmp1 = a0[x] + T; -// tmp2 = a0[x] - T; -// vc = (tmp1 < b0[x] && tmp1 < b2[x]) || -// (tmp2 > b0[x] && tmp2 > b2[x]); -// if (vc) -// { -// sump[index]++; -// } -// -// x ++; -// if (!(x&3)) x += 4; -// } -// } -// currbot0 += 4*pitchover2; -// currbot2 += 4*pitchover2; -// currtop0 += 4*pitchover2; -// currtop2 += 4*pitchover2; -// currtop4 += 4*pitchover2; -// a0 += 4*pitchover2; -// a2 += 4*pitchover2; -// b0 += 4*pitchover2; -// b2 += 4*pitchover2; -// b4 += 4*pitchover2; -// } -// } -// } -// -// // Now find the blocks that have the greatest differences. -//#ifdef WINDOWED_MATCH -// highest_matchp = 0; -// for (y = 0; y < yblocks; y++) -// { -// for (x = 0; x < xblocks; x++) -// { -//if (frame == 45 && matchp[y * xblocks + x] > 2500) -//{ -// sprintf(buf, "%d/%d = %d\n", x, y, matchp[y * xblocks + x]); -// OutputDebugString(buf); -//} -// if (matchp[y * xblocks + x] > highest_matchp) -// { -// highest_matchp = matchp[y * xblocks + x]; -// } -// } -// } -// highest_matchc = 0; -// for (y = 0; y < yblocks; y++) -// { -// for (x = 0; x < xblocks; x++) -// { -//if (frame == 44 && matchc[y * xblocks + x] > 2500) -//{ -// sprintf(buf, "%d/%d = %d\n", x, y, matchc[y * xblocks + x]); -// OutputDebugString(buf); -//} -// if (matchc[y * xblocks + x] > highest_matchc) -// { -// highest_matchc = matchc[y * xblocks + x]; -// } -// } -// } -//#endif - if ( cx->post ) - { - cx->highest_sump = 0; - for (y = 0; y < cx->yblocks; y++) - { - for (x = 0; x < cx->xblocks; x++) - { - if (cx->sump[y * cx->xblocks + x] > cx->highest_sump) - { - cx->highest_sump = cx->sump[y * cx->xblocks + x]; - } - } - } - cx->highest_sumc = 0; - for (y = 0; y < cx->yblocks; y++) - { - for (x = 0; x < cx->xblocks; x++) - { - if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc) - { - cx->highest_sumc = cx->sumc[y * cx->xblocks + x]; - } - } - } - } -#ifdef WINDOWED_MATCH - CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc); -#else - CacheInsert( cx, frame, p, cx->highest_sump, c, cx->highest_sumc); -#endif -} - - -/** Process the image. -*/ - -static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - // Get the filter service - mlt_filter filter = mlt_frame_pop_service( frame ); - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties frame_properties = mlt_frame_properties( frame ); - context cx = mlt_properties_get_data( properties, "context", NULL ); - mlt_service producer = mlt_service_producer( mlt_filter_service( filter ) ); - cx->out = producer? mlt_producer_get_playtime( MLT_PRODUCER( producer ) ) : 999999; - - if ( ! cx->is_configured ) - { - cx->back = mlt_properties_get_int( properties, "back" ); - cx->chroma = mlt_properties_get_int( properties, "chroma" ); - cx->guide = mlt_properties_get_int( properties, "guide" ); - cx->gthresh = mlt_properties_get_double( properties, "gthresh" ); - cx->post = mlt_properties_get_int( properties, "post" ); - cx->vthresh = mlt_properties_get_double( properties, "vthresh" ); - cx->bthresh = mlt_properties_get_double( properties, "bthresh" ); - cx->dthresh = mlt_properties_get_double( properties, "dthresh" ); - cx->blend = mlt_properties_get_int( properties, "blend" ); - cx->nt = mlt_properties_get_int( properties, "nt" ); - cx->y0 = mlt_properties_get_int( properties, "y0" ); - cx->y1 = mlt_properties_get_int( properties, "y1" ); - cx->hints = mlt_properties_get_int( properties, "hints" ); - cx->debug = mlt_properties_get_int( properties, "debug" ); - cx->show = mlt_properties_get_int( properties, "show" ); - } - - // Get the image - int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); - - if ( ! cx->sump ) - { - int guide = mlt_properties_get_int( properties, "guide" ); - cx->cycle = 0; - if ( guide == GUIDE_32 ) - { - // 24fps to 30 fps telecine. - cx->cycle = 5; - } - else if ( guide == GUIDE_22 ) - { - // PAL guidance (expect the current match to be continued). - cx->cycle = 2; - } - else if ( guide == GUIDE_32322 ) - { - // 25fps to 30 fps telecine. - cx->cycle = 6; - } - - cx->xblocks = (*width+BLKSIZE-1) / BLKSIZE; - cx->yblocks = (*height+BLKSIZE-1) / BLKSIZE; - cx->sump = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) ); - cx->sumc = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) ); - mlt_properties_set_data( properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL ); - mlt_properties_set_data( properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL ); - cx->tff = mlt_properties_get_int( frame_properties, "top_field_first" ); - // fprintf(stderr, "%s: TOP FIELD FIRST %d\n", __FUNCTION__, cx->tff ); - } - - // Only process if we have no error and a valid colour space - if ( error == 0 && *format == mlt_image_yuv422 ) - { - // Put the current image into the image cache, keyed on position - size_t image_size = (*width * *height) << 1; - mlt_position pos = mlt_filter_get_position( filter, frame ); - uint8_t *image_copy = mlt_pool_alloc( image_size ); - memcpy( image_copy, *image, image_size ); - char key[20]; - sprintf( key, MLT_POSITION_FMT, pos ); - mlt_properties_set_data( cx->image_cache, key, image_copy, image_size, (mlt_destructor)mlt_pool_release, NULL ); - - // Only if we have enough frame images cached - if ( pos > 1 && pos > cx->cycle + 1 ) - { - pos -= cx->cycle + 1; - // Get the current frame image - sprintf( key, MLT_POSITION_FMT, pos ); - cx->fcrp = mlt_properties_get_data( cx->image_cache, key, NULL ); - if (!cx->fcrp) return error; - - // Get the previous frame image - cx->pframe = pos == 0 ? 0 : pos - 1; - sprintf( key, "%d", cx->pframe ); - cx->fprp = mlt_properties_get_data( cx->image_cache, key, NULL ); - if (!cx->fprp) return error; - - // Get the next frame image - cx->nframe = pos > cx->out ? cx->out : pos + 1; - sprintf( key, "%d", cx->nframe ); - cx->fnrp = mlt_properties_get_data( cx->image_cache, key, NULL ); - if (!cx->fnrp) return error; - - cx->pitch = *width << 1; - cx->pitchover2 = cx->pitch >> 1; - cx->pitchtimes4 = cx->pitch << 2; - cx->w = *width << 1; - cx->h = *height; - if ((cx->w/2) & 1) - fprintf( stderr, "%s: width must be a multiple of 2\n", __FUNCTION__ ); - if (cx->h & 1) - fprintf( stderr, "%s: height must be a multiple of 2\n", __FUNCTION__ ); - cx->wover2 = cx->w/2; - cx->hover2 = cx->h/2; - cx->hplus1over2 = (cx->h+1)/2; - cx->hminus2 = cx->h - 2; - cx->dpitch = cx->pitch; - - // Ensure that the metrics for the frames - // after the current frame are in the cache. They will be used for - // pattern guidance. - if ( cx->guide ) - { - for ( cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++ ) - { - if ( ! CacheQuery( cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) ) - { - sprintf( key, "%d", cx->y ); - cx->crp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL ); - sprintf( key, "%d", cx->y ? cx->y - 1 : 1 ); - cx->prp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL ); - CalculateMetrics( cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL ); } - } - } - - // Check for manual overrides of the field matching. - cx->found = 0; - cx->film = 1; - cx->override = 0; - cx->inpattern = 0; - cx->vthresh = cx->vthresh; - cx->back = cx->back_saved; - - // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates. - if ( ! CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) ) - { - CalculateMetrics( cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL ); - CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ); - } - if ( ! CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ) ) - { - CalculateMetrics( cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL ); - CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ); - } - - // Determine the best candidate match. - if ( !cx->found ) - { - cx->lowest = cx->c; - cx->chosen = C; - if ( cx->back == ALWAYS_BACK && cx->p < cx->lowest ) - { - cx->lowest = cx->p; - cx->chosen = P; - } - if ( cx->np < cx->lowest ) - { - cx->lowest = cx->np; - cx->chosen = N; - } - } - if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N)) - { - cx->chosen = C; - cx->lowest = cx->c; - } - - // See if we can apply pattern guidance. - cx->mismatch = 100.0; - if ( cx->guide ) - { - cx->hard = 0; - if ( pos >= cx->cycle && PredictHardYUY2( cx, pos, &cx->predicted, &cx->predicted_metric) ) - { - cx->inpattern = 1; - cx->mismatch = 0.0; - cx->hard = 1; - if ( cx->chosen != cx->predicted ) - { - // The chosen frame doesn't match the prediction. - if ( cx->predicted_metric == 0 ) - cx->mismatch = 0.0; - else - cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest ) ) / cx->predicted_metric; - if ( cx->mismatch < cx->gthresh ) - { - // It's close enough, so use the predicted one. - if ( !cx->found ) - { - cx->chosen = cx->predicted; - cx->override = 1; - } - } - else - { - cx->hard = 0; - cx->inpattern = 0; - } - } - } - - if ( !cx->hard && cx->guide != GUIDE_22 ) - { - int i; - struct PREDICTION *pred = PredictSoftYUY2( cx, pos ); - - if ( ( pos <= cx->out - cx->cycle) && ( pred[0].metric != 0xffffffff ) ) - { - // Apply pattern guidance. - // If the predicted match metric is within defined percentage of the - // best calculated one, then override the calculated match with the - // predicted match. - i = 0; - while ( pred[i].metric != 0xffffffff ) - { - cx->predicted = pred[i].predicted; - cx->predicted_metric = pred[i].predicted_metric; -#ifdef DEBUG_PATTERN_GUIDANCE - fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted); -#endif - if ( cx->chosen != cx->predicted ) - { - // The chosen frame doesn't match the prediction. - if ( cx->predicted_metric == 0 ) - cx->mismatch = 0.0; - else - cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest )) / cx->predicted_metric; - if ( (int) cx->mismatch <= cx->gthresh ) - { - // It's close enough, so use the predicted one. - if ( !cx->found ) - { - cx->chosen = cx->predicted; - cx->override = 1; - } - cx->inpattern = 1; - break; - } - else - { - // Looks like we're not in a predictable pattern. - cx->inpattern = 0; - } - } - else - { - cx->inpattern = 1; - cx->mismatch = 0.0; - break; - } - i++; - } - } - } - } - - // Check the match for progressive versus interlaced. - if ( cx->post ) - { - if (cx->chosen == P) cx->vmetric = cx->pblock; - else if (cx->chosen == C) cx->vmetric = cx->cblock; - else if (cx->chosen == N) cx->vmetric = cx->npblock; - - if ( !cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest ) - { - // Backward match. - cx->vmetric = cx->pblock; - cx->chosen = P; - cx->inpattern = 0; - cx->mismatch = 100; - } - if ( cx->vmetric > cx->vthresh ) - { - // After field matching and pattern guidance the frame is still combed. - cx->film = 0; - if ( !cx->found && ( cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP ) ) - { - cx->chosen = C; - cx->vmetric = cx->cblock; - cx->inpattern = 0; - cx->mismatch = 100; - } - } - } - cx->vthresh = cx->vthresh_saved; - - // Setup strings for debug info. - if ( cx->inpattern && !cx->override ) strcpy( cx->status, "[in-pattern]" ); - else if ( cx->inpattern && cx->override ) strcpy( cx->status, "[in-pattern*]" ); - else strcpy( cx->status, "[out-of-pattern]" ); - - // Assemble and output the reconstructed frame according to the final match. - cx->dstp = *image; - if ( cx->chosen == N ) - { - // The best match was with the next frame. - if ( cx->tff ) - { - BitBlt( cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2 ); - BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); - } - else - { - BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); - BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); - } - } - else if ( cx->chosen == C ) - { - // The best match was with the current frame. - BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); - BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); - } - else if ( ! cx->tff ) - { - // The best match was with the previous frame. - BitBlt( cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); - BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); - } - else - { - // The best match was with the previous frame. - BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); - BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); - } - if ( cx->guide ) - PutChosen( cx, pos, cx->chosen ); - - if ( !cx->post || cx->post == POST_METRICS ) - { - if ( cx->force == '+') cx->film = 0; - else if ( cx->force == '-' ) cx->film = 1; - } - else if ((cx->force == '+') || - ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP) - && (cx->film == 0 && cx->force != '-'))) - { - unsigned char *dstpp, *dstpn; - int v1, v2; - - if ( cx->blend ) - { - // Do first and last lines. - uint8_t *final = mlt_pool_alloc( image_size ); - cx->finalp = final; - mlt_frame_set_image( frame, final, image_size, mlt_pool_release ); - dstpn = cx->dstp + cx->dpitch; - for ( cx->x = 0; cx->x < cx->w; cx->x++ ) - { - cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpn[cx->x]) >> 1); - } - cx->finalp = final + (cx->h-1)*cx->dpitch; - cx->dstp = *image + (cx->h-1)*cx->dpitch; - dstpp = cx->dstp - cx->dpitch; - for ( cx->x = 0; cx->x < cx->w; cx->x++ ) - { - cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpp[cx->x]) >> 1); - } - // Now do the rest. - cx->dstp = *image + cx->dpitch; - dstpp = cx->dstp - cx->dpitch; - dstpn = cx->dstp + cx->dpitch; - cx->finalp = final + cx->dpitch; - for ( cx->y = 1; cx->y < cx->h - 1; cx->y++ ) - { - for ( cx->x = 0; cx->x < cx->w; cx->x++ ) - { - v1 = (int) cx->dstp[cx->x] - cx->dthresh; - if ( v1 < 0 ) - v1 = 0; - v2 = (int) cx->dstp[cx->x] + cx->dthresh; - if (v2 > 235) v2 = 235; - if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) - { - if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP ) - { - if (cx->x & 1) cx->finalp[cx->x] = 128; - else cx->finalp[cx->x] = 235; - } - else - cx->finalp[cx->x] = ((int)dstpp[cx->x] + (int)dstpn[cx->x] + (int)cx->dstp[cx->x] + (int)cx->dstp[cx->x]) >> 2; - } - else cx->finalp[cx->x] = cx->dstp[cx->x]; - } - cx->finalp += cx->dpitch; - cx->dstp += cx->dpitch; - dstpp += cx->dpitch; - dstpn += cx->dpitch; - } - - - if (cx->show ) Show( cx, pos, frame_properties); - if (cx->debug) Debug(cx, pos); - if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); - goto final; - } - - // Interpolate mode. - cx->dstp = *image + cx->dpitch; - dstpp = cx->dstp - cx->dpitch; - dstpn = cx->dstp + cx->dpitch; - for ( cx->y = 1; cx->y < cx->h - 1; cx->y+=2 ) - { - for ( cx->x = 0; cx->x < cx->w; cx->x++ ) - { - v1 = (int) cx->dstp[cx->x] - cx->dthresh; - if (v1 < 0) v1 = 0; - v2 = (int) cx->dstp[cx->x] + cx->dthresh; - if (v2 > 235) v2 = 235; - if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) - { - if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP ) - { - if (cx->x & 1) cx->dstp[cx->x] = 128; - else cx->dstp[cx->x] = 235; - } - else - cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1; - } - } - cx->dstp += 2 * cx->dpitch; - dstpp += 2 * cx->dpitch; - dstpn += 2 * cx->dpitch; - } - } - if (cx->show ) Show( cx, pos, frame_properties); - if (cx->debug) Debug(cx, pos); - if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); - -final: - // Flush frame at tail of period from the cache - sprintf( key, MLT_POSITION_FMT, pos - 1 ); - mlt_properties_set_data( cx->image_cache, key, NULL, 0, NULL, NULL ); - } - else - { - // Signal the first {cycle} frames as invalid - mlt_properties_set_int( frame_properties, "garbage", 1 ); - } - } - else if ( error == 0 && *format == mlt_image_yuv420p ) - { - fprintf(stderr,"%s: %d pos " MLT_POSITION_FMT "\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) ); - } - - return error; -} - -/** Process the frame object. -*/ - -static mlt_frame process( mlt_filter this, mlt_frame frame ) -{ - // Push the filter on to the stack - mlt_frame_push_service( frame, this ); - - // Push the frame filter - mlt_frame_push_get_image( frame, get_image ); - - return frame; -} - -/** Constructor for the filter. -*/ - -mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - { - this->process = process; - - // Allocate the context and set up for garbage collection - context cx = (context) mlt_pool_alloc( sizeof(struct context_s) ); - memset( cx, 0, sizeof( struct context_s ) ); - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); - mlt_properties_set_data( properties, "context", cx, sizeof(struct context_s), (mlt_destructor)mlt_pool_release, NULL ); - - // Allocate the metrics cache and set up for garbage collection - cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY )); - mlt_properties_set_data( properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor)mlt_pool_release, NULL ); - int i; - for (i = 0; i < CACHE_SIZE; i++) - { - cx->cache[i].frame = 0xffffffff; - cx->cache[i].chosen = 0xff; - } - - // Allocate the image cache and set up for garbage collection - cx->image_cache = mlt_properties_new(); - mlt_properties_set_data( properties, "image_cache", cx->image_cache, 0, (mlt_destructor)mlt_properties_close, NULL ); - - // Initialize the parameter defaults - mlt_properties_set_int( properties, "guide", 0 ); - mlt_properties_set_int( properties, "back", 0 ); - mlt_properties_set_int( properties, "chroma", 0 ); - mlt_properties_set_int( properties, "post", POST_FULL ); - mlt_properties_set_double( properties, "gthresh", 10.0 ); - mlt_properties_set_double( properties, "vthresh", 50.0 ); - mlt_properties_set_double( properties, "bthresh", 50.0 ); - mlt_properties_set_double( properties, "dthresh", 7.0 ); - mlt_properties_set_int( properties, "blend", 0 ); - mlt_properties_set_int( properties, "nt", 10 ); - mlt_properties_set_int( properties, "y0", 0 ); - mlt_properties_set_int( properties, "y1", 0 ); - mlt_properties_set_int( properties, "hints", 1 ); - } - return this; -} - diff -Nru mlt-0.9.0/src/modules/dgraft/Makefile mlt-0.9.2/src/modules/dgraft/Makefile --- mlt-0.9.0/src/modules/dgraft/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dgraft/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -CFLAGS += -I../.. - -LDFLAGS += -L../../framework -lmlt - -include ../../../config.mak - -TARGET = ../libmltdgraft$(LIBSUF) - -OBJS = factory.o \ - filter_telecide.o - -SRCS := $(OBJS:.o=.c) - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) - -depend: $(SRCS) - $(CC) -MM $(CFLAGS) $^ 1>.depend - -distclean: clean - rm -f .depend - -clean: - rm -f $(OBJS) $(TARGET) - -install: all - install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" - -ifneq ($(wildcard .depend),) -include .depend -endif diff -Nru mlt-0.9.0/src/modules/dv/consumer_libdv.c mlt-0.9.2/src/modules/dv/consumer_libdv.c --- mlt-0.9.0/src/modules/dv/consumer_libdv.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dv/consumer_libdv.c 2014-06-29 20:23:17.000000000 +0000 @@ -343,7 +343,7 @@ output = mlt_properties_get_data( properties, "output_file", NULL ); if ( output == NULL ) { - output = fopen( target, "w" ); + output = fopen( target, "wb" ); if ( output != NULL ) mlt_properties_set_data( properties, "output_file", output, 0, ( mlt_destructor )fclose, 0 ); } diff -Nru mlt-0.9.0/src/modules/dv/consumer_libdv.yml mlt-0.9.2/src/modules/dv/consumer_libdv.yml --- mlt-0.9.0/src/modules/dv/consumer_libdv.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dv/consumer_libdv.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,7 +1,7 @@ schema_version: 0.1 type: consumer identifier: libdv -title: libdv +title: libdv (*deprecated*) version: 1 copyright: Ushodaya Enterprises Limited creator: Charles Yates diff -Nru mlt-0.9.0/src/modules/dv/producer_libdv.yml mlt-0.9.2/src/modules/dv/producer_libdv.yml --- mlt-0.9.0/src/modules/dv/producer_libdv.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/dv/producer_libdv.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,7 +1,7 @@ schema_version: 0.1 type: producer identifier: libdv -title: libdv +title: libdv (*deprecated*) version: 1 copyright: Ushodaya Enterprises Limited creator: Charles Yates diff -Nru mlt-0.9.0/src/modules/effectv/factory.c mlt-0.9.2/src/modules/effectv/factory.c --- mlt-0.9.0/src/modules/effectv/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/factory.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * factory.c -- the factory method interfaces - * Copyright (C) 2007 Stephane Fillod - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -extern mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); - -static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) -{ - char file[ PATH_MAX ]; - snprintf( file, PATH_MAX, "%s/effectv/%s", mlt_environment( "MLT_DATA" ), (char*) data ); - return mlt_properties_parse_yaml( file ); -} - -MLT_REPOSITORY -{ - MLT_REGISTER( filter_type, "BurningTV", filter_burn_init ); - MLT_REGISTER( filter_type, "burningtv", filter_burn_init ); - - MLT_REGISTER_METADATA( filter_type, "BurningTV", metadata, "filter_burningtv.yml" ); - MLT_REGISTER_METADATA( filter_type, "burningtv", metadata, "filter_burningtv.yml" ); -} diff -Nru mlt-0.9.0/src/modules/effectv/filter_burn.c mlt-0.9.2/src/modules/effectv/filter_burn.c --- mlt-0.9.0/src/modules/effectv/filter_burn.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/filter_burn.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -/* - * filter_burn.c -- burning filter - * Copyright (C) 2007 Stephane Fillod - * - * Filter taken from EffecTV - Realtime Digital Video Effector - * Copyright (C) 2001-2006 FUKUCHI Kentaro - * - * BurningTV - burns incoming objects. - * Copyright (C) 2001-2002 FUKUCHI Kentaro - * - * Fire routine is taken from Frank Jan Sorensen's demo program. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include -#include -#include -#include -#include "utils.h" - - -#define MaxColor 120 -#define Decay 15 -#define MAGIC_THRESHOLD "50" - -static RGB32 palette[256]; - -static void makePalette(void) -{ - int i, r, g, b; - uint8_t *p = (uint8_t*) palette; - - for(i=0; i> 8)); - i++; - } - i += 2; - } - } - - return error; -} - -/** Filter processing. -*/ - -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) -{ - // Push the frame filter - mlt_frame_push_service( frame, this ); - mlt_frame_push_get_image( frame, filter_get_image ); - - return frame; -} - -/** Constructor for the filter. -*/ - -mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD ); - } - if (!palette[128]) - { - makePalette(); - } - return this; -} - diff -Nru mlt-0.9.0/src/modules/effectv/filter_burningtv.yml mlt-0.9.2/src/modules/effectv/filter_burningtv.yml --- mlt-0.9.0/src/modules/effectv/filter_burningtv.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/filter_burningtv.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: burningtv -title: BurningTV -version: 1 -copyright: FUKUCHI Kentaro, Stephane Fillod -creator: FUKUCHI Kentaro, Stephane Fillod -contributor: - - Jan Sorensen -license: GPLv2 -language: en -tags: - - Video diff -Nru mlt-0.9.0/src/modules/effectv/image.c mlt-0.9.2/src/modules/effectv/image.c --- mlt-0.9.0/src/modules/effectv/image.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/image.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,308 +0,0 @@ -/* - * EffecTV - Realtime Digital Video Effector - * Copyright (C) 2001-2006 FUKUCHI Kentaro - * - * image.c: utilities for image processing. - * - */ - -#include -#include -#include "utils.h" - - -/* - * Collection of background subtraction functions - */ - -/* checks only fake-Y value */ -/* In these function Y value is treated as R*2+G*4+B. */ - -int image_set_threshold_y(int threshold) -{ - int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */ - - return y_threshold; -} - -void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold) -{ - int i; - int R, G, B; - const RGB32 *p; - short *q; - - p = src; - q = (short *)background; - for(i=0; i>(16-1); - G = ((*p)&0xff00)>>(8-2); - B = (*p)&0xff; - *q = (short)(R + G + B); - p++; - q++; - } -} - -void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold) -{ - int i; - int R, G, B; - const RGB32 *p; - const short *q; - unsigned char *r; - int v; - - p = src; - q = (const short *)background; - r = diff; - for(i=0; i>(16-1); - G = ((*p)&0xff00)>>(8-2); - B = (*p)&0xff; - v = (R + G + B) - (int)(*q); - *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); - - p++; - q++; - r++; - } - -/* The origin of subtraction function is; - * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0; - * - * This functions is transformed to; - * (threshold > (src - dest) > -threshold) ? 0 : 0xff; - * - * (v + threshold)>>24 is 0xff when v is less than -threshold. - * (v - threshold)>>24 is 0xff when v is less than threshold. - * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when - * abs(src - dest) > threshold. - */ -} - -/* Background image is refreshed every frame */ -void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold) -{ - int i; - int R, G, B; - const RGB32 *p; - short *q; - unsigned char *r; - int v; - - p = src; - q = (short *)background; - r = diff; - for(i=0; i>(16-1); - G = ((*p)&0xff00)>>(8-2); - B = (*p)&0xff; - v = (R + G + B) - (int)(*q); - *q = (short)(R + G + B); - *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); - - p++; - q++; - r++; - } -} - -/* checks each RGB value */ - -/* The range of r, g, b are [0..7] */ -RGB32 image_set_threshold_RGB(int r, int g, int b) -{ - unsigned char R, G, B; - RGB32 rgb_threshold; - - R = G = B = 0xff; - R = R<>8); - b = b ^ 0xffffff; - a = a ^ b; - a = a & rgb_threshold; - *r++ = (0 - a)>>24; - } -} - -void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold) -{ - int i; - const RGB32 *p; - RGB32 *q; - unsigned a, b; - unsigned char *r; - - p = src; - q = background; - r = diff; - for(i=0; i>8); - b = b ^ 0xffffff; - a = a ^ b; - a = a & rgb_threshold; - *r++ = (0 - a)>>24; - } -} - -/* noise filter for subtracted image. */ -void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height) -{ - int x, y; - const unsigned char *src; - unsigned char *dest; - unsigned int count; - unsigned int sum1, sum2, sum3; - - src = diff; - dest = diff2 + width +1; - for(y=1; y>24; - src++; - } - dest += 2; - } -} - -/* Y value filters */ -void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) -{ - int i; - int R, G, B, v; - unsigned char *p = diff; - - for(i = video_area; i>0; i--) { - R = ((*src)&0xff0000)>>(16-1); - G = ((*src)&0xff00)>>(8-2); - B = (*src)&0xff; - v = y_threshold - (R + G + B); - *p = (unsigned char)(v>>24); - src++; - p++; - } -} - -void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) -{ - int i; - int R, G, B, v; - unsigned char *p = diff; - - for(i = video_area; i>0; i--) { - R = ((*src)&0xff0000)>>(16-1); - G = ((*src)&0xff00)>>(8-2); - B = (*src)&0xff; - v = (R + G + B) - y_threshold; - *p = (unsigned char)(v>>24); - src++; - p++; - } -} - -/* tiny edge detection */ -void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold) -{ - int x, y; - const unsigned char *p; - unsigned char *q; - int r, g, b; - int ar, ag, ab; - int w; - - p = (const unsigned char *)src; - q = diff2; - w = width * sizeof(RGB32); - - for(y=0; y y_threshold) { - *q = 255; - } else { - *q = 0; - } - q++; - p += 4; - } - p += 4; - *q++ = 0; - } - memset(q, 0, width); -} - -/* horizontal flipping */ -void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height) -{ - int x, y; - - src += width - 1; - for(y=0; y.depend - -distclean: clean - rm -f .depend - -clean: - rm -f $(OBJS) $(TARGET) - -install: all - install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" - install -d "$(DESTDIR)$(mltdatadir)/effectv" - install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/effectv" - -ifneq ($(wildcard .depend),) -include .depend -endif diff -Nru mlt-0.9.0/src/modules/effectv/utils.c mlt-0.9.2/src/modules/effectv/utils.c --- mlt-0.9.0/src/modules/effectv/utils.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/utils.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * EffecTV - Realtime Digital Video Effector - * Copyright (C) 2001-2006 FUKUCHI Kentaro - * - * utils.c: utilities - * - */ - -#include -#include "utils.h" - -/* - * HSI color system utilities - */ -static int itrunc(double f) -{ - int i; - - i=(int)f; - if(i<0)i=0; - if(i>255)i=255; - return i; -} - -void HSItoRGB(double H, double S, double I, int *r, int *g, int *b) -{ - double T,Rv,Gv,Bv; - - Rv=1+S*sin(H-2*M_PI/3); - Gv=1+S*sin(H); - Bv=1+S*sin(H+2*M_PI/3); - T=255.999*I/2; - *r=itrunc(Rv*T); - *g=itrunc(Gv*T); - *b=itrunc(Bv*T); -} - -/* - * fastrand - fast fake random number generator - * Warning: The low-order bits of numbers generated by fastrand() - * are bad as random numbers. For example, fastrand()%4 - * generates 1,2,3,0,1,2,3,0... - * You should use high-order bits. - */ -#ifdef __DARWIN__ -static -#endif -unsigned int fastrand_val; - -unsigned int fastrand(void) -{ - return (fastrand_val=fastrand_val*1103515245+12345); -} - -void fastsrand(unsigned int seed) -{ - fastrand_val = seed; -} diff -Nru mlt-0.9.0/src/modules/effectv/utils.h mlt-0.9.2/src/modules/effectv/utils.h --- mlt-0.9.0/src/modules/effectv/utils.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/effectv/utils.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * EffecTV - Realtime Digital Video Effector - * Copyright (C) 2001-2006 FUKUCHI Kentaro - * - * utils.h: header file for utils - * - */ - -#ifndef __UTILS_H__ -#define __UTILS_H__ - -#include - -typedef uint32_t RGB32; - -/* DEFINE's by nullset@dookie.net */ -#define RED(n) ((n>>16) & 0x000000FF) -#define GREEN(n) ((n>>8) & 0x000000FF) -#define BLUE(n) ((n>>0) & 0x000000FF) -#define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b)) -#define INTENSITY(n) ( ( (RED(n)+GREEN(n)+BLUE(n))/3)) - -/* utils.c */ -void HSItoRGB(double H, double S, double I, int *r, int *g, int *b); - -#ifndef __DARWIN__ -extern unsigned int fastrand_val; -#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) -#endif -unsigned int fastrand(void); -void fastsrand(unsigned int); - -/* image.c */ -int image_set_threshold_y(int threshold); -void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold); -void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold); -void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold); -RGB32 image_set_threshold_RGB(int r, int g, int b); -void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area); -void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); -void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); -void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height); -void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); -void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); -void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold); -void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height); - -#endif /* __UTILS_H__ */ diff -Nru mlt-0.9.0/src/modules/frei0r/factory.c mlt-0.9.2/src/modules/frei0r/factory.c --- mlt-0.9.0/src/modules/frei0r/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/frei0r/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -305,6 +305,7 @@ { transition->process = transition_process; transition->close = transition_close; + f0r_init(); properties=MLT_TRANSITION_PROPERTIES( transition ); mlt_properties_set_int(properties, "_transition_type", 1 ); diff -Nru mlt-0.9.0/src/modules/frei0r/Makefile mlt-0.9.2/src/modules/frei0r/Makefile --- mlt-0.9.0/src/modules/frei0r/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/frei0r/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -12,10 +12,10 @@ transition_frei0r.o \ frei0r_helper.o -CFLAGS += `pkg-config --cflags frei0r 2> /dev/null` +CFLAGS += $(shell pkg-config --cflags frei0r 2> /dev/null) LDFLAGS += -lm $(LIBDL) -LDFLAGS += `pkg-config --libs frei0r 2> /dev/null` +LDFLAGS += $(shell pkg-config --libs frei0r 2> /dev/null) SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.0/src/modules/frei0r/not_thread_safe.txt mlt-0.9.2/src/modules/frei0r/not_thread_safe.txt --- mlt-0.9.0/src/modules/frei0r/not_thread_safe.txt 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/frei0r/not_thread_safe.txt 2014-06-29 20:23:17.000000000 +0000 @@ -12,6 +12,7 @@ cartoon edgeglow equaliz0r +glow lightgraffiti mask0mate tehRoxx0r diff -Nru mlt-0.9.0/src/modules/gtk2/configure mlt-0.9.2/src/modules/gtk2/configure --- mlt-0.9.0/src/modules/gtk2/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/configure 2014-06-29 20:23:17.000000000 +0000 @@ -35,42 +35,54 @@ exit 0 fi - [ "$disable_gtk2" != "0" ] && echo "- gtk2 not found: gtk2 preview disabled" - [ "$disable_pixbuf" != "0" ] && echo "- pixbuf not found: pixbuf loader and rescaler disabled" - [ "$disable_pango" != "0" ] && echo "- pango not found: pango titler disabled" - - echo > config.h - [ "$disable_gtk2" = "0" ] && echo "#define USE_GTK2" >> config.h - [ "$disable_pixbuf" = "0" ] && echo "#define USE_PIXBUF" >> config.h - [ "$disable_pango" = "0" ] && echo "#define USE_PANGO" >> config.h - echo > config.mak - [ "$disable_gtk2" = "0" ] && echo "USE_GTK2=1" >> config.mak - [ "$disable_pixbuf" = "0" ] && echo "USE_PIXBUF=1" >> config.mak - [ "$disable_pango" = "0" ] && echo "USE_PANGO=1" >> config.mak + + if [ "$disable_gtk2" = "0" ] + then + echo "CFLAGS += -DUSE_GTK2" >> config.mak + echo "USE_GTK2=1" >> config.mak + else + echo "- gtk2 not found: gtk2 preview disabled" + fi + + if [ "$disable_pixbuf" = "0" ] + then + echo "CFLAGS += -DUSE_PIXBUF" >> config.mak + echo "USE_PIXBUF=1" >> config.mak + else + echo "- pixbuf not found: pixbuf loader and rescaler disabled" + fi + + if [ "$disable_pango" = "0" ] + then + echo "CFLAGS += -DUSE_PANGO" >> config.mak + echo "USE_PANGO=1" >> config.mak + else + echo "- pango not found: pango titler disabled" + fi [ "$pkgconfig_prefix" != "" ] && echo "PKGCONFIG_PREFIX=$pkgconfig_prefix" >> config.mak pkg-config --exists 'libexif' if [ $? -eq 0 ] then - echo "Libexif found, enabling auto rotate" - echo "#define USE_EXIF" >> config.h - echo "USE_EXIF=1" >> config.mak - echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak - echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak + echo "- Libexif found, enabling auto rotate" + echo "USE_EXIF=1" >> config.mak + echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak + echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak + echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak elif [ -d "$exif_libdir" -a -d "$exif_includedir" ] then # test if we have a libexif if [ -f "$exif_libdir/exif-data.h" ] then - echo "Libexif found, enabling auto rotate" - echo "#define USE_EXIF" >> config.h + echo "- Libexif found, enabling auto rotate" echo "USE_EXIF=1" >> config.mak echo EXIFCXXFLAGS=-I$exif_includedir >> config.mak - echo EXIFLIBS=-L$exif_libdir lexif >> config.mak + echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak + echo EXIFLIBS=-L$exif_libdir lexif >> config.mak else - echo "Libexif not found, disabling exif features (auto rotate)" + echo "- Libexif not found, disabling exif features (auto rotate)" fi fi diff -Nru mlt-0.9.0/src/modules/gtk2/factory.c mlt-0.9.2/src/modules/gtk2/factory.c --- mlt-0.9.0/src/modules/gtk2/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "config.h" #include #include #include @@ -34,8 +33,6 @@ #ifdef USE_PANGO extern mlt_producer producer_pango_init( const char *filename ); -extern mlt_producer producer_count_init( const char *arg ); -extern mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #endif static void initialise( ) @@ -60,10 +57,6 @@ #ifdef USE_PANGO if ( !strcmp( id, "pango" ) ) return producer_pango_init( arg ); - if ( !strcmp( id, "count" ) ) - return producer_count_init( arg ); - if ( !strcmp( id, "dynamictext" ) ) - return filter_dynamictext_init( profile, type, id, arg ); #endif #ifdef USE_PIXBUF @@ -88,15 +81,11 @@ MLT_REPOSITORY { - MLT_REGISTER( producer_type, "count", create_service ); - MLT_REGISTER( filter_type, "dynamictext", create_service ); MLT_REGISTER( consumer_type, "gtk2_preview", create_service ); MLT_REGISTER( filter_type, "gtkrescale", create_service ); MLT_REGISTER( producer_type, "pango", create_service ); MLT_REGISTER( producer_type, "pixbuf", create_service ); - MLT_REGISTER_METADATA( producer_type, "count", metadata, "producer_count.yml" ); - MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" ); MLT_REGISTER_METADATA( consumer_type, "gtk2_preview", metadata, "consumer_gtk2_preview.yml" ); MLT_REGISTER_METADATA( filter_type, "gtkrescale", metadata, "filter_rescale.yml" ); MLT_REGISTER_METADATA( producer_type, "pango", metadata, "producer_pango.yml" ); diff -Nru mlt-0.9.0/src/modules/gtk2/filter_dynamictext.c mlt-0.9.2/src/modules/gtk2/filter_dynamictext.c --- mlt-0.9.0/src/modules/gtk2/filter_dynamictext.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/filter_dynamictext.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,371 +0,0 @@ -/* - * filter_dynamictext.c -- dynamic text overlay filter - * Copyright (C) 2011 Ushodaya Enterprises Limited - * Author: Brian Matherly - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include // for stat() -#include // for stat() -#include // for stat() -#include // for strftime() and gtime() - -#define MAX_TEXT_LEN 512 - -/** Get the next token and indicate whether it is enclosed in "# #". -*/ -static int get_next_token(char* str, int* pos, char* token, int* is_keyword) -{ - int token_pos = 0; - int str_len = strlen( str ); - - if( (*pos) >= str_len || str[*pos] == '\0' ) - { - return 0; - } - - if( str[*pos] == '#' ) - { - *is_keyword = 1; - (*pos)++; - } - else - { - *is_keyword = 0; - } - - while( *pos < str_len && token_pos < MAX_TEXT_LEN - 1) - { - if( str[*pos] == '\\' && str[(*pos) + 1] == '#' ) - { - // Escape Sequence - "#" preceeded by "\" - copy the # into the token. - token[token_pos] = '#'; - token_pos++; - (*pos)++; // skip "\" - (*pos)++; // skip "#" - } - else if( str[*pos] == '#' ) - { - if( *is_keyword ) - { - // Found the end of the keyword - (*pos)++; - } - break; - } - else - { - token[token_pos] = str[*pos]; - token_pos++; - (*pos)++; - } - } - - token[token_pos] = '\0'; - - return 1; -} - -static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text ) -{ - int frames = mlt_frame_get_position( frame ); - double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); - char tc[12] = ""; - if (fps == 0) - { - strncat( text, "-", MAX_TEXT_LEN - strlen( text ) - 1 ); - } - else - { - int seconds = frames / fps; - frames = frames % lrint( fps ); - int minutes = seconds / 60; - seconds = seconds % 60; - int hours = minutes / 60; - minutes = minutes % 60; - sprintf(tc, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames); - strncat( text, tc, MAX_TEXT_LEN - strlen( text ) - 1 ); - } -} - -static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text ) -{ - int pos = mlt_frame_get_position( frame ); - char s[12]; - snprintf( s, sizeof( s ) - 1, "%d", pos ); - strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 ); -} - -static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text ) -{ - mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - char* filename = mlt_properties_get( producer_properties, "resource"); - struct stat file_info; - - if( !stat(filename, &file_info)) - { - struct tm* time_info = gmtime( &(file_info.st_mtime) ); - char date[11] = ""; - strftime( date, 11, "%Y/%m/%d", time_info ); - strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); - } -} - -static void get_localfiledate_str( mlt_filter filter, mlt_frame frame, char* text ) -{ - mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - char* filename = mlt_properties_get( producer_properties, "resource" ); - struct stat file_info; - - if( !stat( filename, &file_info ) ) - { - struct tm* time_info = localtime( &(file_info.st_mtime) ); - char date[11] = ""; - strftime( date, 11, "%Y/%m/%d", time_info ); - strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); - } -} - -static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text ) -{ - mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - strncat( text, mlt_properties_get( producer_properties, "resource" ), MAX_TEXT_LEN - strlen( text ) - 1 ); -} - -/** Perform substitution for keywords that are enclosed in "# #". -*/ -static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame) -{ - char keyword[MAX_TEXT_LEN] = ""; - int pos = 0; - int is_keyword = 0; - - while ( get_next_token(value, &pos, keyword, &is_keyword) ) - { - if(!is_keyword) - { - strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 ); - } - else if ( !strcmp( keyword, "timecode" ) ) - { - get_timecode_str( filter, frame, result ); - } - else if ( !strcmp( keyword, "frame" ) ) - { - get_frame_str( filter, frame, result ); - } - else if ( !strcmp( keyword, "filedate" ) ) - { - get_filedate_str( filter, frame, result ); - } - else if ( !strcmp( keyword, "localfiledate" ) ) - { - get_localfiledate_str( filter, frame, result ); - } - else if ( !strcmp( keyword, "resource" ) ) - { - get_resource_str( filter, frame, result ); - } - else - { - // replace keyword with property value from this frame - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); - char *frame_value = mlt_properties_get( frame_properties, keyword ); - if( frame_value ) - { - strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 ); - } - } - } -} - -static void setup_producer( mlt_filter filter, mlt_producer producer, mlt_frame frame ) -{ - mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - char* dynamic_text = mlt_properties_get( my_properties, "argument" ); - - // Check for keywords in dynamic text - if ( dynamic_text ) - { - // Apply keyword substitution before passing the text to the filter. - char result[MAX_TEXT_LEN] = ""; - substitute_keywords( filter, result, dynamic_text, frame ); - mlt_properties_set( producer_properties, "markup", (char*)result ); - } - - // Pass the properties to the pango producer - mlt_properties_set( producer_properties, "family", mlt_properties_get( my_properties, "family" ) ); - mlt_properties_set( producer_properties, "size", mlt_properties_get( my_properties, "size" ) ); - mlt_properties_set( producer_properties, "weight", mlt_properties_get( my_properties, "weight" ) ); - mlt_properties_set( producer_properties, "fgcolour", mlt_properties_get( my_properties, "fgcolour" ) ); - mlt_properties_set( producer_properties, "bgcolour", mlt_properties_get( my_properties, "bgcolour" ) ); - mlt_properties_set( producer_properties, "olcolour", mlt_properties_get( my_properties, "olcolour" ) ); - mlt_properties_set( producer_properties, "pad", mlt_properties_get( my_properties, "pad" ) ); - mlt_properties_set( producer_properties, "outline", mlt_properties_get( my_properties, "outline" ) ); - mlt_properties_set( producer_properties, "align", mlt_properties_get( my_properties, "halign" ) ); -} - -static void setup_transition( mlt_filter filter, mlt_transition transition ) -{ - mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); - - mlt_properties_set( transition_properties, "geometry", mlt_properties_get( my_properties, "geometry" ) ); - mlt_properties_set( transition_properties, "halign", mlt_properties_get( my_properties, "halign" ) ); - mlt_properties_set( transition_properties, "valign", mlt_properties_get( my_properties, "valign" ) ); - mlt_properties_set_int( transition_properties, "out", mlt_properties_get_int( my_properties, "_out" ) ); - mlt_properties_set_int( transition_properties, "refresh", 1 ); -} - - -/** Get the image. -*/ -static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - int error = 0; - mlt_filter filter = mlt_frame_pop_service( frame ); - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_producer producer = mlt_properties_get_data( properties, "_producer", NULL ); - mlt_transition transition = mlt_properties_get_data( properties, "_transition", NULL ); - mlt_frame text_frame = NULL; - mlt_position position = 0; - - // Configure this filter - mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); - setup_producer( filter, producer, frame ); - setup_transition( filter, transition ); - mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); - - // Make sure the producer is in the correct position - position = mlt_filter_get_position( filter, frame ); - mlt_producer_seek( producer, position ); - - // Get the b frame and process with transition if successful - if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &text_frame, 0 ) == 0 ) - { - // Get the a and b frame properties - mlt_properties a_props = MLT_FRAME_PROPERTIES( frame ); - mlt_properties b_props = MLT_FRAME_PROPERTIES( text_frame ); - - // Set the frame and text_frame to be in the same position and have same consumer requirements - mlt_frame_set_position( text_frame, position ); - mlt_frame_set_position( frame, position ); - mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) ); - - // Apply all filters that are attached to this filter to the b frame - mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), text_frame, 0 ); - - // Process the frame - mlt_transition_process( transition, frame, text_frame ); - - // Get the image - *format = mlt_image_yuv422; - error = mlt_frame_get_image( frame, image, format, width, height, 1 ); - - // Close the b frame - mlt_frame_close( text_frame ); - } - - return error; -} - -/** Filter processing. -*/ -static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) -{ - // Get the properties of the frame - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - - // Save the frame out point - mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_out", mlt_properties_get_int( properties, "out" ) ); - - // Push the filter on to the stack - mlt_frame_push_service( frame, filter ); - - // Push the get_image on to the stack - mlt_frame_push_get_image( frame, filter_get_image ); - - return frame; -} - -/** Constructor for the filter. -*/ -mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter filter = mlt_filter_new(); - mlt_transition transition = mlt_factory_transition( profile, "composite", NULL ); - mlt_producer producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" ); - - if ( filter && transition && producer ) - { - mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); - - // Register the transition for reuse/destruction - mlt_properties_set_data( my_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); - - // Register the producer for reuse/destruction - mlt_properties_set_data( my_properties, "_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); - - // Ensure that we loop - mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); - - // Assign default values - mlt_properties_set( my_properties, "argument", arg ? arg: "#timecode#" ); - mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100" ); - mlt_properties_set( my_properties, "family", "Sans" ); - mlt_properties_set( my_properties, "size", "48" ); - mlt_properties_set( my_properties, "weight", "400" ); - mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); - mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); - mlt_properties_set( my_properties, "olcolour", "0x00000000" ); - mlt_properties_set( my_properties, "pad", "0" ); - mlt_properties_set( my_properties, "halign", "left" ); - mlt_properties_set( my_properties, "valign", "top" ); - mlt_properties_set( my_properties, "outline", "0" ); - - mlt_properties_set_int( my_properties, "_filter_private", 1 ); - - filter->process = filter_process; - } - else - { - if( filter ) - { - mlt_filter_close( filter ); - } - - if( transition ) - { - mlt_transition_close( transition ); - } - - if( producer ) - { - mlt_producer_close( producer ); - } - - filter = NULL; - } - return filter; -} diff -Nru mlt-0.9.0/src/modules/gtk2/filter_dynamictext.yml mlt-0.9.2/src/modules/gtk2/filter_dynamictext.yml --- mlt-0.9.0/src/modules/gtk2/filter_dynamictext.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/filter_dynamictext.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: dynamictext -title: Dynamic text -version: 1 -copyright: Ushodaya Enterprises Limited -creator: Brian Matherly -license: LGPLv2.1 -language: en -tags: - - Video -description: Overlay dynamic text onto the video -notes: > - The dynamic text filter will search for keywords in the text to be overlayed - and will replace those keywords on a frame-by-frame basis. - -parameters: - - identifier: argument - title: Dynamic text - type: string - description: | - The text to overlay. May include keywords enclosed in "#". - Keywords include: - * #timecode# - timecode of the frame (based on framerate and position) - * #frame# - frame number of the frame - * #filedate# - modification date of the file - Keywords may also be any frame property (e.g. #meta.media.0.codec.frame_rate#) - The # may be escaped with "\". - required: yes - readonly: no - default: > #trick to escape "#" character - #timecode# - widget: text - - identifier: geometry - title: Geometry - type: geometry - description: A set of X/Y coordinates by which to adjust the text. - default: 0%/0%:100%x100%:100 - - identifier: family - title: Font family - type: string - description: > - The typeface of the font. - default: Sans - readonly: no - mutable: yes - widget: combo - - identifier: size - title: Font size - type: integer - description: > - The size in pixels of the font. - default: 48 - readonly: no - mutable: yes - widget: spinner - - identifier: weight - title: Font weight - type: integer - description: The weight of the font. - minimum: 100 - maximum: 1000 - default: 400 - readonly: no - mutable: yes - widget: spinner - - identifier: fgcolour - title: Foreground color - type: string - description: > - A color value is a hexadecimal representation of RGB plus alpha channel - as 0xrrggbbaa. Colors can also be the words: white, black, red, green, - or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. - default: 0x000000ff - readonly: no - mutable: yes - widget: color - - identifier: bgcolour - title: Background color - type: string - description: > - A color value is a hexadecimal representation of RGB plus alpha channel - as 0xrrggbbaa. Colors can also be the words: white, black, red, green, - or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. - default: 0x00000020 - readonly: no - mutable: yes - widget: color - - identifier: olcolour - title: Outline color - type: string - description: > - A color value is a hexadecimal representation of RGB plus alpha channel - as 0xrrggbbaa. Colors can also be the words: white, black, red, green, - or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. - readonly: no - mutable: yes - widget: color - - identifier: outline - title: Outline Width - type: string - description: > - The width of the outline in pixels. - readonly: no - default: 0 - minimum: 0 - maximum: 3 - mutable: yes - widget: spinner - - identifier: pad - title: Padding - type: integer - description: > - The number of pixels to pad the background rectangle beyond edges of text. - readonly: no - default: 0 - mutable: yes - widget: spinner - - identifier: halign - title: Horizontal alignment - description: > - Set the horizontal alignment within the geometry rectangle. - type: string - default: left - values: - - left - - centre - - right - mutable: yes - widget: combo - - identifier: valign - title: Vertical alignment - description: > - Set the vertical alignment within the geometry rectangle. - type: string - default: top - values: - - top - - middle - - bottom - mutable: yes - widget: combo diff -Nru mlt-0.9.0/src/modules/gtk2/Makefile mlt-0.9.2/src/modules/gtk2/Makefile --- mlt-0.9.0/src/modules/gtk2/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -1,24 +1,24 @@ -CFLAGS += -I../.. - -LDFLAGS += -L../../framework -lmlt -lpthread -lm - include ../../../config.mak include config.mak +CFLAGS := -I../.. $(CFLAGS) + +LDFLAGS := -L../../framework -lmlt -lpthread -lm $(LDFLAGS) + TARGET = ../libmltgtk2$(LIBSUF) OBJS = factory.o ifdef USE_GTK2 OBJS += consumer_gtk2.o -CFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --cflags gtk+-2.0` -LDFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --libs gtk+-2.0` +CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags gtk+-2.0) +LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs gtk+-2.0) endif ifdef USE_PIXBUF OBJS += producer_pixbuf.o pixops.o filter_rescale.o -CFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --cflags gdk-pixbuf-2.0` -LDFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --libs gdk-pixbuf-2.0` +CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags gdk-pixbuf-2.0) +LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs gdk-pixbuf-2.0) endif ifdef USE_EXIF @@ -34,10 +34,9 @@ ifdef USE_PANGO OBJS += producer_pango.o -OBJS += producer_count.o -OBJS += filter_dynamictext.o -CFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --cflags pangoft2` -LDFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --libs pangoft2` +CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags pangoft2) +CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags-only-I freetype2 | awk '{for (i=1; i<=NF; i++) $$i=sprintf("%s/freetype", $$i); print}') +LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs pangoft2) ifeq ($(targetos),Darwin) LDFLAGS += -liconv endif diff -Nru mlt-0.9.0/src/modules/gtk2/producer_count.c mlt-0.9.2/src/modules/gtk2/producer_count.c --- mlt-0.9.0/src/modules/gtk2/producer_count.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/producer_count.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,619 +0,0 @@ -/* - * producer_count.c -- counting producer - * Copyright (C) 2013 Brian Matherly - * Author: Brian Matherly - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -/* Private Constants */ -#define MAX_TEXT_LEN 512 -#define LINE_PIXEL_VALUE 0x00 -#define RING_PIXEL_VALUE 0xff -#define CLOCK_PIXEL_VALUE 0x50 -#define FRAME_BACKGROUND_COLOR "0xd0d0d0ff" -#define TEXT_BACKGROUND_COLOR "0x00000000" -#define TEXT_FOREGROUND_COLOR "0x000000ff" -// Ratio of graphic elements relative to image size -#define LINE_WIDTH_RATIO 1 -#define OUTER_RING_RATIO 90 -#define INNER_RING_RATIO 80 -#define TEXT_SIZE_RATIO 70 - -static inline void mix_pixel( uint8_t* image, int width, int x, int y, int value, float mix ) -{ - uint8_t* p = image + (( y * width ) + x ) * 4; - - if( mix != 1.0 ) - { - value = ((float)value * mix) + ((float)*p * (1.0 - mix)); - } - - *p = value; - p++; - *p = value; - p++; - *p = value; -} - -/** Fill an audio buffer with 1kHz samples. -*/ - -static void fill_beep( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) -{ - int s = 0; - int c = 0; - - for( s = 0; s < samples; s++ ) - { - float f = 1000.0; - float t = (float)s/(float)frequency; - float value = sin( 2*M_PI*f*t ); - - for( c = 0; c < channels; c++ ) - { - float* sample_ptr = buffer + (c * samples) + s; - *sample_ptr = value; - } - } -} - -static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) -{ - mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_count", NULL ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - char* sound = mlt_properties_get( producer_properties, "sound" ); - double fps = mlt_producer_get_fps( producer ); - int position = mlt_frame_get_position( frame ); - int size = 0; - int do_beep = 0; - - if( fps == 0 ) fps = 25; - - // Correct the returns if necessary - *format = mlt_audio_float; - *frequency = *frequency <= 0 ? 48000 : *frequency; - *channels = *channels <= 0 ? 2 : *channels; - *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples; - - // Allocate the buffer - size = *samples * *channels * sizeof( float ); - *buffer = mlt_pool_alloc( size ); - - // Determine if this should be a tone or silence. - if( strcmp( sound, "none") ) - { - if( !strcmp( sound, "2pop" ) ) - { - int out = mlt_properties_get_int( producer_properties, "out" ); - int frames = out - position; - - if( frames == lrint( fps * 2 ) ) - { - do_beep = 1; - } - } - else if( !strcmp( sound, "frame0" ) ) - { - int frames = position; - - // Apply the direction - char* direction = mlt_properties_get( producer_properties, "direction" ); - if( !strcmp( direction, "down" ) ) - { - int out = mlt_properties_get_int( producer_properties, "out" ); - frames = out - position; - } - - frames = position % lrint( fps ); - if( frames == 0 ) - { - do_beep = 1; - } - } - } - - if( do_beep ) - { - fill_beep( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); - } - else - { - // Fill silence. - memset( *buffer, 0, size ); - } - - // Set the buffer for destruction - mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); - return 0; -} - -static mlt_frame get_background_frame( mlt_producer producer ) -{ - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - mlt_frame bg_frame = NULL; - mlt_producer color_producer = mlt_properties_get_data( producer_properties, "_color_producer", NULL ); - - if( !color_producer ) - { - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); - color_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "colour:" ); - mlt_properties_set_data( producer_properties, "_color_producer", color_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); - - mlt_properties color_properties = MLT_PRODUCER_PROPERTIES( color_producer ); - mlt_properties_set( color_properties, "colour", FRAME_BACKGROUND_COLOR ); - } - - if( color_producer ) - { - mlt_producer_seek( color_producer, 0 ); - mlt_service_get_frame( MLT_PRODUCER_SERVICE( color_producer ), &bg_frame, 0 ); - } - - return bg_frame; -} - -static mlt_frame get_text_frame( mlt_producer producer, mlt_position position ) -{ - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - mlt_producer pango_producer = mlt_properties_get_data( producer_properties, "_pango_producer", NULL ); - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); - mlt_frame text_frame = NULL; - - if( !pango_producer ) - { - pango_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" ); - - // Save the producer for future use. - mlt_properties_set_data( producer_properties, "_pango_producer", pango_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); - - // Calculate the font size. - char font_size[MAX_TEXT_LEN]; - snprintf( font_size, MAX_TEXT_LEN - 1, "%dpx", profile->height * TEXT_SIZE_RATIO / 100 ); - - // Configure the producer. - mlt_properties pango_properties = MLT_PRODUCER_PROPERTIES( pango_producer ); - mlt_properties_set( pango_properties, "size", font_size ); - mlt_properties_set( pango_properties, "weight", "400" ); - mlt_properties_set( pango_properties, "fgcolour", TEXT_FOREGROUND_COLOR ); - mlt_properties_set( pango_properties, "bgcolour", TEXT_BACKGROUND_COLOR ); - mlt_properties_set( pango_properties, "pad", "0" ); - mlt_properties_set( pango_properties, "outline", "0" ); - mlt_properties_set( pango_properties, "align", "center" ); - } - - if( pango_producer ) - { - mlt_properties pango_properties = MLT_PRODUCER_PROPERTIES( pango_producer ); - char* direction = mlt_properties_get( producer_properties, "direction" ); - char* style = mlt_properties_get( producer_properties, "style" ); - char text[MAX_TEXT_LEN] = ""; - int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25; - - // Apply the direction - if( !strcmp( direction, "down" ) ) - { - int out = mlt_properties_get_int( producer_properties, "out" ); - position = out - position; - } - - // Calculate clock values - int seconds = position / fps; - int frames = MLT_POSITION_MOD(position, fps); - int minutes = seconds / 60; - seconds = seconds % 60; - int hours = minutes / 60; - minutes = minutes % 60; - - // Apply the time style - if( !strcmp( style, "frames" ) ) - { - snprintf( text, MAX_TEXT_LEN - 1, MLT_POSITION_FMT, position ); - } - else if( !strcmp( style, "timecode" ) ) - { - snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames ); - } - else if( !strcmp( style, "clock" ) ) - { - snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d", hours, minutes, seconds ); - } - else if( !strcmp( style, "seconds+1" ) ) - { - snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds + 1 ); - } - else // seconds - { - snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds ); - } - - mlt_properties_set( pango_properties, "markup", text ); - - // Get the frame. - mlt_service_get_frame( MLT_PRODUCER_SERVICE( pango_producer ), &text_frame, 0 ); - } - - return text_frame; -} - -static void draw_ring( uint8_t* image, mlt_profile profile, int radius, int line_width ) -{ - float sar = mlt_profile_sar( profile ); - int x_center = profile->width / 2; - int y_center = profile->height / 2; - int max_radius = radius + line_width; - int a = max_radius + 1; - int b = 0; - - line_width += 1; // Compensate for aliasing. - - // Scan through each pixel in one quadrant of the circle. - while( a-- ) - { - b = ( max_radius / sar ) + 1.0; - while( b-- ) - { - // Use Pythagorean theorem to determine the distance from this pixel to the center. - float a2 = a*a; - float b2 = b*sar*b*sar; - float c = sqrtf( a2 + b2 ); - float distance = c - radius; - - if( distance > 0 && distance < line_width ) - { - // This pixel is within the ring. - float mix = 1.0; - - if( distance < 1.0 ) - { - // Antialias the outside of the ring - mix = distance; - } - else if( (float)line_width - distance < 1.0 ) - { - // Antialias the inside of the ring - mix = (float)line_width - distance; - } - - // Apply this value to all 4 quadrants of the circle. - mix_pixel( image, profile->width, x_center + b, y_center - a, RING_PIXEL_VALUE, mix ); - mix_pixel( image, profile->width, x_center - b, y_center - a, RING_PIXEL_VALUE, mix ); - mix_pixel( image, profile->width, x_center + b, y_center + a, RING_PIXEL_VALUE, mix ); - mix_pixel( image, profile->width, x_center - b, y_center + a, RING_PIXEL_VALUE, mix ); - } - } - } -} - -static void draw_cross( uint8_t* image, mlt_profile profile, int line_width ) -{ - int x = 0; - int y = 0; - int i = 0; - - // Draw a horizontal line - i = line_width; - while( i-- ) - { - y = ( profile->height - line_width ) / 2 + i; - x = profile->width - 1; - while( x-- ) - { - mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); - } - } - - // Draw a vertical line - line_width = lrint((float)line_width * mlt_profile_sar( profile )); - i = line_width; - while( i-- ) - { - x = ( profile->width - line_width ) / 2 + i; - y = profile->height - 1; - while( y-- ) - { - mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); - } - } -} - -static void draw_clock( uint8_t* image, mlt_profile profile, int angle, int line_width ) -{ - float sar = mlt_profile_sar( profile ); - int q = 0; - int x_center = profile->width / 2; - int y_center = profile->height / 2; - - line_width += 1; // Compensate for aliasing. - - // Look at each quadrant of the frame to see what should be done. - for( q = 1; q <= 4; q++ ) - { - int max_angle = q * 90; - int x_sign = ( q == 1 || q == 2 ) ? 1 : -1; - int y_sign = ( q == 1 || q == 4 ) ? 1 : -1; - int x_start = x_center * x_sign; - int y_start = y_center * y_sign; - - // Compensate for rounding error of even lengths - // (there is no "middle" pixel so everything is offset). - if( x_sign == 1 && profile->width % 2 == 0 ) x_start--; - if( y_sign == -1 && profile->height % 2 == 0 ) y_start++; - - if( angle >= max_angle ) - { - // This quadrant is completely behind the clock hand. Fill it in. - int dx = x_start + x_sign; - while( dx ) - { - dx -= x_sign; - int dy = y_start + y_sign; - while( dy ) - { - dy -= y_sign; - mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); - } - } - } - else if ( max_angle - angle < 90 ) - { - // This quadrant is partially filled - // Calculate a point (vx,vy) that lies on the line created by the angle from 0,0. - int vx = 0; - int vy = y_start; - float lv = 0; - - // Assume maximum y and calculate the corresponding x value - // for a point at the other end of this line. - if( x_sign * y_sign == 1 ) - { - vx = x_sign * sar * y_center / tan( ( max_angle - angle ) * M_PI / 180.0 ); - } - else - { - vx = x_sign * sar * y_center * tan( ( max_angle - angle ) * M_PI / 180.0 ); - } - - // Calculate the length of the line defined by vx,vy - lv = sqrtf((float)(vx*vx)*sar*sar + (float)vy*vy); - - // Scan through each pixel in the quadrant counting up/down to 0,0. - int dx = x_start + x_sign; - while( dx ) - { - dx -= x_sign; - int dy = y_start + y_sign; - while( dy ) - { - dy -= y_sign; - // Calculate the cross product to determine which side of - // the line this pixel lies on. - int xp = vx * (vy - dy) - vy * (vx - dx); - xp = xp * -1; // Easier to work with positive. Positive number means "behind" the line. - if( xp > 0 ) - { - // This pixel is behind the clock hand and should be filled in. - // Calculate the distance from the pixel to the line to determine - // if it is part of the clock hand. - float distance = (float)xp / lv; - int val = CLOCK_PIXEL_VALUE; - float mix = 1.0; - - if( distance < line_width ) - { - // This pixel makes up the clock hand. - val = LINE_PIXEL_VALUE; - - if( distance < 1.0 ) - { - // Antialias the outside of the clock hand - mix = distance; - } - else if( (float)line_width - distance < 1.0 ) - { - // Antialias the inside of the clock hand - mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); - mix = (float)line_width - distance; - } - } - - mix_pixel( image, profile->width, x_center + dx, y_center - dy, val, mix ); - } - } - } - } - } -} - -static void add_clock_to_frame( mlt_producer producer, mlt_frame frame, mlt_position position ) -{ - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - uint8_t* image = NULL; - mlt_image_format format = mlt_image_rgb24a; - int size = 0; - int width = profile->width; - int height = profile->height; - int line_width = LINE_WIDTH_RATIO * (width > height ? height : width) / 100; - int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25; - int radius = (width > height ? height : width) / 2; - char* direction = mlt_properties_get( producer_properties, "direction" ); - int clock_angle = 0; - - mlt_frame_get_image( frame, &image, &format, &width, &height, 1 ); - - // Calculate the angle for the clock. - if( !strcmp( direction, "down" ) ) - { - int out = mlt_producer_get_out( producer ); - int frames = fps - MLT_POSITION_MOD(out - position, fps); - clock_angle = lrint( (frames + 1) * 360 / fps ); - } - else - { - int frames = MLT_POSITION_MOD(position, fps); - clock_angle = lrint( (frames + 1) * 360 / fps ); - } - - draw_clock( image, profile, clock_angle, line_width ); - draw_cross( image, profile, line_width ); - draw_ring( image, profile, ( radius * OUTER_RING_RATIO ) / 100, line_width ); - draw_ring( image, profile, ( radius * INNER_RING_RATIO ) / 100, line_width ); - - size = mlt_image_format_size( format, width, height, NULL ); - mlt_frame_set_image( frame, image, size, mlt_pool_release ); -} - - -static void add_text_to_bg( mlt_producer producer, mlt_frame bg_frame, mlt_frame text_frame ) -{ - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - mlt_transition transition = mlt_properties_get_data( producer_properties, "_transition", NULL ); - - if( !transition ) - { - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); - transition = mlt_factory_transition( profile, "composite", NULL ); - - // Save the transition for future use. - mlt_properties_set_data( producer_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); - - // Configure the transition. - mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); - mlt_properties_set( transition_properties, "geometry", "0%/0%:100%x100%:100" ); - mlt_properties_set( transition_properties, "halign", "center" ); - mlt_properties_set( transition_properties, "valign", "middle" ); - } - - if( transition && bg_frame && text_frame ) - { - // Apply the transition. - mlt_transition_process( transition, bg_frame, text_frame ); - } -} - -static int producer_get_image( mlt_frame frame, uint8_t** image, mlt_image_format* format, int* width, int* height, int writable ) -{ - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_position position = mlt_frame_original_position( frame ); - mlt_producer producer = mlt_properties_get_data( properties, "_producer_count", NULL ); - mlt_frame bg_frame = NULL; - mlt_frame text_frame = NULL; - int error = 1; - int size = 0; - char* background = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "background" ); - - mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); - - bg_frame = get_background_frame( producer ); - if( !strcmp( background, "clock" ) ) - { - add_clock_to_frame( producer, bg_frame, position ); - } - text_frame = get_text_frame( producer, position ); - add_text_to_bg( producer, bg_frame, text_frame ); - - if( bg_frame ) - { - // Get the image from the background frame. - error = mlt_frame_get_image( bg_frame, image, format, width, height, writable ); - size = mlt_image_format_size( *format, *width, *height, NULL ); - // Detach the image from the bg_frame so it is not released. - mlt_frame_set_image( bg_frame, *image, size, NULL ); - // Attach the image to the input frame. - mlt_frame_set_image( frame, *image, size, mlt_pool_release ); - mlt_frame_close( bg_frame ); - } - - if( text_frame ) - { - mlt_frame_close( text_frame ); - } - - mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); - - return error; -} - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) -{ - // Generate a frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); - - if ( *frame != NULL ) - { - // Obtain properties of frame - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); - - // Save the producer to be used later - mlt_properties_set_data( frame_properties, "_producer_count", producer, 0, NULL, NULL ); - - // Update time code on the frame - mlt_frame_set_position( *frame, mlt_producer_frame( producer ) ); - - mlt_properties_set_int( frame_properties, "progressive", 1 ); - mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); - mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); - mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); - - // Configure callbacks - mlt_frame_push_get_image( *frame, producer_get_image ); - mlt_frame_push_audio( *frame, producer_get_audio ); - } - - // Calculate the next time code - mlt_producer_prepare_next( producer ); - - return 0; -} - -static void producer_close( mlt_producer this ) -{ - this->close = NULL; - mlt_producer_close( this ); - free( this ); -} - -/** Initialize. -*/ - -mlt_producer producer_count_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - // Create a new producer object - mlt_producer producer = mlt_producer_new( profile ); - - // Initialize the producer - if ( producer ) - { - mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); - mlt_properties_set( properties, "direction", "down" ); - mlt_properties_set( properties, "style", "seconds+1" ); - mlt_properties_set( properties, "sound", "none" ); - mlt_properties_set( properties, "background", "clock" ); - - // Callback registration - producer->get_frame = producer_get_frame; - producer->close = ( mlt_destructor )producer_close; - } - - return producer; -} diff -Nru mlt-0.9.0/src/modules/gtk2/producer_count.yml mlt-0.9.2/src/modules/gtk2/producer_count.yml --- mlt-0.9.0/src/modules/gtk2/producer_count.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/producer_count.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -schema_version: 0.1 -type: producer -identifier: count -title: Count -version: 1 -copyright: Brian Matherly -creator: Brian Matherly -license: LGPLv2.1 -language: en -tags: - - Audio - - Video -description: > - Generate frames with a counter and synchronized tone. - The counter can go up or down. -parameters: - - identifier: direction - title: Count Direction - description: Whether to count up or down. - type: string - default: down - values: - - up - - down - mutable: yes - widget: combo - - identifier: style - title: Counter Style - description: | - The style of the counter. - * seconds - seconds counting up from or down to 0 - * seconds+1 - seconds counting up from or down to 1 - * frames - frames - * timecode - timecode in the format HH:MM:SS:FF - * clock - clock in the format HH:MM:SS - type: string - default: seconds+1 - values: - - seconds - - seconds+1 - - frames - - timecode - - clock - mutable: yes - widget: combo - - identifier: sound - title: Sound - description: | - The sound to be produced. - * silent - No sound - * 2pop - A 1kHz beep exactly two seconds before the out point - * frame0 - A 1kHz beep at frame 0 of every second - type: string - default: silent - values: - - none - - 2pop - - frame0 - mutable: yes - widget: combo - - identifier: background - title: Background - description: | - The background style. - * none - No background - * clock - Film style clock animation - type: string - default: clock - values: - - none - - clock - mutable: yes - widget: combo diff -Nru mlt-0.9.0/src/modules/gtk2/producer_pango.c mlt-0.9.2/src/modules/gtk2/producer_pango.c --- mlt-0.9.0/src/modules/gtk2/producer_pango.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/producer_pango.c 2014-06-29 20:23:17.000000000 +0000 @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -204,6 +204,11 @@ } else { + // Convert file name string encoding. + mlt_properties_set( properties, "resource", filename ); + mlt_properties_from_utf8( properties, "resource", "_resource" ); + filename = mlt_properties_get( properties, "_resource" ); + FILE *f = fopen( filename, "r" ); if ( f != NULL ) { @@ -231,7 +236,6 @@ if ( markup && markup[ strlen( markup ) - 1 ] == '\n' ) markup[ strlen( markup ) - 1 ] = '\0'; - mlt_properties_set( properties, "resource", filename ); if ( markup ) mlt_properties_set( properties, "markup", markup ); else diff -Nru mlt-0.9.0/src/modules/gtk2/producer_pixbuf.c mlt-0.9.2/src/modules/gtk2/producer_pixbuf.c --- mlt-0.9.0/src/modules/gtk2/producer_pixbuf.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/gtk2/producer_pixbuf.c 2014-06-29 20:23:17.000000000 +0000 @@ -25,8 +25,6 @@ #include #include -#include "config.h" - #ifdef USE_EXIF #include #endif @@ -92,7 +90,7 @@ mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "seekable", 1 ); - mlt_properties_set_int( properties, "loop", 1 ); + mlt_properties_set_int( properties, "loop", 1 ); // Validate the resource if ( filename ) @@ -372,13 +370,13 @@ position += mlt_producer_get_in( producer ); // Image index - int loop = mlt_properties_get_int( producer_props, "loop" ); - int current_idx; - if (loop) { + int loop = mlt_properties_get_int( producer_props, "loop" ); + int current_idx; + if (loop) { current_idx = ( int )floor( ( double )position / ttl ) % self->count; - } else { + } else { current_idx = MIN(( double )position / ttl, self->count - 1); - } + } // Key for the cache char image_key[ 10 ]; @@ -444,9 +442,10 @@ self->image, self->pixbuf, current_idx, self->image_idx, self->pixbuf_idx, width ); // If we have a pixbuf and we need an image - if ( self->pixbuf && ( !self->image || ( format != mlt_image_none && format != self->format ) ) ) + if ( self->pixbuf && ( !self->image || ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) ) ) { char *interps = mlt_properties_get( properties, "rescale.interp" ); + if ( interps ) interps = strdup( interps ); int interp = GDK_INTERP_BILINEAR; if ( !interps ) { @@ -458,6 +457,7 @@ interp = GDK_INTERP_TILES; else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "bicubic" ) == 0 ) interp = GDK_INTERP_HYPER; + if ( interps ) free( interps ); // Note - the original pixbuf is already safe and ready for destruction pthread_mutex_lock( &g_mutex ); @@ -495,7 +495,7 @@ pthread_mutex_unlock( &g_mutex ); // Convert image to requested format - if ( format != mlt_image_none && format != self->format ) + if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) { uint8_t *buffer = NULL; @@ -553,8 +553,10 @@ mlt_producer producer = &self->parent; // Use the width and height suggested by the rescale filter because we can do our own scaling. - *width = mlt_properties_get_int( properties, "rescale_width" ); - *height = mlt_properties_get_int( properties, "rescale_height" ); + if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) + *width = mlt_properties_get_int( properties, "rescale_width" ); + if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) + *height = mlt_properties_get_int( properties, "rescale_height" ); // Restore pixbuf and image mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); diff -Nru mlt-0.9.0/src/modules/jackrack/Makefile mlt-0.9.2/src/modules/jackrack/Makefile --- mlt-0.9.0/src/modules/jackrack/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/jackrack/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -21,18 +21,18 @@ OBJS = factory.o \ consumer_jack.o -CFLAGS += `pkg-config --cflags jack` -LDFLAGS += `pkg-config --libs jack` +CFLAGS += $(shell pkg-config --cflags jack) +LDFLAGS += $(shell pkg-config --libs jack) ifdef GPL OBJS += $(GPL_OBJS) CFLAGS += -DGPL -CFLAGS += `pkg-config --cflags libxml-2.0` -CFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --cflags glib-2.0` +CFLAGS += $(shell pkg-config --cflags libxml-2.0) +CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags glib-2.0) LDFLAGS += $(LIBDL) -LDFLAGS += `pkg-config --libs libxml-2.0` -LDFLAGS += `pkg-config $(PKGCONFIG_PREFIX) --libs glib-2.0` +LDFLAGS += $(shell pkg-config --libs libxml-2.0) +LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs glib-2.0) LDFLAGS += -lm YML_FILES = *.yml diff -Nru mlt-0.9.0/src/modules/jackrack/plugin_mgr.c mlt-0.9.2/src/modules/jackrack/plugin_mgr.c --- mlt-0.9.0/src/modules/jackrack/plugin_mgr.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/jackrack/plugin_mgr.c 2014-06-29 20:23:17.000000000 +0000 @@ -237,10 +237,8 @@ ladspa_path = g_strdup ("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa"); #endif - dir = strtok (ladspa_path, ":"); - do + for (dir = strtok (ladspa_path, ":"); dir; dir = strtok (NULL, ":")) plugin_mgr_get_dir_plugins (plugin_mgr, dir); - while ((dir = strtok (NULL, ":"))); g_free (ladspa_path); } diff -Nru mlt-0.9.0/src/modules/kdenlive/filter_boxblur.c mlt-0.9.2/src/modules/kdenlive/filter_boxblur.c --- mlt-0.9.0/src/modules/kdenlive/filter_boxblur.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kdenlive/filter_boxblur.c 2014-06-29 20:23:17.000000000 +0000 @@ -44,7 +44,7 @@ if (y > 0) pts[z] += rgb[width * -3]; if (x>0 && y>0) pts[z] -= rgb[(width + 1) * -3]; *rgb++ = pts[z]; - } + } image += 3; } } @@ -84,18 +84,38 @@ } } -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + // Get the image *format = mlt_image_rgb24; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); - short hori = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "hori" ); - short vert = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "vert" ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { - double factor = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "boxblur" ); + short hori = mlt_properties_get_int( properties, "hori" ); + short vert = mlt_properties_get_int( properties, "vert" ); + + // Get blur factor + double factor = mlt_properties_get_int( properties, "start" ); + if ( mlt_properties_get( properties, "end" ) ) + { + double end = (double) mlt_properties_get_int( properties, "end" ); + factor += ( end - factor ) * mlt_filter_get_progress( filter, frame ); + } + + // If animated property "blur" is set, use its value. + char* blur_property = mlt_properties_get( properties, "blur" ); + if ( blur_property ) + { + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + factor = mlt_properties_anim_get_double( properties, "blur", position, length ); + } + if ( factor != 0) { int h = *height + 1; @@ -111,25 +131,9 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - // Get the starting blur level - double blur = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "start" ); - short hori = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "hori" ); - short vert = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "vert" ); - - // If there is an end adjust gain to the range - if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL ) - { - // Determine the time position of this frame in the transition duration - double end = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "end" ); - blur += ( end - blur ) * mlt_filter_get_progress( this, frame ); - } - - // Push the frame filter - mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "boxblur", blur ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "hori", hori ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "vert", vert ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -140,15 +144,16 @@ mlt_filter filter_boxblur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "2" : arg); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "hori", arg == NULL ? "1" : arg); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "vert", arg == NULL ? "1" : arg); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "2" : arg); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "hori", "1" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "vert", "1" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "blur", NULL ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/kdenlive/filter_boxblur.yml mlt-0.9.2/src/modules/kdenlive/filter_boxblur.yml --- mlt-0.9.0/src/modules/kdenlive/filter_boxblur.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kdenlive/filter_boxblur.yml 2014-06-29 20:23:17.000000000 +0000 @@ -2,10 +2,60 @@ type: filter identifier: boxblur title: Box Blur -version: 1 +version: 2 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video +parameters: + - identifier: start + title: Size + argument: yes + type: integer + description: > + If an end size is provided, then this is the starting size, and + size is interpolated over the duration of the filter from start + to end size. + If the keyframable property "blur" is provided, then this is ignored, and + "blur" is used instead. + This parameter also affects the size both horizontally and vertically + simultaneously, in amounts proportional to the horizontal size and + vertical size parameters. + unit: pixels + mutable: yes + default: 2 + minimum: 1 + + - identifier: end + title: End size + type: integer + unit: pixels + mutable: yes + minimum: 1 + + - identifier: blur + title: Size + type: integer + description: > + If this value is set the start and end parameters are ignored. + unit: pixels + mutable: yes + minimum: 1 + + - identifier: hori + title: Horizontal size + type: integer + unit: pixels + mutable: yes + minimum: 1 + default: 1 + + - identifier: vert + title: Vertical size + type: integer + unit: pixels + mutable: yes + minimum: 1 + default: 1 diff -Nru mlt-0.9.0/src/modules/kdenlive/filter_wave.c mlt-0.9.2/src/modules/kdenlive/filter_wave.c --- mlt-0.9.0/src/modules/kdenlive/filter_wave.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kdenlive/filter_wave.c 2014-06-29 20:23:17.000000000 +0000 @@ -62,7 +62,8 @@ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - mlt_properties unique = mlt_frame_pop_service( frame ); + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_frame_get_position( frame ); // Get the image @@ -72,11 +73,30 @@ // Only process if we have no error and a valid colour space if ( error == 0 ) { - double factor = mlt_properties_get_int( unique, "wave" ); - int speed = mlt_properties_get_int( unique, "speed" ); - int deformX = mlt_properties_get_int( unique, "deformX" ); - int deformY = mlt_properties_get_int( unique, "deformY" ); - if (factor != 0) { + double factor = mlt_properties_get_double( properties, "start" ); + + mlt_position f_pos = mlt_filter_get_position( filter, frame ); + mlt_position f_len = mlt_filter_get_length2( filter, frame ); + int speed = mlt_properties_anim_get_int( properties, "speed", f_pos, f_len ); + int deformX = mlt_properties_anim_get_int( properties, "deformX", f_pos, f_len ); + int deformY = mlt_properties_anim_get_int( properties, "deformY", f_pos, f_len ); + + if ( mlt_properties_get( properties, "end" ) ) + { + // Determine the time position of this frame in the transition duration + double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "end" ) ); + factor += ( end - factor ) * mlt_filter_get_progress( filter, frame ); + } + + // If animated property "wave" is set, use its value. + char* wave_property = mlt_properties_get( properties, "wave" ); + if ( wave_property ) + { + factor = mlt_properties_anim_get_double( properties, "wave", f_pos, f_len ); + } + + if (factor != 0) + { int image_size = *width * (*height) * 2; uint8_t *dst = mlt_pool_alloc (image_size); DoWave(*image, *width, (*height), dst, position, speed, factor, deformX, deformY); @@ -93,27 +113,7 @@ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - // Get the starting wave level - double wave = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "start" ); - int speed = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "speed" ); - int deformX = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "deformX" ); - int deformY = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "deformY" ); - - // If there is an end adjust gain to the range - if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "end" ) != NULL ) - { - // Determine the time position of this frame in the transition duration - double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "end" ) ); - wave += ( end - wave ) * mlt_filter_get_progress( filter, frame ); - } - - // Push the frame filter - mlt_properties unique = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); - mlt_properties_set_double( unique, "wave", wave ); - mlt_properties_set_int( unique, "speed", speed ); - mlt_properties_set_int( unique, "deformX", deformX ); - mlt_properties_set_int( unique, "deformY", deformY ); - mlt_frame_push_service( frame, unique ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -129,12 +129,11 @@ { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "10" : arg); - mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "speed", arg == NULL ? "5" : arg); - mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformX", arg == NULL ? "1" : arg); - mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformY", arg == NULL ? "1" : arg); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "speed", "5"); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformX", "1"); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformY", "1"); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "wave", NULL); } return filter; } - - diff -Nru mlt-0.9.0/src/modules/kdenlive/filter_wave.yml mlt-0.9.2/src/modules/kdenlive/filter_wave.yml --- mlt-0.9.0/src/modules/kdenlive/filter_wave.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kdenlive/filter_wave.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,11 +1,58 @@ -schema_version: 0.1 +schema_version: 0.2 type: filter identifier: wave title: Wave -version: 1 +version: 2 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video +parameters: + - identifier: start + title: Amplitude + argument: yes + type: integer + description: > + If an end amplitude is provided, then this is the starting amplitude, and + amplitude is interpolated over the duration of the filter from start + to end amplitude. + If the keyframable property "wave" is provided, then this is ignored, and + "wave" is used instead. + This parameter also affects the pulsation period and the phase length. + mutable: yes + default: 10 + minimum: 1 + + - identifier: end + title: End amplitude + type: integer + mutable: yes + minimum: 1 + + - identifier: wave + title: Amplitude + type: integer + description: > + If this value is set the start and end parameters are ignored. + mutable: yes + minimum: 1 + + - identifier: speed + title: Speed + type: integer + mutable: yes + default: 5 + + - identifier: deformX + title: Deform horizontally? + type: boolean + mutable: yes + default: 1 + + - identifier: deformY + title: Deform vertically? + type: boolean + mutable: yes + default: 1 diff -Nru mlt-0.9.0/src/modules/kdenlive/producer_framebuffer.c mlt-0.9.2/src/modules/kdenlive/producer_framebuffer.c --- mlt-0.9.0/src/modules/kdenlive/producer_framebuffer.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kdenlive/producer_framebuffer.c 2014-06-29 20:23:17.000000000 +0000 @@ -83,6 +83,11 @@ } } + if ( *format == mlt_image_none ) + { + // set format to the original's producer format + *format = (mlt_image_format) mlt_properties_get_int( properties, "_original_format" ); + } // Determine output buffer size *width = mlt_properties_get_int( frame_properties, "width" ); *height = mlt_properties_get_int( frame_properties, "height" ); @@ -226,8 +231,21 @@ // Get the frame mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index ); + // Cache the frame mlt_properties_set_data( properties, "first_frame", first_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); + + // Find the original producer's format + int width = 0; + int height = 0; + mlt_image_format format = mlt_image_none; + uint8_t *image = NULL; + int error = mlt_frame_get_image( first_frame, &image, &format, &width, &height, 0 ); + if ( !error ) + { + // cache the original producer's pixel format + mlt_properties_set_int( properties, "_original_format", (int) format ); + } } mlt_properties_inherit( frame_properties, MLT_FRAME_PROPERTIES(first_frame) ); diff -Nru mlt-0.9.0/src/modules/kino/avi.cc mlt-0.9.2/src/modules/kino/avi.cc --- mlt-0.9.0/src/modules/kino/avi.cc 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/avi.cc 2014-06-29 20:23:17.000000000 +0000 @@ -17,8 +17,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "config.h" - // C++ includes #include diff -Nru mlt-0.9.0/src/modules/kino/configure mlt-0.9.2/src/modules/kino/configure --- mlt-0.9.0/src/modules/kino/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/configure 2014-06-29 20:23:17.000000000 +0000 @@ -17,14 +17,22 @@ pkg-config libdv 2> /dev/null libdv_disabled=$? - echo > config.h - [ "$lqt_disabled" = "0" ] && echo "#define HAVE_LIBQUICKTIME" >> config.h - [ "$libdv_disabled" = "0" ] && echo "#define HAVE_LIBDV" >> config.h echo > config.mak - [ "$lqt_disabled" = "0" ] && echo "HAVE_LIBQUICKTIME=1" >> config.mak - [ "$libdv_disabled" = "0" ] && echo "HAVE_LIBDV=1" >> config.mak + + if [ "$lqt_disabled" = "0" ] + then + echo "CFLAGS += -DHAVE_LIBQUICKTIME" >> config.mak + echo "HAVE_LIBQUICKTIME=1" >> config.mak + else + echo "- libquicktime not found: only enabling dv avi support" + fi + + if [ "$libdv_disabled" = "0" ] + then + echo "CFLAGS += -DHAVE_LIBDV" >> config.mak + echo "HAVE_LIBDV=1" >> config.mak + fi - [ "$lqt_disabled" != "0" ] && echo "- libquicktime not found: only enabling dv avi support" [ "$libdv_disabled" != "0" -a "$lqt_disabled" = "0" ] && echo "- libdv not found: mov dv may not have audio" exit 0 diff -Nru mlt-0.9.0/src/modules/kino/filehandler.cc mlt-0.9.2/src/modules/kino/filehandler.cc --- mlt-0.9.0/src/modules/kino/filehandler.cc 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/filehandler.cc 2014-06-29 20:23:17.000000000 +0000 @@ -17,8 +17,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "config.h" - extern "C" { #include } diff -Nru mlt-0.9.0/src/modules/kino/kino_wrapper.cc mlt-0.9.2/src/modules/kino/kino_wrapper.cc --- mlt-0.9.0/src/modules/kino/kino_wrapper.cc 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/kino_wrapper.cc 2014-06-29 20:23:17.000000000 +0000 @@ -18,8 +18,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "config.h" - #include #include diff -Nru mlt-0.9.0/src/modules/kino/Makefile mlt-0.9.2/src/modules/kino/Makefile --- mlt-0.9.0/src/modules/kino/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -1,11 +1,11 @@ -CFLAGS += -I../../ -CXXFLAGS += $(CFLAGS) -Wno-deprecated - -LDFLAGS += -L../../framework -lmlt -lpthread - include ../../../config.mak include config.mak +CFLAGS := -I../../ $(CFLAGS) +CXXFLAGS := $(CFLAGS) -Wno-deprecated $(CXXFLAGS) + +LDFLAGS := -L../../framework -lmlt -lpthread $(LDFLAGS) + TARGET = ../libmltkino.so OBJS = factory.o producer_kino.o diff -Nru mlt-0.9.0/src/modules/kino/riff.cc mlt-0.9.2/src/modules/kino/riff.cc --- mlt-0.9.0/src/modules/kino/riff.cc 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/kino/riff.cc 2014-06-29 20:23:17.000000000 +0000 @@ -17,8 +17,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "config.h" - // C++ includes #include diff -Nru mlt-0.9.0/src/modules/linsys/consumer_SDIstream.c mlt-0.9.2/src/modules/linsys/consumer_SDIstream.c --- mlt-0.9.0/src/modules/linsys/consumer_SDIstream.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/linsys/consumer_SDIstream.c 2014-06-29 20:23:17.000000000 +0000 @@ -428,7 +428,7 @@ mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-fatal-error", NULL ); } - uint8_t *video_buffer; + uint8_t *video_buffer = NULL; int16_t *audio_buffer_tmp; // the upstream audio buffer // Loop until told not to diff -Nru mlt-0.9.0/src/modules/lumas/Makefile mlt-0.9.2/src/modules/lumas/Makefile --- mlt-0.9.0/src/modules/lumas/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/lumas/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -5,6 +5,12 @@ @./create_lumas luma: luma.c +# When cross-compiling, use the host OS compiler to build the luma +# binary because the files are generated at build time. +# Strips the CROSS prefix from the C compiler variable. +ifdef CROSS + $(subst $(CROSS),,$(CC)) -o $@ luma.c +endif create_lumas: diff -Nru mlt-0.9.0/src/modules/Makefile mlt-0.9.2/src/modules/Makefile --- mlt-0.9.0/src/modules/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -4,7 +4,7 @@ all clean depend: list='$(SUBDIRS)'; \ for subdir in $$list; do \ - if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \ + if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) -C $$subdir $@ || exit 1; \ fi \ done @@ -13,7 +13,7 @@ rm -f consumers.dat filters.dat producers.dat transitions.dat make.inc; \ list='$(SUBDIRS)'; \ for subdir in $$list; do \ - if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \ + if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) -C $$subdir $@ || exit 1; \ fi \ done @@ -21,7 +21,7 @@ install: list='$(SUBDIRS)'; \ for subdir in $$list; do \ - if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \ + if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \ fi \ done diff -Nru mlt-0.9.0/src/modules/motion_est/filter_motion_est.c mlt-0.9.2/src/modules/motion_est/filter_motion_est.c --- mlt-0.9.0/src/modules/motion_est/filter_motion_est.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/motion_est/filter_motion_est.c 2014-06-29 20:23:17.000000000 +0000 @@ -904,6 +904,7 @@ bounds = calloc( 1, sizeof(*bounds) ); mlt_properties_set_data( MLT_FILTER_PROPERTIES(filter), "bounds", bounds, sizeof(*bounds), free, NULL ); mlt_geometry_fetch( geometry, bounds, 0 ); + mlt_geometry_close( geometry ); } } } diff -Nru mlt-0.9.0/src/modules/normalize/filter_audiolevel.c mlt-0.9.2/src/modules/normalize/filter_audiolevel.c --- mlt-0.9.0/src/modules/normalize/filter_audiolevel.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/normalize/filter_audiolevel.c 2014-06-29 20:23:17.000000000 +0000 @@ -92,7 +92,7 @@ level = 41.0/42.0; } // max amplitude = 40/42, 3to10 oversamples=41, more then 10 oversamples=42 - if ( level == 0.0 ) + if ( level == 0.0 && num_samples > 0 ) level = val / num_samples * 40.0/42.0 / 127.0; if ( iec_scale ) level = IEC_Scale( AMPTODBFS( level ) ); diff -Nru mlt-0.9.0/src/modules/normalize/filter_volume.c mlt-0.9.2/src/modules/normalize/filter_volume.c --- mlt-0.9.0/src/modules/normalize/filter_volume.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/normalize/filter_volume.c 2014-06-29 20:23:17.000000000 +0000 @@ -74,9 +74,6 @@ else if (x > lmtr_lvl) xp = tanh((x - lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) + lmtr_lvl; -// if ( x != xp ) -// fprintf( stderr, "filter_volume: sample %f limited %f\n", x, xp ); - return xp; } @@ -101,7 +98,6 @@ } } if (j) smoothed /= j; -// fprintf( stderr, "smoothed over %d values, result %f\n", j, smoothed ); return smoothed; } @@ -166,13 +162,13 @@ static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the filter from the frame - mlt_filter this = mlt_frame_pop_audio( frame ); + mlt_filter filter = mlt_frame_pop_audio( frame ); // Get the properties from the filter - mlt_properties filter_props = MLT_FILTER_PROPERTIES( this ); + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); // Get the frame's filter instance properties - mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( this ) ); + mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); // Get the parameters double gain = mlt_properties_get_double( instance_props, "gain" ); @@ -183,6 +179,15 @@ int i, j; double sample; int16_t peak; + + // Use animated value for gain if "level" property is set + char* level_property = mlt_properties_get( filter_props, "level" ); + if ( level_property != NULL ) + { + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + gain = mlt_properties_anim_get_double( filter_props, "level", position, length ); + } if ( mlt_properties_get( instance_props, "limiter" ) != NULL ) limiter_level = mlt_properties_get_double( instance_props, "limiter" ); @@ -190,14 +195,13 @@ // Get the producer's audio *format = mlt_audio_s16; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); -// fprintf( stderr, "filter_volume: frequency %d\n", *frequency ); // Determine numeric limits int bytes_per_samp = (samp_width - 1) / 8 + 1; int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1; int samplemin = -samplemax - 1; - mlt_service_lock( MLT_FILTER_SERVICE( this ) ); + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if ( normalise ) { @@ -210,13 +214,12 @@ // Compute the signal power and put into smoothing buffer smooth_buffer[ smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak ); -// fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ smooth_index ] ); + if ( smooth_buffer[ smooth_index ] > EPSILON ) { mlt_properties_set_int( filter_props, "_smooth_index", ( smooth_index + 1 ) % window ); // Smooth the data and compute the gain -// fprintf( stderr, "smoothed %f over %d frames\n", get_smoothed_data( smooth_buffer, window ), window ); gain *= amplitude / get_smoothed_data( smooth_buffer, window ); } } @@ -225,9 +228,6 @@ gain *= amplitude / signal_max_power( (int16_t*) *buffer, *channels, *samples, &peak ); } } - -// if ( gain > 1.0 && normalise ) -// fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain ); if ( max_gain > 0 && gain > max_gain ) gain = max_gain; @@ -244,13 +244,12 @@ // Determine ramp increment double gain_step = ( gain - previous_gain ) / *samples; -// fprintf( stderr, "filter_volume: previous gain %f current gain %f step %f\n", previous_gain, gain, gain_step ); // Save the current gain for the next iteration mlt_properties_set_double( filter_props, "_previous_gain", gain ); mlt_properties_set_position( filter_props, "_last_position", current_position ); - mlt_service_unlock( MLT_FILTER_SERVICE( this ) ); + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Ramp from the previous gain to the current gain = previous_gain; @@ -288,10 +287,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_properties filter_props = MLT_FILTER_PROPERTIES( this ); - mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( this ) ); + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); double gain = 1.0; // no adjustment @@ -334,7 +333,7 @@ end = fabs( end ); if ( end != -1 ) - gain += ( end - gain ) * mlt_filter_get_progress( this, frame ); + gain += ( end - gain ) * mlt_filter_get_progress( filter, frame ); } } } @@ -413,7 +412,7 @@ // If there is an end adjust gain to the range if ( mlt_properties_get( filter_props, "end" ) != NULL ) { - amplitude *= mlt_filter_get_progress( this, frame ); + amplitude *= mlt_filter_get_progress( filter, frame ); } mlt_properties_set_int( instance_props, "normalise", 1 ); mlt_properties_set_double( instance_props, "amplitude", amplitude ); @@ -432,7 +431,7 @@ } // Push the filter onto the stack - mlt_frame_push_audio( frame, this ); + mlt_frame_push_audio( frame, filter ); // Override the get_audio method mlt_frame_push_audio( frame, filter_get_audio ); @@ -445,16 +444,18 @@ mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) ); - if ( this != NULL && mlt_filter_init( this, NULL ) == 0 ) + mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); + if ( filter != NULL && mlt_filter_init( filter, NULL ) == 0 ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( this ); - this->process = filter_process; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + filter->process = filter_process; if ( arg != NULL ) mlt_properties_set( properties, "gain", arg ); mlt_properties_set_int( properties, "window", 75 ); mlt_properties_set( properties, "max_gain", "20dB" ); + + mlt_properties_set( properties, "level", NULL ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/normalize/filter_volume.yml mlt-0.9.2/src/modules/normalize/filter_volume.yml --- mlt-0.9.0/src/modules/normalize/filter_volume.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/normalize/filter_volume.yml 2014-06-29 20:23:17.000000000 +0000 @@ -2,7 +2,7 @@ type: filter identifier: volume title: Volume -version: 1 +version: 2 copyright: Ushodaya Enterprises Limited creator: Dan Denneedy license: GPLv2 @@ -25,6 +25,8 @@ The gain may also be set to "normalise" to normalise the volume to the target amplitude -12dBFS. + + This value is discarded if value for property "level" is set. - identifier: window title: Window type: integer @@ -72,4 +74,21 @@ description: > A gain value just like the Gain property. This causes the gain to be interpolated from 'gain' to 'end' over the duration. + + This value is discarded if value for property "level" is set. + mutable: yes + - identifier: max_gain + title: Max gain + type: string + description: > + A floating point or decibel value of the maximum gain that can be + applied during normalisation. + default: 20dB + mutable: yes + - identifier: level + title: Level + type: float + description: > + The animated floating point value of the gain + adjustment. Properties "gain" and "end" are discarded if this is set. mutable: yes diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_dust.c mlt-0.9.2/src/modules/oldfilm/filter_dust.c --- mlt-0.9.0/src/modules/oldfilm/filter_dust.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_dust.c 2014-06-29 20:23:17.000000000 +0000 @@ -17,8 +17,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -//#include -//#include #include #include @@ -27,31 +25,36 @@ #include #include -static void overlay_image(uint8_t *src, int src_width, int src_height , uint8_t * overlay, int overlay_width, int overlay_height, uint8_t * alpha , int xpos, int ypos, int upsidedown , int mirror ){ +static void overlay_image(uint8_t *src, int src_width, int src_height , uint8_t *overlay, int overlay_width, int overlay_height, uint8_t * alpha , int xpos, int ypos, int upsidedown , int mirror ) +{ int x,y; - for (y=ypos;y=0 && (y-ypos)= 0 && (y - ypos) < overlay_height ) + { + uint8_t *scanline_image = src + src_width * y * 2; int overlay_y = upsidedown ? ( overlay_height - ( y - ypos ) - 1 ) : ( y - ypos ); uint8_t *scanline_overlay=overlay + overlay_width * 2 * overlay_y; - for ( x = xpos ; x < src_width && x-xpos < overlay_width ;x++){ - if ( x>0 ){ + for ( x = xpos ; x < src_width && x - xpos < overlay_width ;x++ ) + { + if ( x > 0 ) + { int overlay_x = mirror ? overlay_width - ( x - xpos ) -1 : ( x - xpos ); - double alp=(double)*(alpha+ overlay_width * overlay_y + overlay_x )/255.0; + double alp = (double) * (alpha + overlay_width * overlay_y + overlay_x ) / 255.0; uint8_t* image_pixel = scanline_image + x * 2; uint8_t* overlay_pixel = scanline_overlay + overlay_x * 2; - *image_pixel=(double)(*overlay_pixel)*alp+ (double)*image_pixel*(1.0-alp) ; - if (xpos%2==0) + *image_pixel = (double)(*overlay_pixel) * alp + (double) *image_pixel * (1.0-alp) ; + if ( xpos % 2 == 0 ) image_pixel++; else - image_pixel+=3; + image_pixel += 3; - mirror? overlay_pixel-- : overlay_pixel++; + mirror ? overlay_pixel-- : overlay_pixel++; - *image_pixel=(double)(*(overlay_pixel))*alp + (double)(*image_pixel )*(1.0-alp) ; + *image_pixel = (double) (*(overlay_pixel)) * alp + (double)(*image_pixel) * ( 1.0 - alp ); } } @@ -59,58 +62,62 @@ } } -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position pos = mlt_filter_get_position( filter, frame ); + mlt_position len = mlt_filter_get_length2( filter, frame ); - int maxdia = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxdiameter" ); - int maxcount = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxcount" ); + int maxdia = mlt_properties_anim_get_int( properties, "maxdiameter", pos, len ); + int maxcount = mlt_properties_anim_get_int( properties, "maxcount", pos, len ); *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); - // load svg - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + + // Load svg char *factory = mlt_properties_get( properties, "factory" ); - char temp[1204]=""; + char temp[1204] = ""; sprintf( temp, "%s/oldfilm/", mlt_environment( "MLT_DATA" ) ); - mlt_properties direntries=mlt_properties_new(); - mlt_properties_dir_list(direntries,temp,"dust*.svg",1); + mlt_properties direntries = mlt_properties_new(); + mlt_properties_dir_list( direntries, temp,"dust*.svg",1 ); if (!maxcount) return 0; - double position = mlt_filter_get_progress( filter, this ); - srand(position*10000); + double position = mlt_filter_get_progress( filter, frame ); + srand( position * 10000 ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); - int im=rand()%maxcount; - int piccount=mlt_properties_count(direntries); - while (im-- && piccount){ - - int picnum=rand()%piccount; + int im = rand() % maxcount; + int piccount = mlt_properties_count( direntries ); + while ( im-- && piccount ) + { + int picnum = rand() % piccount; - int y1=rand()%*height; - int x1=rand()%*width; - char resource[1024]=""; - char savename[1024]="",savename1[1024]="", cachedy[100]; - int dx=(*width*maxdia/100); - int luma_width,luma_height; + int y1 = rand() % *height; + int x1 = rand() % *width; + char resource[1024] = ""; + char savename[1024] = "", savename1[1024] = "", cachedy[100]; + int dx = ( *width * maxdia / 100); + int luma_width, luma_height; uint8_t *luma_image = NULL; - uint8_t *alpha =NULL; - int updown= rand()%2; - int mirror=rand()%2; - - sprintf(resource,"%s",mlt_properties_get_value(direntries,picnum)); - sprintf(savename,"cache-%d-%d",picnum,dx); - sprintf(savename1,"cache-alpha-%d-%d",picnum,dx); - sprintf(cachedy,"cache-dy-%d-%d",picnum,dx); + uint8_t *alpha = NULL; + int updown = rand() % 2; + int mirror = rand() % 2; + + sprintf( resource, "%s", mlt_properties_get_value(direntries,picnum) ); + sprintf( savename, "cache-%d-%d", picnum,dx ); + sprintf( savename1, "cache-alpha-%d-%d", picnum, dx ); + sprintf( cachedy, "cache-dy-%d-%d", picnum,dx ); - luma_image= mlt_properties_get_data( properties , savename , NULL ); - alpha= mlt_properties_get_data( properties , savename1 , NULL ); + luma_image = mlt_properties_get_data( properties , savename , NULL ); + alpha = mlt_properties_get_data( properties , savename1 , NULL ); - if (luma_image == NULL || alpha == NULL ){ + if ( luma_image == NULL || alpha == NULL ) + { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); mlt_producer producer = mlt_factory_producer( profile, factory, resource ); @@ -121,8 +128,8 @@ mlt_properties_set( producer_properties, "eof", "loop" ); mlt_frame luma_frame = NULL; - if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 ){ - + if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 ) + { mlt_image_format luma_format = mlt_image_yuv422; luma_width = dx; luma_height = luma_width * mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "height" ) / mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "width" ); @@ -130,27 +137,30 @@ mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "best" );// none/nearest/tiles/hyper mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 ); - alpha =mlt_frame_get_alpha_mask(luma_frame); + alpha = mlt_frame_get_alpha_mask (luma_frame ); - uint8_t* savealpha=mlt_pool_alloc ( luma_width * luma_height ); - uint8_t* savepic=mlt_pool_alloc ( luma_width * luma_height * 2); + uint8_t* savealpha = mlt_pool_alloc( luma_width * luma_height ); + uint8_t* savepic = mlt_pool_alloc( luma_width * luma_height * 2); - if (savealpha && savepic ){ - memcpy (savealpha, alpha , luma_width * luma_height ); - memcpy (savepic, luma_image , luma_width * luma_height * 2); + if ( savealpha && savepic ) + { + memcpy( savealpha, alpha , luma_width * luma_height ); + memcpy( savepic, luma_image , luma_width * luma_height * 2 ); - mlt_properties_set_data ( properties , savename , savepic , sizeof(savepic) , mlt_pool_release, NULL ); - mlt_properties_set_data ( properties , savename1 , savealpha , sizeof(savealpha) , mlt_pool_release, NULL ); - mlt_properties_set_int ( properties , cachedy , luma_height ); + mlt_properties_set_data( properties, savename, savepic, sizeof(savepic), mlt_pool_release, NULL ); + mlt_properties_set_data( properties, savename1, savealpha, sizeof(savealpha), mlt_pool_release, NULL ); + mlt_properties_set_int( properties, cachedy, luma_height ); - overlay_image(*image,*width,*height,luma_image,luma_width,luma_height, alpha, x1, y1 , updown , mirror ); + overlay_image( *image, *width, *height, luma_image, luma_width, luma_height, alpha, x1, y1, updown, mirror ); } mlt_frame_close( luma_frame ); } mlt_producer_close( producer ); } - }else { - overlay_image ( *image , *width, *height , luma_image , dx , mlt_properties_get_int ( properties , cachedy ) , alpha , x1 , y1 , updown , mirror ); + } + else + { + overlay_image ( *image, *width, *height, luma_image, dx, mlt_properties_get_int ( properties, cachedy ), alpha, x1, y1, updown, mirror ); } } @@ -163,61 +173,64 @@ int h = *height; int w = *width; - int im=rand()%maxcount; + int im = rand() % maxcount; - while (im-- ){ - int type=im%2; - int y1=rand()%h; - int x1=rand()%w; - int dx=rand()%maxdia; - int dy=rand()%maxdia; - int x=0,y=0;//,v=0; - double v=0.0; + while ( im-- ) + { + int type = im % 2; + int y1 = rand() % h; + int x1 = rand() % w; + int dx = rand() % maxdia; + int dy = rand() % maxdia; + int x=0, y=0; + double v = 0.0; for ( x = -dx ; x < dx ; x++ ) - for ( y = -dy ; y < dy ; y++ ) { - if ( x1+x < w && x1+x > 0 && y1+y < h && y1+y > 0 ){ - uint8_t *pix=*image+(y+y1)*w*2+(x+x1)*2; - //v=(1.0-fabs(x)/dx)*(1.0-fabs(y)/dy); - v=pow((double)x/(double)dx*5.0,2.0)+pow((double)y/(double)dy*5.0,2.0); + { + for ( y = -dy ; y < dy ; y++ ) + { + if ( x1 + x < w && x1 + x > 0 && y1 + y < h && y1 + y > 0 ){ + uint8_t *pix = *image + (y+y1) * w * 2 + (x + x1) * 2; + + v=pow((double) x /(double)dx * 5.0, 2.0) + pow((double)y / (double)dy * 5.0, 2.0); if (v>10) v=10; - v=1.0-(v/10.0); + v = 1.0 - ( v / 10.0 ); - switch(type){ + switch(type) + { case 0: - *pix-=(*pix)*v; + *pix -= (*pix) * v; break; case 1: - *pix+=(255-*pix)*v; + *pix += ( 255-*pix ) * v; break; } } } + } } } return error; } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } - mlt_filter filter_dust_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxdiameter", "2" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxcount", "10" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "maxdiameter", "2" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "maxcount", "10" ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_grain.c mlt-0.9.2/src/modules/oldfilm/filter_grain.c --- mlt-0.9.0/src/modules/oldfilm/filter_grain.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_grain.c 2014-06-29 20:23:17.000000000 +0000 @@ -19,67 +19,72 @@ #include #include - #include #include #include + #define MIN(a,b) (ab?a:b) -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position pos = mlt_filter_get_position( filter, frame ); + mlt_position len = mlt_filter_get_length2( filter, frame ); - mlt_filter filter = mlt_frame_pop_service( this ); *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); - + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + if ( error == 0 && *image ) { int h = *height; int w = *width; - - double position = mlt_filter_get_progress( filter, this ); + + double position = mlt_filter_get_progress( filter, frame ); srand(position*10000); + + int noise = mlt_properties_anim_get_int( properties, "noise", pos, len ); + double contrast = mlt_properties_anim_get_double( properties, "contrast", pos, len ) / 100.0; + double brightness = 127.0 * (mlt_properties_anim_get_double( properties, "brightness", pos, len ) -100.0 ) / 100.0; - int noise = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "noise" ); - double contrast = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "contrast" )/100.0; - double brightness = 127.0 * (mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "brightness" )-100.0)/100.0; - - int x=0,y=0,pix=0; - for (x=0;x20){ - pix= MIN ( MAX ( ( (double)*pixel -127.0 ) * contrast + 127.0 + brightness , 0 ) , 255 ) ; - if (noise>0) - pix-=(rand()%noise-noise); - - *pixel= MIN ( MAX ( pix , 0 ) , 255 ); + int x = 0,y = 0,pix = 0; + for ( x = 0; x < w; x++ ) + { + for( y = 0; y < h; y++ ) + { + uint8_t* pixel = (*image + (y) * w * 2 + (x) * 2 ); + if (*pixel > 20) + { + pix = MIN ( MAX ( ( (double)*pixel -127.0 ) * contrast + 127.0 + brightness , 0 ) , 255 ) ; + if ( noise > 0 ) pix -= ( rand() % noise - noise ); + + *pixel = MIN ( MAX ( pix , 0 ) , 255 ); } - //*(pixel+1)= MIN ( MAX ( ( (double)*(pixel+1) -127.0 ) * .5 + 127.0 , 0 ) , 255 ) ; } + } } return error; } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } mlt_filter filter_grain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "noise", "40" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "contrast", "160" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightness", "70" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "noise", "40" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "contrast", "160" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightness", "70" ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_lines.c mlt-0.9.2/src/modules/oldfilm/filter_lines.c --- mlt-0.9.0/src/modules/oldfilm/filter_lines.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_lines.c 2014-06-29 20:23:17.000000000 +0000 @@ -24,113 +24,125 @@ #include #include -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position pos = mlt_filter_get_position( filter, frame ); + mlt_position len = mlt_filter_get_length2( filter, frame ); - mlt_filter filter = mlt_frame_pop_service( this ); *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { int h = *height; int w = *width; - int line_width = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "line_width" ); - int num = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "num" ); - double maxdarker= (double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "darker" ) ; - double maxlighter=(double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "lighter" ) ; - //int frame = mlt_properties_get_int( this, "_position" ); + int line_width = mlt_properties_anim_get_int( properties, "line_width", pos, len ); + int num = mlt_properties_anim_get_int( properties, "num", pos, len ); + double maxdarker = (double) mlt_properties_anim_get_int( properties, "darker", pos, len ); + double maxlighter = (double) mlt_properties_anim_get_int( properties, "lighter", pos, len ); + char buf[256]; char typebuf[256]; if ( line_width < 1 ) return 0; - double position = mlt_filter_get_progress( filter, this ); + double position = mlt_filter_get_progress( filter, frame ); srand(position*10000); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); - while (num--){ - int type=(rand()%3)+1; - int x1=(double)w*rand()/RAND_MAX; - int dx=rand()%line_width; - int x=0,y=0; - int ystart=rand()%h; - int yend=rand()%h; - - sprintf(buf,"line%d",num); - sprintf(typebuf,"typeline%d",num); - maxlighter+=rand()%30-15; - maxdarker+=rand()%30-15; - - if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf)==0){ - mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1); + while ( num-- ) + { + int type = (rand() % 3 ) + 1; + int x1 = (double)w * rand() / RAND_MAX; + int dx = rand() % line_width; + int x = 0, y = 0; + int ystart = rand() % h; + int yend = rand() % h; + + sprintf( buf, "line%d", num); + sprintf( typebuf, "typeline%d", num); + maxlighter += rand() % 30 -15; + maxdarker += rand() % 30 -15; + + if ( mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf ) ==0 ) + { + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), buf, x1 ); } - - if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf)==0 ){ + + if ( mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf)==0 ) + { mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),typebuf,type); } - x1=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf); - type=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf); - if (position!=mlt_properties_get_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos")){ - x1+=(rand()%11-5); + x1 = mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ), buf ); + type = mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ), typebuf ); + if ( position != mlt_properties_get_double(MLT_FILTER_PROPERTIES( filter ), "last_oldfilm_line_pos")) + { + x1 += (rand() % 11 - 5); } - - if (yend0){ - uint8_t* pixel=(*image+(y)*w*2+(x+x1)*2); - double diff=1.0-fabs(x)/dx; - switch(type){ + + for ( x = -dx ; x < dx && dx != 0 ; x++ ) + { + for( y = ystart; y < yend; y++ ) + { + if ( x + x1 < w && x + x1 > 0) + { + uint8_t* pixel = (*image + (y) * w * 2 + ( x + x1) * 2); + double diff = 1.0 - fabs(x) / dx; + switch( type ) + { case 1: //blackline - *pixel-=((double)*pixel*diff*maxdarker/100.0); + *pixel -= ((double) * pixel * diff * maxdarker / 100.0); break; case 2: //whiteline - *pixel+=((255.0-(double)*pixel)*diff*maxlighter/100.0); + *pixel += ((255.0-(double)*pixel) * diff * maxlighter /100.0); break; case 3: //greenline - *(pixel+1)-=((*(pixel+1))*diff*maxlighter/100.0); + *(pixel+1) -= ((*(pixel+1)) * diff * maxlighter / 100.0); break; } + } } - mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1); + } + mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf , x1); } - mlt_properties_set_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos",position); + mlt_properties_set_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos", position); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } mlt_filter filter_lines_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "line_width", 2 ); - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "num", 5 ); - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "darker" , 40 ) ; - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "lighter" , 40 ) ; + filter->process = filter_process; + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "line_width", 2 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "num", 5 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "darker" , 40 ) ; + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "lighter" , 40 ) ; } - return this; + return filter; } - diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_oldfilm.c mlt-0.9.2/src/modules/oldfilm/filter_oldfilm.c --- mlt-0.9.0/src/modules/oldfilm/filter_oldfilm.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_oldfilm.c 2014-06-29 20:23:17.000000000 +0000 @@ -23,144 +23,147 @@ #include #include #include -static double sinarr[]={ -// 0.0,0.125270029508395,0.2485664757507,0.3679468485397,0.481530353985902, -// 0.587527525713892,0.684268417247276,0.770228911401552,0.84405473219009,0.904582780944473, -// 0.95085946050647,0.982155698800724,0.997978435097294,0.99807838800221,0.982453982794196, -// 0.951351376233828,0.90526057845426,0.844907733031696,0.771243676860277,0.685428960066342, -// 0.588815561967795,0.48292559113694,0.369427305139443,0.250108827749629,0.12684997771773, -// 0.00159265291648683,-0.123689763546002,-0.247023493251739,-0.366465458626247,-0.48013389541149, -// -0.586237999170027,-0.683106138750633,-0.769212192222595,-0.84319959036574,-0.903902688919827, -// -0.950365132881376,-0.981854923525203,-0.997875950775248,-0.998175809236459,-0.982749774749007, -// -0.951840878815686,-0.905936079729926,-0.845758590726883,-0.772256486024771,-0.68658776426406, -// -0.590102104664575,-0.484319603325524,-0.37090682467023,-0.251650545336281,-0.128429604166398,0.0 -0.0,0.0627587292804297,0.125270029508395,0.18728744713136,0.2485664757507,0.308865520098932,0.3679468485397,0.425577530335206,0.481530353985902,0.535584723021826,0.587527525713892,0.637153975276265,0.684268417247276,0.728685100865749,0.770228911401552,0.80873606055313,0.84405473219009,0.876045680894979,0.904582780944473,0.929553523565587,0.95085946050647,0.968416592172968,0.982155698800724,0.99202261335714,0.997978435097294,0.999999682931835,0.99807838800221,0.992222125098244,0.982453982794196,0.968812472421035,0.951351376233828,0.930139535372831,0.90526057845426,0.876812591860795,0.844907733031696,0.809671788277164,0.771243676860277,0.72977490330168,0.685428960066342,0.638380682987321,0.588815561967795,0.536929009678953,0.48292559113694,0.427018217196276,0.369427305139443,0.310379909672042,0.250108827749629,0.188851680765468,0.12684997771773,0.064348163049637,0.00159265291648683,-0.0611691363208864,-0.123689763546002,-0.18572273843423,-0.247023493251739,-0.307350347074556,-0.366465458626247,-0.424135763977612,-0.48013389541149,-0.534239077829989,-0.586237999170027,-0.635925651395529,-0.683106138750633,-0.727593450087328,-0.769212192222595,-0.807798281433749,-0.84319959036574,-0.875276547799941,-0.903902688919827,-0.928965153904073,-0.950365132881376,-0.968018255492714,-0.981854923525203,-0.991820585306115,-0.997875950775248,-0.999997146387718,-0.998175809236459,-0.992419120023356,-0.982749774749007,-0.969205895232745,-0.951840878815686,-0.930723187839362,-0.905936079729926,-0.877577278752084,-0.845758590726883,-0.810605462232336,-0.772256486024771,-0.730862854630786,-0.68658776426406,-0.639605771417098,-0.590102104664575,-0.538271934391528,-0.484319603325524,-0.428457820906457,-0.37090682467023,-0.311893511952568,-0.251650545336281,-0.190415435368805,-0.128429604166398,-0.0659374335968388,0.0, +static double sinarr[] = { +0.0,0.0627587292804297,0.125270029508395,0.18728744713136,0.2485664757507,0.308865520098932, +0.3679468485397,0.425577530335206,0.481530353985902,0.535584723021826,0.587527525713892,0.637153975276265, +0.684268417247276,0.728685100865749,0.770228911401552,0.80873606055313,0.84405473219009,0.876045680894979, +0.904582780944473,0.929553523565587,0.95085946050647,0.968416592172968,0.982155698800724,0.99202261335714, +0.997978435097294,0.999999682931835,0.99807838800221,0.992222125098244,0.982453982794196,0.968812472421035, +0.951351376233828,0.930139535372831,0.90526057845426,0.876812591860795,0.844907733031696,0.809671788277164, +0.771243676860277,0.72977490330168,0.685428960066342,0.638380682987321,0.588815561967795,0.536929009678953, +0.48292559113694,0.427018217196276,0.369427305139443,0.310379909672042,0.250108827749629,0.188851680765468, +0.12684997771773,0.064348163049637,0.00159265291648683,-0.0611691363208864,-0.123689763546002,-0.18572273843423, +-0.247023493251739,-0.307350347074556,-0.366465458626247,-0.424135763977612,-0.48013389541149,-0.534239077829989, +-0.586237999170027,-0.635925651395529,-0.683106138750633,-0.727593450087328,-0.769212192222595,-0.807798281433749, +-0.84319959036574,-0.875276547799941,-0.903902688919827,-0.928965153904073,-0.950365132881376,-0.968018255492714, +-0.981854923525203,-0.991820585306115,-0.997875950775248,-0.999997146387718,-0.998175809236459,-0.992419120023356, +-0.982749774749007,-0.969205895232745,-0.951840878815686,-0.930723187839362,-0.905936079729926,-0.877577278752084, +-0.845758590726883,-0.810605462232336,-0.772256486024771,-0.730862854630786,-0.68658776426406,-0.639605771417098, +-0.590102104664575,-0.538271934391528,-0.484319603325524,-0.428457820906457,-0.37090682467023,-0.311893511952568, +-0.251650545336281,-0.190415435368805,-0.128429604166398,-0.0659374335968388,0.0, }; -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position pos = mlt_filter_get_position( filter, frame ); + mlt_position len = mlt_filter_get_length2( filter, frame ); - mlt_filter filter = mlt_frame_pop_service( this ); *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); - + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + if ( error == 0 && *image ) { int h = *height; int w = *width; - int x=0; - int y=0; - - double position = mlt_filter_get_progress( filter, this ); - srand(position*10000); - - int delta = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "delta" ); - int every = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "every" ); - - int bdu = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_up" ); - int bdd = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_down" ); - int bevery = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_every" ); - - int udu = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_up" ); - int udd = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_down" ); - int uduration = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_duration" ); - - int diffpic=0; - if (delta) - diffpic=rand()%delta*2-delta; - - int brightdelta=0; - if ((bdu+bdd)!=0) - brightdelta=rand()%(bdu+bdd)-bdd; - if (rand()%100>every) - diffpic=0; - if (rand()%100>bevery) - brightdelta=0; - int yend,ydiff; - int unevendevelop_delta=0; - if (uduration>0){ - float uval= sinarr[ ( ((int)position) % uduration) * 100 / uduration ] ; - unevendevelop_delta = uval * ( uval>0 ? udu : udd ); - } + int x = 0; + int y = 0; - + double position = mlt_filter_get_progress( filter, frame ); + srand( position * 10000); - if (diffpic<=0){ - y=h; - yend=0; - ydiff=-1; - }else{ - y=0; - yend=h; - ydiff=1; - } + int delta = mlt_properties_anim_get_int( properties, "delta", pos, len ); + int every = mlt_properties_anim_get_int( properties, "every", pos, len ); - while(y!=yend){ - //int newy=y+diffpic; - for (x=0;x0 && newyw)?w-x:-1; - int randy=((newy)<=frameborder)?(newy):((newy)+frameborder>h)?h-(y+diffpic):-1; - if (randx>=0 ){ - oldval=oldval*pow(((double)randx/(double)frameborder),1.5); - } - if (randy>=0 ){ - oldval=oldval*pow(((double)randy/(double)frameborder),1.5); - } - if (randx>=0 && randy>=0){ - //oldval=oldval*(randx*randy)/500.0; - } - */ - if ( ((int) oldval + brightdelta + unevendevelop_delta ) >255) - *pic=255; - else if ( ( (int) oldval + brightdelta + unevendevelop_delta ) <0){ - *pic=0; - }else - *pic = oldval + brightdelta + unevendevelop_delta; - *(pic+1)=*(pic+diffpic*w*2+1); + int bdu = mlt_properties_anim_get_int( properties, "brightnessdelta_up", pos, len ); + int bdd = mlt_properties_anim_get_int( properties, "brightnessdelta_down", pos, len ); + int bevery = mlt_properties_anim_get_int( properties, "brightnessdelta_every", pos, len ); + + int udu = mlt_properties_anim_get_int( properties, "unevendevelop_up", pos, len ); + int udd = mlt_properties_anim_get_int( properties, "unevendevelop_down", pos, len ); + int uduration = mlt_properties_anim_get_int( properties, "unevendevelop_duration", pos, len ); + + int diffpic = 0; + if ( delta ) + diffpic = rand() % delta * 2 - delta; + int brightdelta = 0; + if (( bdu + bdd ) != 0 ) + brightdelta = rand() % (bdu + bdd) - bdd; + if ( rand() % 100 > every ) + diffpic = 0; + if ( rand() % 100 > bevery) + brightdelta = 0; + int yend, ydiff; + int unevendevelop_delta = 0; + if ( uduration > 0 ) + { + float uval = sinarr[ ( ((int)position) % uduration) * 100 / uduration ]; + unevendevelop_delta = uval * ( uval > 0 ? udu : udd ); + } + if ( diffpic <= 0 ) + { + y = h; + yend = 0; + ydiff =- 1; + } + else + { + y = 0; + yend = h; + ydiff = 1; + } - }else{ + while( y != yend ) + { + for ( x = 0; x < w; x++ ) + { + uint8_t* pic = ( *image + y * w * 2 + x * 2 ); + int newy = y + diffpic; + if ( newy > 0 && newy < h ) + { + uint8_t oldval= *( pic + diffpic * w * 2 ); + if ( ((int) oldval + brightdelta + unevendevelop_delta ) > 255 ) + { + *pic=255; + } + else if ( ( (int) oldval + brightdelta + unevendevelop_delta ) <0 ) + { *pic=0; - //*(pic-1)=127; } - - } - y+=ydiff; + else + { + *pic = oldval + brightdelta + unevendevelop_delta; + } + *( pic + 1 ) =* ( pic + diffpic * w * 2 + 1 ); + + } + else + { + *pic = 0; + } + } + y += ydiff; } } return error; } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } mlt_filter filter_oldfilm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "delta", "14" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "every", "20" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_up" , "20" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_down" , "30" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_every" , "70" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "unevendevelop_up" , "60" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "unevendevelop_down" , "20" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "unevendevelop_duration" , "70" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "delta", "14" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "every", "20" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_up" , "20" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_down" , "30" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_every" , "70" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_up" , "60" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_down" , "20" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_duration" , "70" ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_tcolor.c mlt-0.9.2/src/modules/oldfilm/filter_tcolor.c --- mlt-0.9.0/src/modules/oldfilm/filter_tcolor.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_tcolor.c 2014-06-29 20:23:17.000000000 +0000 @@ -27,60 +27,57 @@ #define MIN(a,b) (aprocess = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cr", "190" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cb", "190" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cr", "190" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cb", "190" ); } - return this; + return filter; } - diff -Nru mlt-0.9.0/src/modules/oldfilm/filter_vignette.c mlt-0.9.2/src/modules/oldfilm/filter_vignette.c --- mlt-0.9.0/src/modules/oldfilm/filter_vignette.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/oldfilm/filter_vignette.c 2014-06-29 20:23:17.000000000 +0000 @@ -24,114 +24,109 @@ #include #include #include + #define MIN(a,b) (adx){ //center, make not darker + if (radius-smooth > dx) //center, make not darker + { continue; } - else if (radius+smooth<=dx){//max dark after smooth area - delta=0.0; - }else{ + else if ( radius + smooth <= dx ) //max dark after smooth area + { + delta = 0.0; + } + else + { // linear pos from of opacity (from 0 to 1) - delta=(double)(radius+smooth-dx)/(2.0*smooth); - if (mode==1){ + delta = (double) ( radius + smooth - dx ) / ( 2.0 * smooth ); + if ( mode == 1 ) + { //make cos for smoother non linear fade - delta = (double)pow(cos(((1.0-delta)*3.14159/2.0)),3.0); + delta = (double) pow( cos((( 1.0 - delta ) * 3.14159 / 2.0 )), 3.0 ); } } - delta=MAX(max_opac,delta); - *pix=(double)(*pix)*delta; - *(pix+1)=((double)(*(pix+1)-127.0)*delta)+127.0; + delta = MAX( max_opac, delta ); + *pix = (double) (*pix) * delta; + *(pix+1) = ((double)(*(pix+1) - 127.0 ) * delta ) + 127.0; } } } - + return error; } -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } - mlt_filter filter_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - //int i=0; - if ( this != NULL ) - { - /* - for (i=-SIGMOD_STEPS/2;iprocess = filter_process; - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "smooth", 0.8 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "radius", 0.5 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "x", 0.5 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "y", 0.5 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "opacity", 0.0 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "mode", 0 ); - - //mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "end", "" ); + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = filter_process; + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "smooth", 0.8 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "radius", 0.5 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "x", 0.5 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "y", 0.5 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "opacity", 0.0 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "mode", 0 ); } - return this; + return filter; } - diff -Nru mlt-0.9.0/src/modules/opengl/factory.c mlt-0.9.2/src/modules/opengl/factory.c --- mlt-0.9.0/src/modules/opengl/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -40,6 +40,7 @@ extern mlt_filter filter_movit_saturation_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_transition transition_movit_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_movit_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_movit_overlay_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); @@ -71,20 +72,22 @@ MLT_REGISTER( filter_type, "movit.sharpen", filter_deconvolution_sharpen_init ); MLT_REGISTER( filter_type, "movit.vignette", filter_movit_vignette_init ); MLT_REGISTER( filter_type, "movit.white_balance", filter_white_balance_init ); + MLT_REGISTER( transition_type, "movit.luma_mix", transition_movit_luma_init ); MLT_REGISTER( transition_type, "movit.mix", transition_movit_mix_init ); MLT_REGISTER( transition_type, "movit.overlay", transition_movit_overlay_init ); MLT_REGISTER_METADATA( filter_type, "movit.blur", metadata, "filter_movit_blur.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.diffusion", metadata, "filter_movit_diffusion.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.glow", metadata, "filter_movit_glow.yml" ); - MLT_REGISTER_METADATA( filter_type, "movit.lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.lift_gamma_gain", metadata, "filter_movit_lift_gamma_gain.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.mirror", metadata, "filter_movit_mirror.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.opacity", metadata, "filter_movit_opacity.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.rect", metadata, "filter_movit_rect.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.saturation", metadata, "filter_movit_saturation.yml" ); - MLT_REGISTER_METADATA( filter_type, "movit.sharpen", metadata, "filter_deconvolution_sharpen.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.sharpen", metadata, "filter_movit_deconvolution_sharpen.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.vignette", metadata, "filter_movit_vignette.yml" ); - MLT_REGISTER_METADATA( filter_type, "movit.white_balance", metadata, "filter_white_balance.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.white_balance", metadata, "filter_movit_white_balance.yml" ); + MLT_REGISTER_METADATA( transition_type, "movit.luma_mix", metadata, "transition_movit_luma.yml" ); MLT_REGISTER_METADATA( transition_type, "movit.mix", metadata, "transition_movit_mix.yml" ); MLT_REGISTER_METADATA( transition_type, "movit.overlay", metadata, "transition_movit_overlay.yml" ); } diff -Nru mlt-0.9.0/src/modules/opengl/fbo_input.cpp mlt-0.9.2/src/modules/opengl/fbo_input.cpp --- mlt-0.9.0/src/modules/opengl/fbo_input.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/fbo_input.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * fbo_input.cpp - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "fbo_input.h" -#include - -// comes from unexported effect_util.h -extern void set_uniform_int(GLuint glsl_program_num, const std::string &prefix, const std::string &key, int value); - -FBOInput::FBOInput(unsigned width, unsigned height) - : texture_num(0) - , needs_mipmaps(false) - , width(width) - , height(height) -{ - register_int("needs_mipmaps", &needs_mipmaps); -} - -void FBOInput::set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) -{ - glActiveTexture(GL_TEXTURE0 + *sampler_num); - check_error(); - glBindTexture(GL_TEXTURE_2D, texture_num); - check_error(); - - // Bind it to a sampler. - set_uniform_int(glsl_program_num, prefix, "tex", *sampler_num); - ++*sampler_num; -} - -std::string FBOInput::output_fragment_shader() -{ - return read_file("flat_input.frag"); -// return "uniform sampler2D PREFIX(tex); vec4 FUNCNAME(vec2 tc) { return texture2D(PREFIX(tex), tc); }\n"; -} diff -Nru mlt-0.9.0/src/modules/opengl/fbo_input.h mlt-0.9.2/src/modules/opengl/fbo_input.h --- mlt-0.9.0/src/modules/opengl/fbo_input.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/fbo_input.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * fbo_input.h - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef FBO_INPUT_H -#define FBO_INPUT_H - -#include - -class FBOInput : public Input -{ -public: - FBOInput(unsigned width, unsigned height); - - virtual std::string effect_type_id() const { return "FBOInput"; } - void finalize() {} - bool can_output_linear_gamma() const { return false; } - AlphaHandling alpha_handling() const { return OUTPUT_POSTMULTIPLIED_ALPHA; } - std::string output_fragment_shader(); - void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num); - unsigned get_width() const { return width; } - unsigned get_height() const { return height; } - Colorspace get_color_space() const { return COLORSPACE_sRGB; } - GammaCurve get_gamma_curve() const { return GAMMA_sRGB; } - void set_texture(GLuint texture) { - texture_num = texture; - } - -private: - GLuint texture_num; - int needs_mipmaps; - unsigned width, height; -}; - -#endif // FBO_INPUT_H diff -Nru mlt-0.9.0/src/modules/opengl/filter_deconvolution_sharpen.cpp mlt-0.9.2/src/modules/opengl/filter_deconvolution_sharpen.cpp --- mlt-0.9.0/src/modules/opengl/filter_deconvolution_sharpen.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_deconvolution_sharpen.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/* - * filter_deconvolution_sharpen.cpp - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "glsl_manager.h" -#include - -static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_int( "matrix_size", - mlt_properties_anim_get_int( properties, "matrix_size", position, length ) ); - ok |= effect->set_float( "cirlce_radius", - mlt_properties_anim_get_double( properties, "circle_radius", position, length ) ); - ok |= effect->set_float( "gaussian_radius", - mlt_properties_anim_get_double( properties, "gaussian_radius", position, length ) ); - ok |= effect->set_float( "correlation", - mlt_properties_anim_get_double( properties, "correlation", position, length ) ); - ok |= effect->set_float( "noise", - mlt_properties_anim_get_double( properties, "noise", position, length ) ); - assert(ok); - } - GlslManager::get_instance()->unlock_service( frame ); - *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); -} - -static mlt_frame process( mlt_filter filter, mlt_frame frame ) -{ - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new DeconvolutionSharpenEffect() ); - } - mlt_frame_push_service( frame, filter ); - mlt_frame_push_get_image( frame, get_image ); - return frame; -} - -extern "C" { - -mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter filter = NULL; - GlslManager* glsl = GlslManager::get_instance(); - - if ( glsl && ( filter = mlt_filter_new() ) ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties_set_int( properties, "matrix_size", 5 ); - mlt_properties_set_double( properties, "circle_radius", 2.0 ); - mlt_properties_set_double( properties, "gaussian_radius", 0.0 ); - mlt_properties_set_double( properties, "correlation", 0.95 ); - mlt_properties_set_double( properties, "noise", 0.01 ); - filter->process = process; - } - return filter; -} - -} diff -Nru mlt-0.9.0/src/modules/opengl/filter_deconvolution_sharpen.yml mlt-0.9.2/src/modules/opengl/filter_deconvolution_sharpen.yml --- mlt-0.9.0/src/modules/opengl/filter_deconvolution_sharpen.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_deconvolution_sharpen.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: movit.sharpen -title: Deconvolution Sharpen (GLSL) -version: 1 -copyright: Dan Dennedy -creator: Steinar H. Gunderson -license: GPLv2 -language: en -tags: - - Video -description: > - Deconvolution Sharpen is a filter that sharpens by way of deconvolution - (i.e., trying to reverse the blur kernel, as opposed to just boosting high - frequencies), more specifically by FIR Wiener filters. It is the same - algorithm as used by the (now largely abandoned) Refocus plug-in for GIMP, - and I suspect the same as in Photoshop's “Smart Sharpen” filter. - The effect gives generally better results than unsharp masking, but can be very - GPU intensive, and requires a fair bit of tweaking to get good results without - ringing and/or excessive noise. - -parameters: - - identifier: matrix_size - title: Matrix Size - type: integer - minimum: 0 - maximum: 10 - default: 5 - mutable: yes - - - identifier: circle_radius - title: Circle Radius - type: float - minimum: 0 - default: 2 - mutable: yes - - - identifier: gaussian_radius - title: Gaussian Radius - type: float - minimum: 0 - default: 0 - mutable: yes - - - identifier: correlation - title: Correlation - type: float - minimum: 0 - default: 0.95 - mutable: yes - - - identifier: noise - title: Noise Level - type: float - minimum: 0 - default: 0.01 - mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_glsl_manager.cpp mlt-0.9.2/src/modules/opengl/filter_glsl_manager.cpp --- mlt-0.9.0/src/modules/opengl/filter_glsl_manager.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_glsl_manager.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -20,9 +20,11 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +#include #include +#include #include "mlt_movit_input.h" #include "mlt_flip_effect.h" #include @@ -32,78 +34,79 @@ #include } -void deleteManager(GlslManager *p) +#if defined(__DARWIN__) +#include +#elif defined(WIN32) +#include +#else +#include +#endif + +using namespace movit; + +void dec_ref_and_delete(GlslManager *p) { - delete p; + if (p->dec_ref() == 0) { + delete p; + } } GlslManager::GlslManager() : Mlt::Filter( mlt_filter_new() ) + , resource_pool(new ResourcePool()) , pbo(0) , initEvent(0) + , closeEvent(0) + , prev_sync(NULL) { mlt_filter filter = get_filter(); if ( filter ) { // Set the mlt_filter child in case we choose to override virtual functions. filter->child = this; - mlt_properties_set_data(mlt_global_properties(), "glslManager", this, 0, - (mlt_destructor) deleteManager, NULL); + add_ref(mlt_global_properties()); mlt_events_register( get_properties(), "init glsl", NULL ); + mlt_events_register( get_properties(), "close glsl", NULL ); initEvent = listen("init glsl", this, (mlt_listener) GlslManager::onInit); + closeEvent = listen("close glsl", this, (mlt_listener) GlslManager::onClose); } } GlslManager::~GlslManager() { mlt_log_debug(get_service(), "%s\n", __FUNCTION__); - while (fbo_list.peek_back()) - delete (glsl_fbo) fbo_list.pop_back(); - while (texture_list.peek_back()) - delete (glsl_texture) texture_list.pop_back(); - delete pbo; + cleanupContext(); +// XXX If there is still a frame holding a reference to a texture after this +// destructor is called, then it will crash in release_texture(). +// while (texture_list.peek_back()) +// delete (glsl_texture) texture_list.pop_back(); delete initEvent; + delete closeEvent; + if (prev_sync != NULL) { + glDeleteSync( prev_sync ); + } + while (syncs_to_delete.count() > 0) { + GLsync sync = (GLsync) syncs_to_delete.pop_front(); + glDeleteSync( sync ); + } + delete resource_pool; } -GlslManager* GlslManager::get_instance() -{ - return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0); -} - -glsl_fbo GlslManager::get_fbo(int width, int height) +void GlslManager::add_ref(mlt_properties properties) { - for (int i = 0; i < fbo_list.count(); ++i) { - glsl_fbo fbo = (glsl_fbo) fbo_list.peek(i); - if (!fbo->used && (fbo->width == width) && (fbo->height == height)) { - fbo->used = 1; - return fbo; - } - } - GLuint fb = 0; - glGenFramebuffers(1, &fb); - if (!fb) - return NULL; - - glsl_fbo fbo = new glsl_fbo_s; - if (!fbo) { - glDeleteFramebuffers(1, &fb); - return NULL; - } - fbo->fbo = fb; - fbo->width = width; - fbo->height = height; - fbo->used = 1; - fbo_list.push_back(fbo); - return fbo; + inc_ref(); + mlt_properties_set_data(properties, "glslManager", this, 0, + (mlt_destructor) dec_ref_and_delete, NULL); } -void GlslManager::release_fbo(glsl_fbo fbo) +GlslManager* GlslManager::get_instance() { - fbo->used = 0; + return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0); } glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format) { + lock(); for (int i = 0; i < texture_list.count(); ++i) { glsl_texture tex = (glsl_texture) texture_list.peek(i); if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) { @@ -112,21 +115,26 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture( GL_TEXTURE_2D, 0); tex->used = 1; + unlock(); return tex; } } + unlock(); + GLuint tex = 0; glGenTextures(1, &tex); - if (!tex) + if (!tex) { return NULL; + } glsl_texture gtex = new glsl_texture_s; if (!gtex) { glDeleteTextures(1, &tex); return NULL; } + glBindTexture( GL_TEXTURE_2D, tex ); - glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, internal_format, GL_UNSIGNED_BYTE, NULL ); + glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); @@ -138,7 +146,9 @@ gtex->height = height; gtex->internal_format = internal_format; gtex->used = 1; + lock(); texture_list.push_back(gtex); + unlock(); return gtex; } @@ -147,20 +157,36 @@ texture->used = 0; } +void GlslManager::delete_sync(GLsync sync) +{ + // We do not know which thread we are called from, and we can only + // delete this if we are in one with a valid OpenGL context. + // Thus, store it for later deletion in render_frame_texture(). + GlslManager* g = GlslManager::get_instance(); + g->lock(); + g->syncs_to_delete.push_back(sync); + g->unlock(); +} + glsl_pbo GlslManager::get_pbo(int size) { + lock(); if (!pbo) { GLuint pb = 0; glGenBuffers(1, &pb); - if (!pb) + if (!pb) { + unlock(); return NULL; + } pbo = new glsl_pbo_s; if (!pbo) { glDeleteBuffers(1, &pb); + unlock(); return NULL; } pbo->pbo = pb; + pbo->size = 0; } if (size > pbo->size) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo); @@ -168,9 +194,27 @@ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); pbo->size = size; } + unlock(); return pbo; } +void GlslManager::cleanupContext() +{ + lock(); + while (texture_list.peek_back()) { + glsl_texture texture = (glsl_texture) texture_list.peek_back(); + glDeleteTextures(1, &texture->texture); + delete texture; + texture_list.pop_back(); + } + if (pbo) { + glDeleteBuffers(1, &pbo->pbo); + delete pbo; + pbo = 0; + } + unlock(); +} + void GlslManager::onInit( mlt_properties owner, GlslManager* filter ) { mlt_log_debug( filter->get_service(), "%s\n", __FUNCTION__ ); @@ -181,8 +225,13 @@ #else std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR); #endif - ::init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF ); - filter->set( "glsl_supported", movit_initialized ); + bool success = init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF ); + filter->set( "glsl_supported", success ); +} + +void GlslManager::onClose( mlt_properties owner, GlslManager *filter ) +{ + filter->cleanupContext(); } void GlslManager::onServiceChanged( mlt_properties owner, mlt_service aservice ) @@ -190,9 +239,6 @@ Mlt::Service service( aservice ); service.lock(); service.set( "movit chain", NULL, 0 ); - service.set( "movit input", NULL, 0 ); - // Destroy the effect list. - GlslManager::get_instance()->set( service.get( "_unique_id" ), NULL, 0 ); service.unlock(); } @@ -216,96 +262,234 @@ } // extern "C" -Mlt::Properties GlslManager::effect_list( Mlt::Service& service ) +static void deleteChain( GlslChain* chain ) { - char *unique_id = service.get( "_unique_id" ); - mlt_properties properties = (mlt_properties) get_data( unique_id ); - if ( !properties ) { - properties = mlt_properties_new(); - set( unique_id, properties, 0, (mlt_destructor) mlt_properties_close ); + // The Input* is owned by the EffectChain, but the MltInput* is not. + // Thus, we have to delete it here. + for (std::map::iterator input_it = chain->inputs.begin(); + input_it != chain->inputs.end(); + ++input_it) { + delete input_it->second; } - Mlt::Properties p( properties ); - return p; + delete chain->effect_chain; + delete chain; +} + +void* GlslManager::get_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, int *length ) +{ + const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" ); + char buf[256]; + snprintf( buf, sizeof(buf), "%s_%s", key, unique_id ); + return mlt_properties_get_data( MLT_FRAME_PROPERTIES(frame), buf, length ); } -static void deleteChain( EffectChain* chain ) +int GlslManager::set_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise ) { - delete chain; + const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" ); + char buf[256]; + snprintf( buf, sizeof(buf), "%s_%s", key, unique_id ); + return mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), buf, value, length, destroy, serialise ); } -bool GlslManager::init_chain( mlt_service aservice ) +void GlslManager::set_chain( mlt_service service, GlslChain* chain ) { - bool error = true; - Mlt::Service service( aservice ); - EffectChain* chain = (EffectChain*) service.get_data( "movit chain" ); - if ( !chain ) { - mlt_profile profile = mlt_service_profile( aservice ); - Input* input = new MltInput( profile->width, profile->height ); - chain = new EffectChain( profile->display_aspect_num, profile->display_aspect_den ); - chain->add_input( input ); - service.set( "movit chain", chain, 0, (mlt_destructor) deleteChain ); - service.set( "movit input", input, 0 ); - service.set( "_movit finalized", 0 ); - service.listen( "service-changed", aservice, (mlt_listener) GlslManager::onServiceChanged ); - service.listen( "property-changed", aservice, (mlt_listener) GlslManager::onPropertyChanged ); - error = false; - } - return error; + mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", chain, 0, (mlt_destructor) deleteChain, NULL ); } -EffectChain* GlslManager::get_chain( mlt_service service ) +GlslChain* GlslManager::get_chain( mlt_service service ) +{ + return (GlslChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", NULL ); +} + +Effect* GlslManager::get_effect( mlt_service service, mlt_frame frame ) { - return (EffectChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit chain", NULL ); + return (Effect*) get_frame_specific_data( service, frame, "_movit effect", NULL ); } -MltInput *GlslManager::get_input( mlt_service service ) +Effect* GlslManager::set_effect( mlt_service service, mlt_frame frame, Effect* effect ) { - return (MltInput*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit input", NULL ); + set_frame_specific_data( service, frame, "_movit effect", effect, 0, NULL, NULL ); + return effect; } -void GlslManager::reset_finalized( mlt_service service ) +MltInput* GlslManager::get_input( mlt_producer producer, mlt_frame frame ) { - mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_movit finalized", 0 ); + return (MltInput*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", NULL ); } -Effect* GlslManager::get_effect( mlt_filter filter, mlt_frame frame ) +MltInput* GlslManager::set_input( mlt_producer producer, mlt_frame frame, MltInput* input ) { - Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); - char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); - return (Effect*) GlslManager::get_instance()->effect_list( producer ).get_data( unique_id ); + set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", input, 0, NULL, NULL ); + return input; } -Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect ) +uint8_t* GlslManager::get_input_pixel_pointer( mlt_producer producer, mlt_frame frame ) { - Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); - EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" ); - chain->add_effect( effect ); - char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); - GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 ); - return effect; + return (uint8_t*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", NULL ); } -Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect, Effect* input_b ) +uint8_t* GlslManager::set_input_pixel_pointer( mlt_producer producer, mlt_frame frame, uint8_t* image ) { - Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); - EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" ); - chain->add_effect( effect, chain->last_added_effect(), - input_b? input_b : chain->last_added_effect() ); - char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); - GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 ); - return effect; + set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", image, 0, NULL, NULL ); + return image; +} + +mlt_service GlslManager::get_effect_input( mlt_service service, mlt_frame frame ) +{ + return (mlt_service) get_frame_specific_data( service, frame, "_movit effect input", NULL ); +} + +void GlslManager::set_effect_input( mlt_service service, mlt_frame frame, mlt_service input_service ) +{ + set_frame_specific_data( service, frame, "_movit effect input", input_service, 0, NULL, NULL ); +} + +void GlslManager::get_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) +{ + *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect secondary input", NULL ); + *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect secondary input frame", NULL ); +} + +void GlslManager::set_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame ) +{ + set_frame_specific_data( service, frame, "_movit effect secondary input", input_service, 0, NULL, NULL ); + set_frame_specific_data( service, frame, "_movit effect secondary input frame", input_frame, 0, NULL, NULL ); +} + +void GlslManager::get_effect_third_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) +{ + *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect third input", NULL ); + *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect third input frame", NULL ); +} + +void GlslManager::set_effect_third_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame ) +{ + set_frame_specific_data( service, frame, "_movit effect third input", input_service, 0, NULL, NULL ); + set_frame_specific_data( service, frame, "_movit effect third input frame", input_frame, 0, NULL, NULL ); } -void GlslManager::render( mlt_service service, void* chain, GLuint fbo, int width, int height ) +int GlslManager::render_frame_texture(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { - EffectChain* effect_chain = (EffectChain*) chain; - mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); - if ( !mlt_properties_get_int( properties, "_movit finalized" ) ) { - mlt_properties_set_int( properties, "_movit finalized", 1 ); - effect_chain->add_effect( new Mlt::VerticalFlip() ); - effect_chain->finalize(); + glsl_texture texture = get_texture( width, height, GL_RGBA8 ); + if (!texture) { + return 1; } - effect_chain->render_to_fbo( fbo, width, height ); + + GLuint fbo; + glGenFramebuffers( 1, &fbo ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + lock(); + while (syncs_to_delete.count() > 0) { + GLsync sync = (GLsync) syncs_to_delete.pop_front(); + glDeleteSync( sync ); + } + unlock(); + + // Make sure we never have more than one frame pending at any time. + // This ensures we do not swamp the GPU with so much work + // that we cannot actually display the frames we generate. + if (prev_sync != NULL) { + glFlush(); + glClientWaitSync( prev_sync, 0, GL_TIMEOUT_IGNORED ); + glDeleteSync( prev_sync ); + } + chain->render_to_fbo( fbo, width, height ); + prev_sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); + GLsync sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); + + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + glDeleteFramebuffers( 1, &fbo ); + check_error(); + + *image = (uint8_t*) &texture->texture; + mlt_frame_set_image( frame, *image, 0, NULL ); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, + (mlt_destructor) GlslManager::release_texture, NULL ); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.fence", sync, 0, + (mlt_destructor) GlslManager::delete_sync, NULL ); + + return 0; +} + +int GlslManager::render_frame_rgba(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) +{ + glsl_texture texture = get_texture( width, height, GL_RGBA8 ); + if (!texture) { + return 1; + } + + // Use a PBO to hold the data we read back with glReadPixels(). + // (Intel/DRI goes into a slow path if we don't read to PBO.) + int img_size = width * height * 4; + glsl_pbo pbo = get_pbo( img_size ); + if (!pbo) { + release_texture(texture); + return 1; + } + + // Set the FBO + GLuint fbo; + glGenFramebuffers( 1, &fbo ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + chain->render_to_fbo( fbo, width, height ); + + // Read FBO into PBO + glBindFramebuffer( GL_FRAMEBUFFER, fbo ); + check_error(); + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); + check_error(); + glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); + check_error(); + glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); + check_error(); + + // Copy from PBO + uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); + check_error(); + *image = (uint8_t*) mlt_pool_alloc( img_size ); + mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); + memcpy( *image, buf, img_size ); + + // Convert BGRA to RGBA + register uint8_t *p = *image; + register int n = width * height + 1; + while ( --n ) { + uint8_t b = p[0]; + *p = p[2]; p += 2; + *p = b; p += 2; + } + + // Release PBO and FBO + glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); + check_error(); + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + glBindTexture( GL_TEXTURE_2D, 0 ); + check_error(); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, + (mlt_destructor) GlslManager::release_texture, NULL); + glDeleteFramebuffers( 1, &fbo ); + check_error(); + + return 0; } void GlslManager::lock_service( mlt_frame frame ) diff -Nru mlt-0.9.0/src/modules/opengl/filter_glsl_manager.h mlt-0.9.2/src/modules/opengl/filter_glsl_manager.h --- mlt-0.9.0/src/modules/opengl/filter_glsl_manager.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_glsl_manager.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,145 @@ +/* + * filter_glsl_manager.h + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GLSL_MANAGER_H +#define GLSL_MANAGER_H + +// Include a random Movit header file to get in GL.h, without including it +// ourselves (which might interfere with whatever OpenGL extension library +// Movit has chosen to use). +#include + +#include +#include +#include +#include + +#define MAXLISTCOUNT 1024 +typedef struct glsl_list_s *glsl_list; +struct glsl_list_s +{ + void *items[MAXLISTCOUNT]; + int count; + + int ( *add )( glsl_list, void* ); + void* ( *at )( glsl_list, int ); + void* ( *take_at )( glsl_list, int ); + void* ( *take )( glsl_list, void* ); +}; + +struct glsl_texture_s +{ + int used; + GLuint texture; + int width; + int height; + GLint internal_format; +}; +typedef struct glsl_texture_s *glsl_texture; + +struct glsl_pbo_s +{ + int size; + GLuint pbo; +}; +typedef struct glsl_pbo_s *glsl_pbo; + +namespace movit { + +class Effect; +class EffectChain; +class ResourcePool; + +} // namespace movit + +class MltInput; + +struct GlslChain +{ + movit::EffectChain *effect_chain; + + // All MltInputs in the effect chain. These are not owned by the + // EffectChain (although the contained Input* is). + std::map inputs; + + // All services owned by the effect chain and their associated Movit effect. + std::map effects; + + // For each effect in the Movit graph, a unique identifier for the service + // and whether it's disabled or not, using post-order traversal. + // We need to generate the chain if and only if this has changed. + std::string fingerprint; +}; + +class GlslManager : public Mlt::Filter +{ +public: + GlslManager(); + ~GlslManager(); + void add_ref(mlt_properties properties); + static GlslManager* get_instance(); + + glsl_texture get_texture(int width, int height, GLint internal_format); + static void release_texture(glsl_texture); + static void delete_sync(GLsync sync); + glsl_pbo get_pbo(int size); + void cleanupContext(); + + movit::ResourcePool* get_resource_pool() { return resource_pool; } + + static void set_chain(mlt_service, GlslChain*); + static GlslChain* get_chain(mlt_service); + + static movit::Effect* get_effect(mlt_service, mlt_frame); + static movit::Effect* set_effect(mlt_service, mlt_frame, movit::Effect*); + static MltInput* get_input(mlt_producer, mlt_frame); + static MltInput* set_input(mlt_producer, mlt_frame, MltInput*); + static uint8_t* get_input_pixel_pointer(mlt_producer, mlt_frame); + static uint8_t* set_input_pixel_pointer(mlt_producer, mlt_frame, uint8_t*); + + static mlt_service get_effect_input(mlt_service, mlt_frame); + static void set_effect_input(mlt_service, mlt_frame, mlt_service); + static void get_effect_secondary_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); + static void set_effect_secondary_input(mlt_service, mlt_frame, mlt_service, mlt_frame); + static void get_effect_third_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); + static void set_effect_third_input(mlt_service, mlt_frame, mlt_service, mlt_frame); + + int render_frame_texture(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); + int render_frame_rgba(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); + static void lock_service(mlt_frame frame); + static void unlock_service(mlt_frame frame); + +private: + static void* get_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, int *length ); + static int set_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise ); + + static void onInit( mlt_properties owner, GlslManager* filter ); + static void onClose( mlt_properties owner, GlslManager* filter ); + static void onServiceChanged( mlt_properties owner, mlt_service service ); + static void onPropertyChanged( mlt_properties owner, mlt_service service, const char* property ); + movit::ResourcePool* resource_pool; + Mlt::Deque texture_list; + Mlt::Deque syncs_to_delete; + glsl_pbo pbo; + Mlt::Event* initEvent; + Mlt::Event* closeEvent; + GLsync prev_sync; +}; + +#endif // GLSL_MANAGER_H diff -Nru mlt-0.9.0/src/modules/opengl/filter_lift_gamma_gain.cpp mlt-0.9.2/src/modules/opengl/filter_lift_gamma_gain.cpp --- mlt-0.9.0/src/modules/opengl/filter_lift_gamma_gain.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_lift_gamma_gain.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/* - * filter_lift_gamma_gain.cpp - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "glsl_manager.h" -#include - -static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - RGBTriplet triplet( - mlt_properties_anim_get_double( properties, "lift_r", position, length ), - mlt_properties_anim_get_double( properties, "lift_g", position, length ), - mlt_properties_anim_get_double( properties, "lift_b", position, length ) - ); - bool ok = effect->set_vec3( "lift", (float*) &triplet ); - triplet.r = mlt_properties_anim_get_double( properties, "gamma_r", position, length ); - triplet.g = mlt_properties_anim_get_double( properties, "gamma_g", position, length ); - triplet.b = mlt_properties_anim_get_double( properties, "gamma_b", position, length ); - ok |= effect->set_vec3( "gamma", (float*) &triplet ); - triplet.r = mlt_properties_anim_get_double( properties, "gain_r", position, length ); - triplet.g = mlt_properties_anim_get_double( properties, "gain_g", position, length ); - triplet.b = mlt_properties_anim_get_double( properties, "gain_b", position, length ); - ok |= effect->set_vec3( "gain", (float*) &triplet ); - assert(ok); - } - GlslManager::get_instance()->unlock_service( frame ); - *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); -} - -static mlt_frame process( mlt_filter filter, mlt_frame frame ) -{ - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new LiftGammaGainEffect ); - } - mlt_frame_push_service( frame, filter ); - mlt_frame_push_get_image( frame, get_image ); - return frame; -} - -extern "C" { - -mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter filter = NULL; - GlslManager* glsl = GlslManager::get_instance(); - - if ( glsl && ( filter = mlt_filter_new() ) ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties_set_double( properties, "lift_r", 0.0 ); - mlt_properties_set_double( properties, "lift_g", 0.0 ); - mlt_properties_set_double( properties, "lift_b", 0.0 ); - mlt_properties_set_double( properties, "gamma_r", 1.0 ); - mlt_properties_set_double( properties, "gamma_g", 1.0 ); - mlt_properties_set_double( properties, "gamma_b", 1.0 ); - mlt_properties_set_double( properties, "gain_r", 1.0 ); - mlt_properties_set_double( properties, "gain_g", 1.0 ); - mlt_properties_set_double( properties, "gain_b", 1.0 ); - filter->process = process; - } - return filter; -} - -} diff -Nru mlt-0.9.0/src/modules/opengl/filter_lift_gamma_gain.yml mlt-0.9.2/src/modules/opengl/filter_lift_gamma_gain.yml --- mlt-0.9.0/src/modules/opengl/filter_lift_gamma_gain.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_lift_gamma_gain.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: movit.lift_gamma_gain -title: Lift, Gamma, and Gain (GLSL) -version: 1 -copyright: Dan Dennedy -creator: Steinar H. Gunderson -license: GPLv2 -language: en -tags: - - Video -description: > - A simple lift/gamma/gain effect, used for color grading. -notes: > - Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, - although all parameters affect the entire curve. Mathematically speaking, - it is a bit unusual to look at gamma as a color, but it works pretty well - in practice. - The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). - The lift is actually a case where we actually would _not_ want linear light; - since black by definition becomes equal to the lift color, we want lift to - be pretty close to black, but in linear light that means lift affects the - rest of the curve relatively little. Thus, we actually convert to gamma 2.2 - before lift, and then back again afterwards. (Gain and gamma are, - up to constants, commutative with the de-gamma operation.) - -parameters: - - identifier: lift_r - title: Lift Red - type: float - minimum: 0.0 - default: 0.0 - mutable: yes - - - identifier: lift_g - title: Lift Green - type: float - minimum: 0.0 - default: 0.0 - mutable: yes - - - identifier: lift_b - title: Lift Blue - type: float - minimum: 0.0 - default: 0.0 - mutable: yes - - - identifier: gamma_r - title: Gamma Red - type: float - minimum: 0.0 - default: 1.0 - mutable: yes - - - identifier: gamma_g - title: Gamma Green - type: float - minimum: 0.0 - default: 1.0 - mutable: yes - - - identifier: gamma_b - title: Gamma Blue - type: float - minimum: 0.0 - default: 1.0 - mutable: yes - - - identifier: gain_r - title: Gain Red - type: float - minimum: 0.0 - default: 1.0 - mutable: yes - - - identifier: gain_g - title: Gain Green - type: float - minimum: 0.0 - default: 1.0 - mutable: yes - - - identifier: gain_b - title: Gain Blue - type: float - minimum: 0.0 - default: 1.0 - mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_blur.cpp mlt-0.9.2/src/modules/opengl/filter_movit_blur.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_blur.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_blur.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,36 +21,32 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - double radius = mlt_properties_anim_get_double( properties, "radius", - mlt_filter_get_position( filter, frame ), - mlt_filter_get_length2( filter, frame ) ); - bool ok = effect->set_float( "radius", radius ); - assert(ok); - } + double radius = mlt_properties_anim_get_double( properties, "radius", + mlt_filter_get_position( filter, frame ), + mlt_filter_get_length2( filter, frame ) ); + mlt_properties_set_double( properties, "_movit.parms.float.radius", + radius ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new BlurEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( !effect ) { - effect = GlslManager::add_effect( filter, frame, new BlurEffect() ); - assert(effect); - } - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -65,6 +61,7 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 3 ); filter->process = process; } diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_convert.cpp mlt-0.9.2/src/modules/opengl/filter_movit_convert.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_convert.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_convert.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,14 +21,19 @@ #include #include #include +#include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include #include #include "mlt_movit_input.h" #include #include "mlt_flip_effect.h" +using namespace movit; + +static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_frame frame ); + static void yuv422_to_yuv422p( uint8_t *yuv422, uint8_t *yuv422p, int width, int height ) { uint8_t *Y = yuv422p; @@ -65,6 +70,356 @@ delete chain; } +static void get_format_from_properties( mlt_properties properties, ImageFormat* image_format, YCbCrFormat* ycbcr_format ) +{ + switch ( mlt_properties_get_int( properties, "colorspace" ) ) { + case 601: + ycbcr_format->luma_coefficients = YCBCR_REC_601; + break; + case 709: + default: + ycbcr_format->luma_coefficients = YCBCR_REC_709; + break; + } + + switch ( mlt_properties_get_int( properties, "color_primaries" ) ) { + case 601625: + image_format->color_space = COLORSPACE_REC_601_625; + break; + case 601525: + image_format->color_space = COLORSPACE_REC_601_525; + break; + case 709: + default: + image_format->color_space = COLORSPACE_REC_709; + break; + } + + image_format->gamma_curve = GAMMA_REC_709; + + if ( mlt_properties_get_int( properties, "force_full_luma" ) ) { + ycbcr_format->full_range = true; + } else { + ycbcr_format->full_range = ( mlt_properties_get_int( properties, "full_luma" ) == 1 ); + } + + // TODO: make new frame properties set by producers + ycbcr_format->cb_x_position = ycbcr_format->cr_x_position = 0.0f; + ycbcr_format->cb_y_position = ycbcr_format->cr_y_position = 0.5f; +} + +static void build_fingerprint( mlt_service service, mlt_frame frame, std::string *fingerprint ) +{ + if ( service == (mlt_service) -1 ) { + fingerprint->append( "input" ); + return; + } + + Effect* effect = GlslManager::get_effect( service, frame ); + assert( effect ); + mlt_service input_a = GlslManager::get_effect_input( service, frame ); + fingerprint->push_back( '(' ); + build_fingerprint( input_a, frame, fingerprint ); + fingerprint->push_back( ')' ); + + mlt_frame frame_b; + mlt_service input_b; + GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + fingerprint->push_back( '(' ); + build_fingerprint( input_b, frame_b, fingerprint ); + fingerprint->push_back( ')' ); + } + + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + fingerprint->push_back( '(' ); + build_fingerprint( input_b, frame_b, fingerprint ); + fingerprint->push_back( ')' ); + } + + fingerprint->push_back( '(' ); + fingerprint->append( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_unique_id" ) ); + + const char* effect_fingerprint = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_movit fingerprint" ); + if ( effect_fingerprint ) { + fingerprint->push_back( '[' ); + fingerprint->append( effect_fingerprint ); + fingerprint->push_back( ']' ); + } + + bool disable = mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "_movit.parms.int.disable" ); + if ( disable ) { + fingerprint->push_back( 'd' ); + } + fingerprint->push_back( ')' ); +} + +static Effect* build_movit_chain( mlt_service service, mlt_frame frame, GlslChain *chain ) +{ + if ( service == (mlt_service) -1 ) { + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + MltInput* input = GlslManager::get_input( producer, frame ); + GlslManager::set_input( producer, frame, NULL ); + chain->effect_chain->add_input( input->get_input() ); + chain->inputs.insert(std::make_pair( producer, input ) ); + return input->get_input(); + } + + Effect* effect = GlslManager::get_effect( service, frame ); + assert( effect ); + GlslManager::set_effect( service, frame, NULL ); + + mlt_service input_a = GlslManager::get_effect_input( service, frame ); + mlt_service input_b, input_c; + mlt_frame frame_b, frame_c; + GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + GlslManager::get_effect_third_input( service, frame, &input_c, &frame_c ); + Effect *effect_a = build_movit_chain( input_a, frame, chain ); + + if ( input_c && input_b ) { + Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); + Effect *effect_c = build_movit_chain( input_c, frame_c, chain ); + chain->effect_chain->add_effect( effect, effect_a, effect_b, effect_c ); + } else if ( input_b ) { + Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); + chain->effect_chain->add_effect( effect, effect_a, effect_b ); + } else { + chain->effect_chain->add_effect( effect, effect_a ); + } + + chain->effects.insert(std::make_pair( service, effect ) ); + return effect; +} + +static void dispose_movit_effects( mlt_service service, mlt_frame frame ) +{ + if ( service == (mlt_service) -1 ) { + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + delete GlslManager::get_input( producer, frame ); + GlslManager::set_input( producer, frame, NULL ); + return; + } + + delete GlslManager::get_effect( service, frame ); + GlslManager::set_effect( service, frame, NULL ); + + mlt_service input_a = GlslManager::get_effect_input( service, frame ); + mlt_service input_b; + mlt_frame frame_b; + GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + dispose_movit_effects( input_a, frame ); + + if ( input_b ) { + dispose_movit_effects( input_b, frame_b ); + } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + dispose_movit_effects( input_b, frame_b ); + } +} + +static void finalize_movit_chain( mlt_service leaf_service, mlt_frame frame ) +{ + GlslChain* chain = GlslManager::get_chain( leaf_service ); + + std::string new_fingerprint; + build_fingerprint( leaf_service, frame, &new_fingerprint ); + + // Build the chain if needed. + if ( !chain || new_fingerprint != chain->fingerprint ) { + mlt_log_debug( leaf_service, "=== CREATING NEW CHAIN (old chain=%p, leaf=%p, fingerprint=%s) ===\n", chain, leaf_service, new_fingerprint.c_str() ); + mlt_profile profile = mlt_service_profile( leaf_service ); + chain = new GlslChain; + chain->effect_chain = new EffectChain( + profile->display_aspect_num, + profile->display_aspect_den, + GlslManager::get_instance()->get_resource_pool() + ); + chain->fingerprint = new_fingerprint; + + build_movit_chain( leaf_service, frame, chain ); + set_movit_parameters( chain, leaf_service, frame ); + chain->effect_chain->add_effect( new Mlt::VerticalFlip ); + + ImageFormat output_format; + output_format.color_space = COLORSPACE_sRGB; + output_format.gamma_curve = GAMMA_sRGB; + chain->effect_chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); + chain->effect_chain->set_dither_bits(8); + chain->effect_chain->finalize(); + + GlslManager::set_chain( leaf_service, chain ); + } else { + // Delete all the created Effect instances to avoid memory leaks. + dispose_movit_effects( leaf_service, frame ); + } +} + +static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_frame frame ) +{ + if ( service == (mlt_service) -1 ) { + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + MltInput* input = chain->inputs[ producer ]; + input->set_pixel_data( GlslManager::get_input_pixel_pointer( producer, frame ) ); + return; + } + + Effect* effect = chain->effects[ service ]; + mlt_service input_a = GlslManager::get_effect_input( service, frame ); + set_movit_parameters( chain, input_a, frame ); + + mlt_service input_b; + mlt_frame frame_b; + GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + set_movit_parameters( chain, input_b, frame_b ); + } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + set_movit_parameters( chain, input_b, frame_b ); + } + + mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); + int count = mlt_properties_count( properties ); + for (int i = 0; i < count; ++i) { + const char *name = mlt_properties_get_name( properties, i ); + if (strncmp(name, "_movit.parms.float.", strlen("_movit.parms.float.")) == 0 && + mlt_properties_get_value( properties, i )) { + bool ok = effect->set_float(name + strlen("_movit.parms.float."), + mlt_properties_get_double( properties, name )); + assert(ok); + } + if (strncmp(name, "_movit.parms.int.", strlen("_movit.parms.int.")) == 0 && + mlt_properties_get_value( properties, i )) { + bool ok = effect->set_int(name + strlen("_movit.parms.int."), + mlt_properties_get_int( properties, name )); + assert(ok); + } + if (strncmp(name, "_movit.parms.vec3.", strlen("_movit.parms.vec3.")) == 0 && + strcmp(name + strlen(name) - 3, "[0]") == 0 && + mlt_properties_get_value( properties, i )) { + float val[3]; + char *name_copy = strdup(name); + char *index_char = name_copy + strlen(name_copy) - 2; + val[0] = mlt_properties_get_double( properties, name_copy ); + *index_char = '1'; + val[1] = mlt_properties_get_double( properties, name_copy ); + *index_char = '2'; + val[2] = mlt_properties_get_double( properties, name_copy ); + index_char[-1] = '\0'; + bool ok = effect->set_vec3(name_copy + strlen("_movit.parms.vec3."), val); + assert(ok); + free(name_copy); + } + if (strncmp(name, "_movit.parms.vec4.", strlen("_movit.parms.vec4.")) == 0 && + strcmp(name + strlen(name) - 3, "[0]") == 0 && + mlt_properties_get_value( properties, i )) { + float val[4]; + char *name_copy = strdup(name); + char *index_char = name_copy + strlen(name_copy) - 2; + val[0] = mlt_properties_get_double( properties, name_copy ); + *index_char = '1'; + val[1] = mlt_properties_get_double( properties, name_copy ); + *index_char = '2'; + val[2] = mlt_properties_get_double( properties, name_copy ); + *index_char = '3'; + val[3] = mlt_properties_get_double( properties, name_copy ); + index_char[-1] = '\0'; + bool ok = effect->set_vec4(name_copy + strlen("_movit.parms.vec4."), val); + assert(ok); + free(name_copy); + } + } +} + +static void dispose_pixel_pointers( GlslChain *chain, mlt_service service, mlt_frame frame ) +{ + if ( service == (mlt_service) -1 ) { + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + MltInput* input = chain->inputs[ producer ]; + input->invalidate_pixel_data(); + mlt_pool_release( GlslManager::get_input_pixel_pointer( producer, frame ) ); + return; + } + + mlt_service input_a = GlslManager::get_effect_input( service, frame ); + dispose_pixel_pointers( chain, input_a, frame ); + + mlt_service input_b; + mlt_frame frame_b; + GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + dispose_pixel_pointers( chain, input_b, frame_b ); + } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + dispose_pixel_pointers( chain, input_b, frame_b ); + } +} + +static int movit_render( EffectChain *chain, mlt_frame frame, mlt_image_format *format, mlt_image_format output_format, int width, int height, uint8_t **image ) +{ + GlslManager* glsl = GlslManager::get_instance(); + int error; + if ( output_format == mlt_image_glsl_texture ) { + error = glsl->render_frame_texture( chain, frame, width, height, image ); + } + else { + error = glsl->render_frame_rgba( chain, frame, width, height, image ); + if ( !error && output_format != mlt_image_rgb24a ) { + *format = mlt_image_rgb24a; + error = convert_on_cpu( frame, image, format, output_format ); + } + } + return error; +} + +// Create an MltInput for an image with the given format and dimensions. +static MltInput* create_input( mlt_properties properties, mlt_image_format format, int aspect_width, int aspect_height, int width, int height ) +{ + MltInput* input = new MltInput( format ); + if ( format == mlt_image_rgb24a || format == mlt_image_opengl ) { + // TODO: Get the color space if available. + input->useFlatInput( FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); + } + else if ( format == mlt_image_rgb24 ) { + // TODO: Get the color space if available. + input->useFlatInput( FORMAT_RGB, width, height ); + } + else if ( format == mlt_image_yuv420p ) { + ImageFormat image_format; + YCbCrFormat ycbcr_format; + get_format_from_properties( properties, &image_format, &ycbcr_format ); + ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; + input->useYCbCrInput( image_format, ycbcr_format, width, height ); + } + else if ( format == mlt_image_yuv422 ) { + ImageFormat image_format; + YCbCrFormat ycbcr_format; + get_format_from_properties( properties, &image_format, &ycbcr_format ); + ycbcr_format.chroma_subsampling_x = 2; + ycbcr_format.chroma_subsampling_y = 1; + input->useYCbCrInput( image_format, ycbcr_format, width, height ); + } + return input; +} + +// Make a copy of the given image (allocated using mlt_pool_alloc) suitable +// to pass as pixel pointer to an MltInput (created using create_input +// with the same parameters), and return that pointer. +static uint8_t* make_input_copy( mlt_image_format format, uint8_t *image, int width, int height ) +{ + int img_size = mlt_image_format_size( format, width, height, NULL ); + uint8_t* img_copy = (uint8_t*) mlt_pool_alloc( img_size ); + if ( format == mlt_image_yuv422 ) { + yuv422_to_yuv422p( image, img_copy, width, height ); + } else { + memcpy( img_copy, image, img_size ); + } + return img_copy; +} + static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { // Nothing to do! @@ -73,8 +428,9 @@ mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_log_debug( NULL, "filter_movit_convert: %s -> %s\n", - mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); + mlt_log_debug( NULL, "filter_movit_convert: %s -> %s (%d)\n", + mlt_image_format_name( *format ), mlt_image_format_name( output_format ), + mlt_frame_get_position( frame ) ); // Use CPU if glsl not initialized or not supported. GlslManager* glsl = GlslManager::get_instance(); @@ -88,216 +444,108 @@ int error = 0; int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); - int img_size = mlt_image_format_size( *format, width, height, NULL ); - mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); - mlt_service service = MLT_PRODUCER_SERVICE(producer); GlslManager::get_instance()->lock_service( frame ); - EffectChain* chain = GlslManager::get_chain( service ); - MltInput* input = GlslManager::get_input( service ); + + // If we're at the beginning of a series of Movit effects, store the input + // sent into the chain. + if ( output_format == mlt_image_glsl ) { + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + MltInput *input = create_input( properties, *format, profile->width, profile->height, width, height ); + GlslManager::set_input( producer, frame, input ); + uint8_t *img_copy = make_input_copy( *format, *image, width, height ); + GlslManager::set_input_pixel_pointer( producer, frame, img_copy ); - if ( !chain || !input ) { - GlslManager::get_instance()->unlock_service( frame ); - return 2; - } - - if ( *format != mlt_image_glsl ) { - bool finalize_chain = false; - if ( output_format == mlt_image_glsl_texture ) { - // We might already have a texture from a previous conversion from mlt_image_glsl. - glsl_texture texture = (glsl_texture) mlt_properties_get_data( properties, "movit.convert.texture", NULL ); - // XXX: requires a special property set on the frame by the app for now - // because we do not have reliable way to clear the texture property - // when a downstream filter has changed image. - if ( texture && mlt_properties_get_int( properties, "movit.convert.use_texture") ) { - *image = (uint8_t*) &texture->texture; - mlt_frame_set_image( frame, *image, 0, NULL ); - mlt_properties_set_int( properties, "format", output_format ); - *format = output_format; - GlslManager::get_instance()->unlock_service( frame ); - return error; - } else { - // Use a separate chain to convert image in RAM to OpenGL texture. - // Use cached chain if available and compatible. - Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); - chain = (EffectChain*) producer.get_data( "movit.convert.chain" ); - input = (MltInput*) producer.get_data( "movit.convert.input" ); - int w = producer.get_int( "movit.convert.width" ); - int h = producer.get_int( "movit.convert.height" ); - mlt_image_format f = (mlt_image_format) producer.get_int( "movit.convert.format" ); - if ( !chain || width != w || height != h || output_format != f ) { - chain = new EffectChain( width, height ); - input = new MltInput( width, height ); - chain->add_input( input ); - chain->add_effect( new Mlt::VerticalFlip() ); - finalize_chain = true; - producer.set( "movit.convert.chain", chain, 0, (mlt_destructor) delete_chain ); - producer.set( "movit.convert.width", width ); - producer.set( "movit.convert.height", height ); - producer.set( "movit.convert.width", output_format ); - } - } - } - if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { - input->useFlatInput( chain, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); - input->set_pixel_data( *image ); - } - else if ( *format == mlt_image_rgb24 ) { - input->useFlatInput( chain, FORMAT_RGB, width, height ); - input->set_pixel_data( *image ); - } - else if ( *format == mlt_image_yuv420p ) { - ImageFormat image_format; - YCbCrFormat ycbcr_format; - if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { - image_format.color_space = COLORSPACE_REC_709; - image_format.gamma_curve = GAMMA_REC_709; - ycbcr_format.luma_coefficients = YCBCR_REC_709; - } else if ( 576 == mlt_properties_get_int( properties, "height" ) ) { - image_format.color_space = COLORSPACE_REC_601_625; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } else { - image_format.color_space = COLORSPACE_REC_601_525; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } - ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); - ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; - // TODO: make new frame properties set by producers - ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; - ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; - input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); - input->set_pixel_data( *image ); - } - else if ( *format == mlt_image_yuv422 ) { - ImageFormat image_format; - YCbCrFormat ycbcr_format; - if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { - image_format.color_space = COLORSPACE_REC_709; - image_format.gamma_curve = GAMMA_REC_709; - ycbcr_format.luma_coefficients = YCBCR_REC_709; - } else if ( 576 == height ) { - image_format.color_space = COLORSPACE_REC_601_625; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } else { - image_format.color_space = COLORSPACE_REC_601_525; - image_format.gamma_curve = GAMMA_REC_601; - ycbcr_format.luma_coefficients = YCBCR_REC_601; - } - ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); - ycbcr_format.chroma_subsampling_x = 2; - ycbcr_format.chroma_subsampling_y = 1; - // TODO: make new frame properties set by producers - ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; - ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; - input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); - - // convert chunky to planar - uint8_t* planar = (uint8_t*) mlt_pool_alloc( img_size ); - yuv422_to_yuv422p( *image, planar, width, height ); - input->set_pixel_data( planar ); - mlt_frame_set_image( frame, planar, img_size, mlt_pool_release ); + *image = (uint8_t *) -1; + mlt_frame_set_image( frame, *image, 0, NULL ); + } + + // If we're at the _end_ of a series of Movit effects, render the chain. + if ( *format == mlt_image_glsl ) { + mlt_service leaf_service = (mlt_service) *image; + + if ( leaf_service == (mlt_service) -1 ) { + // Something on the way requested conversion to mlt_glsl, + // but never added an effect. Don't build a Movit chain; + // just do the conversion and we're done. + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + MltInput *input = GlslManager::get_input( producer, frame ); + *image = GlslManager::get_input_pixel_pointer( producer, frame ); + *format = input->get_format(); + delete input; + GlslManager::get_instance()->unlock_service( frame ); + return convert_on_cpu( frame, image, format, output_format ); } - // Finalize the separate conversion chain if needed. - if ( finalize_chain ) - chain->finalize(); - } - - if ( output_format != mlt_image_glsl ) { - glsl_fbo fbo = glsl->get_fbo( width, height ); - - if ( output_format == mlt_image_glsl_texture ) { - glsl_texture texture = glsl->get_texture( width, height, GL_RGBA ); - - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, width, height ); - - glFinish(); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); + // Construct the chain unless we already have a good one. + finalize_movit_chain( leaf_service, frame ); + + // Set per-frame parameters now that we know which Effect instances to set them on. + // (finalize_movit_chain may already have done this, though, but twice doesn't hurt.) + GlslChain *chain = GlslManager::get_chain( leaf_service ); + set_movit_parameters( chain, leaf_service, frame ); + + error = movit_render( chain->effect_chain, frame, format, output_format, width, height, image ); + + dispose_pixel_pointers( chain, leaf_service, frame ); + } + + // If we've been asked to render some frame directly to a texture (without any + // effects in-between), we create a new mini-chain to do so. + if ( *format != mlt_image_glsl && output_format == mlt_image_glsl_texture ) { + // We might already have a texture from a previous conversion from mlt_image_glsl. + glsl_texture texture = (glsl_texture) mlt_properties_get_data( properties, "movit.convert.texture", NULL ); + // XXX: requires a special property set on the frame by the app for now + // because we do not have reliable way to clear the texture property + // when a downstream filter has changed image. + if ( texture && mlt_properties_get_int( properties, "movit.convert.use_texture") ) { *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); - mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, - (mlt_destructor) GlslManager::release_texture, NULL ); - mlt_properties_set_int( properties, "format", output_format ); - *format = output_format; - } - else { - // Use a PBO to hold the data we read back with glReadPixels() - // (Intel/DRI goes into a slow path if we don't read to PBO) - GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? - GL_RGBA : GL_RGB; - img_size = width * height * ( gl_format == GL_RGB? 3 : 4 ); - glsl_pbo pbo = glsl->get_pbo( img_size ); - glsl_texture texture = glsl->get_texture( width, height, gl_format ); - - if ( fbo && pbo && texture ) { - // Set the FBO - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, width, height ); - - // Read FBO into PBO - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); - check_error(); - glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); - check_error(); - glReadPixels( 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); - check_error(); - - // Copy from PBO - uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); - check_error(); - *image = (uint8_t*) mlt_pool_alloc( img_size ); - mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); - memcpy( *image, buf, img_size ); - - if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) { - *format = mlt_image_rgb24; - error = convert_on_cpu( frame, image, format, output_format ); - } - - // Release PBO and FBO - glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); - check_error(); - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - glBindTexture( GL_TEXTURE_2D, 0 ); - check_error(); - mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, - (mlt_destructor) GlslManager::release_texture, NULL); - - mlt_properties_set_int( properties, "format", output_format ); - *format = output_format; + } else { + // Use a separate chain to convert image in RAM to OpenGL texture. + // Use cached chain if available and compatible. + Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); + EffectChain *chain = (EffectChain*) producer.get_data( "movit.convert.chain" ); + MltInput *input = (MltInput*) producer.get_data( "movit.convert.input" ); + int w = producer.get_int( "movit.convert.width" ); + int h = producer.get_int( "movit.convert.height" ); + mlt_image_format f = (mlt_image_format) producer.get_int( "movit.convert.format" ); + if ( !chain || !input || width != w || height != h || *format != f ) { + chain = new EffectChain( width, height, GlslManager::get_instance()->get_resource_pool() ); + input = create_input( properties, *format, width, height, width, height ); + chain->add_input( input->get_input() ); + chain->add_effect( new Mlt::VerticalFlip() ); + ImageFormat movit_output_format; + movit_output_format.color_space = COLORSPACE_sRGB; + movit_output_format.gamma_curve = GAMMA_sRGB; + chain->add_output(movit_output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); + chain->set_dither_bits(8); + chain->finalize(); + producer.set( "movit.convert.chain", chain, 0, (mlt_destructor) delete_chain ); + producer.set( "movit.convert.input", input, 0, NULL ); + producer.set( "movit.convert.width", width ); + producer.set( "movit.convert.height", height ); + producer.set( "movit.convert.format", *format ); } - else { - error = 1; + + if ( *format == mlt_image_yuv422 ) { + // We need to convert to planar, which make_input_copy() will do for us. + uint8_t *planar = make_input_copy( *format, *image, width, height ); + input->set_pixel_data( planar ); + error = movit_render( chain, frame, format, output_format, width, height, image ); + mlt_pool_release( planar ); + } else { + input->set_pixel_data( *image ); + error = movit_render( chain, frame, format, output_format, width, height, image ); } } - if ( fbo ) GlslManager::release_fbo( fbo ); - } - else { - mlt_properties_set_int( properties, "format", output_format ); - *format = output_format; } + GlslManager::get_instance()->unlock_service( frame ); + mlt_properties_set_int( properties, "format", output_format ); + *format = output_format; + return error; } @@ -320,7 +568,7 @@ return frame; } -static mlt_filter create_filter( mlt_profile profile, char *effect ) +static mlt_filter create_filter( mlt_profile profile, const char *effect ) { mlt_filter filter; char *id = strdup( effect ); @@ -348,6 +596,8 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); #ifdef WIN32 // XXX avcolor_space is crashing on Windows in this context! mlt_filter cpu_csc = NULL; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_crop.cpp mlt-0.9.2/src/modules/opengl/filter_movit_crop.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_crop.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_crop.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,8 +21,11 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +#include "optional_effect.h" + +using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { @@ -50,9 +53,12 @@ // Get the image as requested // *format = (mlt_image_format) mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format" ); + + // This is needed to prevent conversion to mlt_image_glsl after producer, + // deinterlace, or fieldorder, The latter two can force output of + // an image after it had already been converted to glsl. *format = mlt_image_none; - if ( mlt_properties_get_int( properties, "test_image" ) ) - *format = mlt_image_yuv422; + error = mlt_frame_get_image( frame, image, format, width, height, writable ); // Skip processing if requested. @@ -76,32 +82,31 @@ mlt_log_debug( MLT_FILTER_SERVICE(filter), "%dx%d -> %dx%d\n", *width, *height, owidth, oheight); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - bool ok = effect->set_int( "width", owidth ); - ok |= effect->set_int( "height", oheight ); - ok |= effect->set_float( "left", -left ); - ok |= effect->set_float( "top", -top ); - assert(ok); - *width = owidth; - *height = oheight; - } + mlt_properties_set_int( properties, "_movit.parms.int.width", owidth ); + mlt_properties_set_int( properties, "_movit.parms.int.height", oheight ); + mlt_properties_set_double( properties, "_movit.parms.float.left", -left ); + mlt_properties_set_double( properties, "_movit.parms.float.top", -top ); + + bool disable = ( *width == owidth && *height == oheight ); + mlt_properties_set_int( properties, "_movit.parms.int.disable", disable ); + GlslManager::get_instance()->unlock_service( frame ); } + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + Effect* effect = GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); + assert(effect); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + RGBATuple border_color( 0.0f, 0.0f, 0.0f, 1.0f ); + bool ok = effect->set_vec4( "border_color", (float*) &border_color ); + assert(ok); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); - if ( !GlslManager::init_chain( MLT_PRODUCER_SERVICE(producer) ) ) { - Effect* effect = GlslManager::add_effect( filter, frame, new PaddingEffect ); - RGBATuple border_color( 0.0f, 0.0f, 0.0f, 1.0f ); - bool ok = effect->set_vec4( "border_color", (float*) &border_color ); - assert(ok); - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -114,6 +119,8 @@ GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); filter->process = process; } return filter; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_deconvolution_sharpen.cpp mlt-0.9.2/src/modules/opengl/filter_movit_deconvolution_sharpen.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_deconvolution_sharpen.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_deconvolution_sharpen.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * filter_deconvolution_sharpen.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "filter_glsl_manager.h" +#include + +using namespace movit; + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + GlslManager::get_instance()->lock_service( frame ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + int matrix_size = mlt_properties_anim_get_int( properties, "matrix_size", position, length ); + mlt_properties_set_int( properties, "_movit.parms.int.matrix_size", matrix_size ); + mlt_properties_set_double( properties, "_movit.parms.float.circle_radius", + mlt_properties_anim_get_double( properties, "circle_radius", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.gaussian_radius", + mlt_properties_anim_get_double( properties, "gaussian_radius", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.correlation", + mlt_properties_anim_get_double( properties, "correlation", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.noise", + mlt_properties_anim_get_double( properties, "noise", position, length ) ); + + // DeconvolutionSharpenEffect compiles the matrix size into the shader, + // so we need to regenerate the chain if this changes. + char fingerprint[256]; + snprintf( fingerprint, sizeof( fingerprint ), "s=%d", matrix_size ); + mlt_properties_set( properties, "_movit fingerprint", fingerprint ); + + GlslManager::get_instance()->unlock_service( frame ); + *format = mlt_image_glsl; + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new DeconvolutionSharpenEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" { + +mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); + mlt_properties_set_int( properties, "matrix_size", 5 ); + mlt_properties_set_double( properties, "circle_radius", 2.0 ); + mlt_properties_set_double( properties, "gaussian_radius", 0.0 ); + mlt_properties_set_double( properties, "correlation", 0.95 ); + mlt_properties_set_double( properties, "noise", 0.01 ); + filter->process = process; + } + return filter; +} + +} diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_deconvolution_sharpen.yml mlt-0.9.2/src/modules/opengl/filter_movit_deconvolution_sharpen.yml --- mlt-0.9.0/src/modules/opengl/filter_movit_deconvolution_sharpen.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_deconvolution_sharpen.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,57 @@ +schema_version: 0.1 +type: filter +identifier: movit.sharpen +title: Deconvolution Sharpen (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + Deconvolution Sharpen is a filter that sharpens by way of deconvolution + (i.e., trying to reverse the blur kernel, as opposed to just boosting high + frequencies), more specifically by FIR Wiener filters. It is the same + algorithm as used by the (now largely abandoned) Refocus plug-in for GIMP, + and I suspect the same as in Photoshop's “Smart Sharpen” filter. + The effect gives generally better results than unsharp masking, but can be very + GPU intensive, and requires a fair bit of tweaking to get good results without + ringing and/or excessive noise. + +parameters: + - identifier: matrix_size + title: Matrix Size + type: integer + minimum: 0 + maximum: 25 + default: 5 + mutable: yes + + - identifier: circle_radius + title: Circle Radius + type: float + minimum: 0 + default: 2 + mutable: yes + + - identifier: gaussian_radius + title: Gaussian Radius + type: float + minimum: 0 + default: 0 + mutable: yes + + - identifier: correlation + title: Correlation + type: float + minimum: 0 + default: 0.95 + mutable: yes + + - identifier: noise + title: Noise Level + type: float + minimum: 0 + default: 0.01 + mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_diffusion.cpp mlt-0.9.2/src/modules/opengl/filter_movit_diffusion.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_diffusion.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_diffusion.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,35 +21,33 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_float( "radius", - mlt_properties_anim_get_double( properties, "radius", position, length ) ); - ok |= effect->set_float( "blurred_mix_amount", - mlt_properties_anim_get_double( properties, "mix", position, length ) ); - assert(ok); - } + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_properties_set_double( properties, "_movit.parms.float.radius", + mlt_properties_anim_get_double( properties, "radius", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.blurred_mix_amount", + mlt_properties_anim_get_double( properties, "mix", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new DiffusionEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new DiffusionEffect() ); - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -64,6 +62,7 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 3.0 ); mlt_properties_set_double( properties, "mix", 0.3 ); filter->process = process; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_glow.cpp mlt-0.9.2/src/modules/opengl/filter_movit_glow.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_glow.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_glow.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,37 +21,35 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_float( "radius", - mlt_properties_anim_get_double( properties, "radius", position, length ) ); - ok |= effect->set_float( "blurred_mix_amount", - mlt_properties_anim_get_double( properties, "blur_mix", position, length ) ); - ok |= effect->set_float( "highlight_cutoff", - mlt_properties_anim_get_double( properties, "highlight_cutoff", position, length ) ); - assert(ok); - } + mlt_properties_set_double( properties, "_movit.parms.float.radius", + mlt_properties_anim_get_double( properties, "radius", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.blurred_mix_amount", + mlt_properties_anim_get_double( properties, "blur_mix", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.highlight_cutoff", + mlt_properties_anim_get_double( properties, "highlight_cutoff", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new GlowEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new GlowEffect() ); - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -66,6 +64,7 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 20.0 ); mlt_properties_set_double( properties, "blur_mix", 1.0 ); mlt_properties_set_double( properties, "highlight_cutoff", 0.2 ); diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_lift_gamma_gain.cpp mlt-0.9.2/src/modules/opengl/filter_movit_lift_gamma_gain.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_lift_gamma_gain.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_lift_gamma_gain.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * filter_lift_gamma_gain.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "filter_glsl_manager.h" +#include + +using namespace movit; + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + GlslManager::get_instance()->lock_service( frame ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_properties_set_double( properties, "_movit.parms.vec3.lift[0]", + mlt_properties_anim_get_double( properties, "lift_r", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.lift[1]", + mlt_properties_anim_get_double( properties, "lift_g", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.lift[2]", + mlt_properties_anim_get_double( properties, "lift_b", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[0]", + mlt_properties_anim_get_double( properties, "gamma_r", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[1]", + mlt_properties_anim_get_double( properties, "gamma_g", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[2]", + mlt_properties_anim_get_double( properties, "gamma_b", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gain[0]", + mlt_properties_anim_get_double( properties, "gain_r", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gain[1]", + mlt_properties_anim_get_double( properties, "gain_g", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.vec3.gain[2]", + mlt_properties_anim_get_double( properties, "gain_b", position, length ) ); + GlslManager::get_instance()->unlock_service( frame ); + *format = mlt_image_glsl; + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new LiftGammaGainEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" { + +mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); + mlt_properties_set_double( properties, "lift_r", 0.0 ); + mlt_properties_set_double( properties, "lift_g", 0.0 ); + mlt_properties_set_double( properties, "lift_b", 0.0 ); + mlt_properties_set_double( properties, "gamma_r", 1.0 ); + mlt_properties_set_double( properties, "gamma_g", 1.0 ); + mlt_properties_set_double( properties, "gamma_b", 1.0 ); + mlt_properties_set_double( properties, "gain_r", 1.0 ); + mlt_properties_set_double( properties, "gain_g", 1.0 ); + mlt_properties_set_double( properties, "gain_b", 1.0 ); + filter->process = process; + } + return filter; +} + +} diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_lift_gamma_gain.yml mlt-0.9.2/src/modules/opengl/filter_movit_lift_gamma_gain.yml --- mlt-0.9.0/src/modules/opengl/filter_movit_lift_gamma_gain.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_lift_gamma_gain.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,89 @@ +schema_version: 0.1 +type: filter +identifier: movit.lift_gamma_gain +title: Lift, Gamma, and Gain (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A simple lift/gamma/gain effect, used for color grading. +notes: > + Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, + although all parameters affect the entire curve. Mathematically speaking, + it is a bit unusual to look at gamma as a color, but it works pretty well + in practice. + The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). + The lift is actually a case where we actually would _not_ want linear light; + since black by definition becomes equal to the lift color, we want lift to + be pretty close to black, but in linear light that means lift affects the + rest of the curve relatively little. Thus, we actually convert to gamma 2.2 + before lift, and then back again afterwards. (Gain and gamma are, + up to constants, commutative with the de-gamma operation.) + +parameters: + - identifier: lift_r + title: Lift Red + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_g + title: Lift Green + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_b + title: Lift Blue + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: gamma_r + title: Gamma Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_g + title: Gamma Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_b + title: Gamma Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_r + title: Gain Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_g + title: Gain Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_b + title: Gain Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_mirror.cpp mlt-0.9.2/src/modules/opengl/filter_movit_mirror.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_mirror.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_mirror.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,16 +21,26 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + *format = mlt_image_glsl; + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new MirrorEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; +} + static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( !effect ) - GlslManager::add_effect( filter, frame, new MirrorEffect() ); - } + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); return frame; } @@ -42,6 +52,8 @@ GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); filter->process = process; } return filter; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_opacity.cpp mlt-0.9.2/src/modules/opengl/filter_movit_opacity.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_opacity.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_opacity.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,39 +21,35 @@ #include #include -#include "glsl_manager.h" -#include +#include "filter_glsl_manager.h" +#include + +using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_float( "strength_first", - mlt_properties_anim_get_double( properties, "opacity", position, length ) ); - assert(ok); - } + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + double opacity = mlt_properties_anim_get_double( properties, "opacity", position, length ); + double alpha = mlt_properties_anim_get_double( properties, "alpha", position, length ); + mlt_properties_set_double( properties, "_movit.parms.vec4.factor[0]", opacity ); + mlt_properties_set_double( properties, "_movit.parms.vec4.factor[1]", opacity ); + mlt_properties_set_double( properties, "_movit.parms.vec4.factor[2]", opacity ); + mlt_properties_set_double( properties, "_movit.parms.vec4.factor[3]", alpha >= 0 ? alpha : opacity ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new MultiplyEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( !effect ) { - effect = GlslManager::add_effect( filter, frame, new MixEffect, 0 ); - assert(effect); - bool ok = effect->set_float( "strength_first", 1.0f ); - ok |= effect->set_float( "strength_second", 0.0f ); - assert(ok); - } - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -67,7 +63,9 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set( properties, "opacity", arg? arg : "1" ); + mlt_properties_set_double( properties, "alpha", -1.0 ); filter->process = process; } return filter; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_opacity.yml mlt-0.9.2/src/modules/opengl/filter_movit_opacity.yml --- mlt-0.9.0/src/modules/opengl/filter_movit_opacity.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_opacity.yml 2014-06-29 20:23:17.000000000 +0000 @@ -15,6 +15,7 @@ the other video. If the video this is applied to already has an alpha channel, then this preserves that by multiplying the opacity level with the alpha channel. This filter is especially handy when used in conjunction with movit.overlay. + You can also use this to fade a clip from and to black. parameters: - identifier: opacity @@ -24,3 +25,16 @@ maximum: 1 default: 1 mutable: yes + + - identifier: alpha + title: Alpha + description: > + When this is less than zero, the alpha component of the Movit mutliply + effect follows opacity. Otherwise, you can set this to another value to + control the alpha component independently. If you set this to 1, then + the opacity parameter can be used to fade to and from black. + type: float + minimum: -1 + maximum: 1 + default: -1 + mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_rect.cpp mlt-0.9.2/src/modules/opengl/filter_movit_rect.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_rect.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_rect.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -18,7 +18,9 @@ */ #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" + +using namespace movit; static mlt_frame process( mlt_filter filter, mlt_frame frame ) { @@ -39,6 +41,8 @@ GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set( MLT_FILTER_PROPERTIES(filter), "rect", arg ); mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "fill", 1 ); filter->process = process; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_resample.cpp mlt-0.9.2/src/modules/opengl/filter_movit_resample.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_resample.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_resample.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,12 +21,14 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +#include "optional_effect.h" + +using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); @@ -60,30 +62,34 @@ if ( iheight != oheight ) mlt_properties_set_int( properties, "consumer_deinterlace", 1 ); + GlslManager::get_instance()->lock_service( frame ); + mlt_properties_set_int( filter_properties, "_movit.parms.int.width", owidth ); + mlt_properties_set_int( filter_properties, "_movit.parms.int.height", oheight ); + + bool disable = ( iwidth == owidth && iheight == oheight ); + mlt_properties_set_int( filter_properties, "_movit.parms.int.disable", disable ); + + *width = owidth; + *height = oheight; + + GlslManager::get_instance()->unlock_service( frame ); + // Get the image as requested if ( *format != mlt_image_none ) *format = mlt_image_glsl; - error = mlt_frame_get_image( frame, image, format, &iwidth, &iheight, writable ); - if ( !error ) { - GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - bool ok = effect->set_int( "width", owidth ); - ok |= effect->set_int( "height", oheight ); - assert(ok); - *width = owidth; - *height = oheight; - } - GlslManager::get_instance()->unlock_service( frame ); - } - - return error; + int error = mlt_frame_get_image( frame, image, format, &iwidth, &iheight, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + Effect *effect = GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); + // This needs to be something else than 0x0 at chain finalization time. + bool ok = effect->set_int("width", owidth); + ok |= effect->set_int("height", oheight); + assert( ok ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new ResampleEffect ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -96,6 +102,8 @@ GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); filter->process = process; } return filter; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_resize.cpp mlt-0.9.2/src/modules/opengl/filter_movit_resize.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_resize.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_resize.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -23,9 +23,12 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include #include +#include "optional_effect.h" + +using namespace movit; static float alignment_parse( char* align ) { @@ -156,16 +159,22 @@ } if ( !error ) { + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - bool ok = effect->set_int( "width", *width ); - ok |= effect->set_int( "height", *height ); - ok |= effect->set_float( "left", rect.x ); - ok |= effect->set_float( "top", rect.y ); - assert(ok); - } + mlt_properties_set_int( filter_properties, "_movit.parms.int.width", *width ); + mlt_properties_set_int( filter_properties, "_movit.parms.int.height", *height ); + mlt_properties_set_double( filter_properties, "_movit.parms.float.left", rect.x ); + mlt_properties_set_double( filter_properties, "_movit.parms.float.top", rect.y ); + + bool disable = ( *width == owidth && *height == oheight ); + mlt_properties_set_int( filter_properties, "_movit.parms.int.disable", disable ); + GlslManager::get_instance()->unlock_service( frame ); + + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } return error; @@ -173,8 +182,6 @@ static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new PaddingEffect ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -188,6 +195,8 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); filter->process = process; } return filter; diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_saturation.cpp mlt-0.9.2/src/modules/opengl/filter_movit_saturation.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_saturation.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_saturation.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,33 +21,31 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_float( "saturation", - mlt_properties_anim_get_double( properties, "saturation", position, length ) ); - assert(ok); - } + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_properties_set_double( properties, "_movit.parms.float.saturation", + mlt_properties_anim_get_double( properties, "saturation", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new SaturationEffect() ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new SaturationEffect() ); - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -62,6 +60,7 @@ if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); mlt_properties_set( properties, "saturation", arg? arg : "1.0" ); filter->process = process; } diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_vignette.cpp mlt-0.9.2/src/modules/opengl/filter_movit_vignette.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_vignette.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_vignette.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -21,35 +21,33 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include +using namespace movit; + static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - bool ok = effect->set_float( "radius", - mlt_properties_anim_get_double( properties, "radius", position, length ) ); - ok |= effect->set_float( "inner_radius", - mlt_properties_anim_get_double( properties, "inner_radius", position, length ) ); - assert(ok); - } + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_properties_set_double( properties, "_movit.parms.float.radius", + mlt_properties_anim_get_double( properties, "radius", position, length ) ); + mlt_properties_set_double( properties, "_movit.parms.float.inner_radius", + mlt_properties_anim_get_double( properties, "inner_radius", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new VignetteEffect() ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new VignetteEffect() ); - } mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; @@ -63,6 +61,8 @@ GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); filter->process = process; mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "radius", 0.3 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "inner_radius", 0.3 ); diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_white_balance.cpp mlt-0.9.2/src/modules/opengl/filter_movit_white_balance.cpp --- mlt-0.9.0/src/modules/opengl/filter_movit_white_balance.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_white_balance.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * filter_white_balance.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "filter_glsl_manager.h" +#include + +using namespace movit; + +static double srgb8_to_linear(int c) +{ + double x = c / 255.0f; + if (x < 0.04045f) { + return (1.0/12.92f) * x; + } else { + return pow((x + 0.055) * (1.0/1.055f), 2.4); + } +} + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + GlslManager::get_instance()->lock_service( frame ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + int color_int = mlt_properties_anim_get_int( properties, "neutral_color", position, length ); + RGBTriplet color( + srgb8_to_linear((color_int >> 24) & 0xff), + srgb8_to_linear((color_int >> 16) & 0xff), + srgb8_to_linear((color_int >> 8) & 0xff) + ); + mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[0]", color.r ); + mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[1]", color.g ); + mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[2]", color.b ); + double output_color_temperature = + mlt_properties_anim_get_double( properties, "color_temperature", position, length ); + mlt_properties_set_double( properties, "_movit.parms.float.output_color_temperature", + output_color_temperature ); + GlslManager::get_instance()->unlock_service( frame ); + *format = mlt_image_glsl; + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); + GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new WhiteBalanceEffect ); + *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" { + +mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + glsl->add_ref( properties ); + mlt_properties_set( properties, "neutral_color", arg? arg : "#7f7f7f" ); + mlt_properties_set_double( properties, "color_temperature", 6500.0 ); + filter->process = process; + } + return filter; +} + +} diff -Nru mlt-0.9.0/src/modules/opengl/filter_movit_white_balance.yml mlt-0.9.2/src/modules/opengl/filter_movit_white_balance.yml --- mlt-0.9.0/src/modules/opengl/filter_movit_white_balance.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_movit_white_balance.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,29 @@ +schema_version: 0.1 +type: filter +identifier: movit.white_balance +title: White Balance (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: Color correction in LMS color space. + +parameters: + - identifier: neutral_color + title: Neutral Color + type: string + widget: color + default: 0x7f7f7f00 + mutable: yes + + - identifier: color_temperature + title: Color Temperature + type: float + minimum: 1000.0 + maximum: 15000.0 + default: 6500.0 + unit: Kelvin + mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/filter_white_balance.cpp mlt-0.9.2/src/modules/opengl/filter_white_balance.cpp --- mlt-0.9.0/src/modules/opengl/filter_white_balance.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_white_balance.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * filter_white_balance.cpp - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "glsl_manager.h" -#include - -static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - GlslManager::get_instance()->lock_service( frame ); - Effect* effect = GlslManager::get_effect( filter, frame ); - if ( effect ) { - mlt_position position = mlt_filter_get_position( filter, frame ); - mlt_position length = mlt_filter_get_length2( filter, frame ); - int color_int = mlt_properties_anim_get_int( properties, "neutral_color", position, length ); - RGBTriplet color( - float((color_int >> 24) & 0xff) / 255.0f, - float((color_int >> 16) & 0xff) / 255.0f, - float((color_int >> 8) & 0xff) / 255.0f - ); - bool ok = effect->set_vec3( "neutral_color", (float*) &color ); - ok |= effect->set_float( "output_color_temperature", - mlt_properties_anim_get_double( properties, "color_temperature", position, length ) ); - assert(ok); - } - GlslManager::get_instance()->unlock_service( frame ); - *format = mlt_image_glsl; - return mlt_frame_get_image( frame, image, format, width, height, writable ); -} - -static mlt_frame process( mlt_filter filter, mlt_frame frame ) -{ - if ( !mlt_frame_is_test_card( frame ) ) { - if ( !GlslManager::get_effect( filter, frame ) ) - GlslManager::add_effect( filter, frame, new WhiteBalanceEffect ); - } - mlt_frame_push_service( frame, filter ); - mlt_frame_push_get_image( frame, get_image ); - return frame; -} - -extern "C" { - -mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter filter = NULL; - GlslManager* glsl = GlslManager::get_instance(); - - if ( glsl && ( filter = mlt_filter_new() ) ) { - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties_set( properties, "neutral_color", arg? arg : "#7f7f7f" ); - mlt_properties_set_double( properties, "color_temperature", 6500.0 ); - filter->process = process; - } - return filter; -} - -} diff -Nru mlt-0.9.0/src/modules/opengl/filter_white_balance.yml mlt-0.9.2/src/modules/opengl/filter_white_balance.yml --- mlt-0.9.0/src/modules/opengl/filter_white_balance.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/filter_white_balance.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: movit.white_balance -title: White Balance (GLSL) -version: 1 -copyright: Dan Dennedy -creator: Steinar H. Gunderson -license: GPLv2 -language: en -tags: - - Video -description: Color correction in LMS color space. - -parameters: - - identifier: neutral_color - title: Neutral Color - type: string - widget: color - default: 0x7f7f7f00 - mutable: yes - - - identifier: color_temperature - title: Color Temperature - type: float - minimum: 1000.0 - maximum: 15000.0 - default: 6500.0 - unit: Kelvin - mutable: yes diff -Nru mlt-0.9.0/src/modules/opengl/glsl_manager.h mlt-0.9.2/src/modules/opengl/glsl_manager.h --- mlt-0.9.0/src/modules/opengl/glsl_manager.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/glsl_manager.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - * glsl_manager.h - * Copyright (C) 2013 Dan Dennedy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef GLSL_MANAGER_H -#define GLSL_MANAGER_H - -#include -#include -#include - -#define MAXLISTCOUNT 1024 -typedef struct glsl_list_s *glsl_list; -struct glsl_list_s -{ - void *items[MAXLISTCOUNT]; - int count; - - int ( *add )( glsl_list, void* ); - void* ( *at )( glsl_list, int ); - void* ( *take_at )( glsl_list, int ); - void* ( *take )( glsl_list, void* ); -}; - -struct glsl_texture_s -{ - int used; - GLuint texture; - int width; - int height; - GLint internal_format; -}; -typedef struct glsl_texture_s *glsl_texture; - -struct glsl_fbo_s -{ - int used; - int width; - int height; - GLuint fbo; -}; -typedef struct glsl_fbo_s *glsl_fbo; - -struct glsl_pbo_s -{ - int size; - GLuint pbo; -}; -typedef struct glsl_pbo_s *glsl_pbo; - -class Effect; -class EffectChain; -class MltInput; - -class GlslManager : public Mlt::Filter -{ -public: - GlslManager(); - ~GlslManager(); - static GlslManager* get_instance(); - - glsl_fbo get_fbo(int width, int height); - static void release_fbo(glsl_fbo); - glsl_texture get_texture(int width, int height, GLint internal_format); - static void release_texture(glsl_texture); - glsl_pbo get_pbo(int size); - - Properties effect_list( Mlt::Service &service ); - static bool init_chain(mlt_service); - static EffectChain* get_chain(mlt_service); - static MltInput* get_input(mlt_service); - static void reset_finalized(mlt_service); - static Effect* get_effect(mlt_filter, mlt_frame); - static Effect* add_effect(mlt_filter, mlt_frame, Effect*); - static Effect* add_effect(mlt_filter, mlt_frame, Effect*, Effect* input_b); - static void render(mlt_service, void *chain, GLuint fbo, int width, int height); - static void lock_service(mlt_frame frame); - static void unlock_service(mlt_frame frame); - -private: - static void onInit( mlt_properties owner, GlslManager* filter ); - static void onServiceChanged( mlt_properties owner, mlt_service service ); - static void onPropertyChanged( mlt_properties owner, mlt_service service, const char* property ); - Mlt::Deque fbo_list; - Mlt::Deque texture_list; - glsl_pbo pbo; - Mlt::Event* initEvent; -}; - -#endif // GLSL_MANAGER_H diff -Nru mlt-0.9.0/src/modules/opengl/Makefile mlt-0.9.2/src/modules/opengl/Makefile --- mlt-0.9.0/src/modules/opengl/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -1,22 +1,21 @@ -CFLAGS += -I../.. +include ../../../config.mak -LDFLAGS += -L../../framework -lmlt -lm +CFLAGS := -I../.. $(CFLAGS) -include ../../../config.mak +LDFLAGS := -L../../framework -lmlt -lm $(LDFLAGS) TARGET = ../libmltopengl$(LIBSUF) OBJS = factory.o -CPPOBJS = fbo_input.o -CPPOBJS += filter_glsl_manager.o +CPPOBJS = filter_glsl_manager.o CPPOBJS += filter_movit_blur.o CPPOBJS += filter_movit_convert.o CPPOBJS += filter_movit_crop.o -CPPOBJS += filter_deconvolution_sharpen.o +CPPOBJS += filter_movit_deconvolution_sharpen.o CPPOBJS += filter_movit_diffusion.o CPPOBJS += filter_movit_glow.o -CPPOBJS += filter_lift_gamma_gain.o +CPPOBJS += filter_movit_lift_gamma_gain.o CPPOBJS += filter_movit_mirror.o CPPOBJS += filter_movit_opacity.o CPPOBJS += filter_movit_rect.o @@ -24,27 +23,28 @@ CPPOBJS += filter_movit_resize.o CPPOBJS += filter_movit_saturation.o CPPOBJS += filter_movit_vignette.o -CPPOBJS += filter_white_balance.o +CPPOBJS += filter_movit_white_balance.o CPPOBJS += mlt_movit_input.o +CPPOBJS += transition_movit_luma.o CPPOBJS += transition_movit_mix.o CPPOBJS += transition_movit_overlay.o -CXXFLAGS += -Wno-deprecated $(CFLAGS) -CXXFLAGS += `pkg-config --cflags movit 2> /dev/null` +CXXFLAGS := -Wno-deprecated $(CFLAGS) $(CXXFLAGS) +CXXFLAGS += $(shell pkg-config --cflags movit 2> /dev/null) -SHADERDIR = `pkg-config --variable=shaderdir movit` +SHADERDIR = $(shell pkg-config --variable=shaderdir movit) CXXFLAGS += -DSHADERDIR=\"$(SHADERDIR)\" LDFLAGS += -L../../mlt++ -lmlt++ ifeq ($(targetos), MinGW) - CXXFLAGS += `pkg-config --cflags glew` - LDFLAGS += -lmovit `pkg-config --libs-only-L glew` -lglew32 -lopengl32 + CXXFLAGS += $(shell pkg-config --cflags movit) + LDFLAGS += $(shell pkg-config --libs movit) -lopengl32 -lpthread else - LDFLAGS += `pkg-config --libs movit 2> /dev/null` + LDFLAGS += $(shell pkg-config --libs movit 2> /dev/null) ifeq ($(targetos), Darwin) CXXFLAGS += -FOpenGL - LDFLAGS += -framework OpenGL + LDFLAGS += -lpthread -framework OpenGL else OBJS += consumer_xgl.o LDFLAGS += -lpthread -lGL -lX11 diff -Nru mlt-0.9.0/src/modules/opengl/mlt_flip_effect.h mlt-0.9.2/src/modules/opengl/mlt_flip_effect.h --- mlt-0.9.0/src/modules/opengl/mlt_flip_effect.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/mlt_flip_effect.h 2014-06-29 20:23:17.000000000 +0000 @@ -23,7 +23,7 @@ namespace Mlt { -class VerticalFlip : public Effect { +class VerticalFlip : public movit::Effect { public: VerticalFlip() {} virtual std::string effect_type_id() const { return "MltVerticalFlip"; } diff -Nru mlt-0.9.0/src/modules/opengl/mlt_movit_input.cpp mlt-0.9.2/src/modules/opengl/mlt_movit_input.cpp --- mlt-0.9.0/src/modules/opengl/mlt_movit_input.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/mlt_movit_input.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -18,72 +18,21 @@ */ #include "mlt_movit_input.h" -#include "fbo_input.h" -MltInput::MltInput(unsigned width, unsigned height) - : m_width(width) - , m_height(height) - , output_linear_gamma(false) - , needs_mipmaps(false) +using namespace movit; + +MltInput::MltInput( mlt_image_format format ) + : m_format(format) , input(0) , isRGB(true) { - register_int("output_linear_gamma", &output_linear_gamma); - register_int("needs_mipmaps", &needs_mipmaps); } MltInput::~MltInput() { - // XXX: this is crashing when a producer is closed - // on Windows when using melt with qglsl. -// delete input; -} - -std::string MltInput::output_fragment_shader() -{ - assert(input); - return input->output_fragment_shader(); -} - -void MltInput::set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) -{ - assert(input); - input->set_gl_state(glsl_program_num, prefix, sampler_num); -} - -Effect::AlphaHandling MltInput::alpha_handling() const -{ - assert(input); - return input->alpha_handling(); -} - -void MltInput::finalize() -{ - assert(input); - bool ok = input->set_int("output_linear_gamma", output_linear_gamma); - ok |= input->set_int("needs_mipmaps", needs_mipmaps); - assert(ok); - input->finalize(); -} - -bool MltInput::can_output_linear_gamma() const -{ - assert(input); - return input->can_output_linear_gamma(); -} - -Colorspace MltInput::get_color_space() const -{ - assert(input); - return input->get_color_space(); -} -GammaCurve MltInput::get_gamma_curve() const -{ - assert(input); - return input->get_gamma_curve(); } -void MltInput::useFlatInput(EffectChain* chain, MovitPixelFormat pix_fmt, unsigned width, unsigned height) +void MltInput::useFlatInput(MovitPixelFormat pix_fmt, unsigned width, unsigned height) { if (!input) { m_width = width; @@ -92,36 +41,20 @@ image_format.color_space = COLORSPACE_sRGB; image_format.gamma_curve = GAMMA_sRGB; input = new FlatInput(image_format, pix_fmt, GL_UNSIGNED_BYTE, width, height); - chain->add_output(image_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); - chain->set_dither_bits(8); } } -void MltInput::useYCbCrInput(EffectChain* chain, const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height) +void MltInput::useYCbCrInput(const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height) { if (!input) { m_width = width; m_height = height; input = new YCbCrInput(image_format, ycbcr_format, width, height); - ImageFormat output_format; - output_format.color_space = COLORSPACE_sRGB; - output_format.gamma_curve = GAMMA_sRGB; - chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); - chain->set_dither_bits(8); isRGB = false; m_ycbcr_format = ycbcr_format; } } -void MltInput::useFBOInput(EffectChain *chain, GLuint texture) -{ - if (!input) { - FBOInput* fboInput = new FBOInput(m_width, m_height); - input = fboInput; - fboInput->set_texture(texture); - } -} - void MltInput::set_pixel_data(const unsigned char* data) { assert(input); @@ -135,3 +68,15 @@ ycbcr->set_pixel_data(2, &data[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); } } + +void MltInput::invalidate_pixel_data() +{ + assert(input); + if (isRGB) { + FlatInput* flat = (FlatInput*) input; + flat->invalidate_pixel_data(); + } else { + YCbCrInput* ycbcr = (YCbCrInput*) input; + ycbcr->invalidate_pixel_data(); + } +} diff -Nru mlt-0.9.0/src/modules/opengl/mlt_movit_input.h mlt-0.9.2/src/modules/opengl/mlt_movit_input.h --- mlt-0.9.0/src/modules/opengl/mlt_movit_input.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/mlt_movit_input.h 2014-06-29 20:23:17.000000000 +0000 @@ -20,42 +20,35 @@ #ifndef MLT_MOVIT_INPUT_H #define MLT_MOVIT_INPUT_H +#include + #include #include #include -class MltInput : public Input +class MltInput { public: - MltInput(unsigned width, unsigned height); + MltInput( mlt_image_format format ); ~MltInput(); - // Effect overrides - std::string effect_type_id() const { return "MltInput"; } - Effect::AlphaHandling alpha_handling() const; - std::string output_fragment_shader(); - void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num); - - // Input ovverrides - void finalize(); - bool can_output_linear_gamma() const; - unsigned get_width() const { return m_width; } - unsigned get_height() const { return m_height; } - Colorspace get_color_space() const; - GammaCurve get_gamma_curve() const; - - // Custom methods - void useFlatInput(EffectChain* chain, MovitPixelFormat pix_fmt, unsigned width, unsigned height); - void useYCbCrInput(EffectChain* chain, const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height); - void useFBOInput(EffectChain* chain, GLuint texture); + void useFlatInput(movit::MovitPixelFormat pix_fmt, unsigned width, unsigned height); + void useYCbCrInput(const movit::ImageFormat& image_format, const movit::YCbCrFormat& ycbcr_format, unsigned width, unsigned height); void set_pixel_data(const unsigned char* data); + void invalidate_pixel_data(); + movit::Input *get_input() { return input; } + + // The original pixel format that was used to create this MltInput, + // in case we change our mind later and want to convert on the CPU instead. + mlt_image_format get_format() const { return m_format; } private: + mlt_image_format m_format; unsigned m_width, m_height; - int output_linear_gamma, needs_mipmaps; - Input *input; + // Note: Owned by the EffectChain, so should not be deleted by us. + movit::Input *input; bool isRGB; - YCbCrFormat m_ycbcr_format; + movit::YCbCrFormat m_ycbcr_format; }; #endif // MLT_MOVIT_INPUT_H diff -Nru mlt-0.9.0/src/modules/opengl/optional_effect.h mlt-0.9.2/src/modules/opengl/optional_effect.h --- mlt-0.9.0/src/modules/opengl/optional_effect.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/optional_effect.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,29 @@ +#ifndef OPTIONAL_EFFECT_H +#define OPTIONAL_EFFECT_H + +#include +#include +#include + +// A wrapper effect that, at rewrite time, can remove itself entirely from the loop. +// It does so if "disable" is set to a nonzero value at finalization time. +template +class OptionalEffect : public T { +public: + OptionalEffect() : disable(0) { this->register_int("disable", &disable); } + virtual std::string effect_type_id() const { return "OptionalEffect[" + T::effect_type_id() + "]"; } + virtual void rewrite_graph(movit::EffectChain *graph, movit::Node *self) { + if (disable) { + assert(self->incoming_links.size() == 1); + graph->replace_sender(self, self->incoming_links[0]); + self->disabled = true; + } else { + T::rewrite_graph(graph, self); + } + } + +private: + int disable; +}; + +#endif diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_luma.cpp mlt-0.9.2/src/modules/opengl/transition_movit_luma.cpp --- mlt-0.9.0/src/modules/opengl/transition_movit_luma.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_luma.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,195 @@ +/* + * transition_movit_luma.cpp + * Copyright (C) 2014 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include +#include +#include + +#include "filter_glsl_manager.h" +#include +#include +#include +#include +#include +#include "mlt_movit_input.h" + +using namespace movit; + +static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error; + + // Get the transition object + mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); + mlt_service service = MLT_TRANSITION_SERVICE( transition ); + + // Get the b frame from the stack + mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + mlt_frame c_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + + // Get the properties of the transition + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + mlt_service_lock( service ); + + // Get the transition parameters + mlt_position position = mlt_transition_get_position( transition, a_frame ); + mlt_position length = mlt_transition_get_length( transition ); + int reverse = mlt_properties_get_int( properties, "reverse" ); + double mix = mlt_transition_get_progress( transition, a_frame ); + double inverse = 1.0 - mix; + double softness = mlt_properties_anim_get_double( properties, "softness", position, length ); + + if ( c_frame ) + { + // Set the Movit parameters. + mlt_properties_set( properties, "_movit.parms.float.strength_first", NULL ); + mlt_properties_set( properties, "_movit.parms.float.strength_second", NULL ); + mlt_properties_set_double( properties, "_movit.parms.float.progress", reverse ? inverse : mix ); + mlt_properties_set_double( properties, "_movit.parms.float.transition_width", 1.0 / (softness + 1.0e-4) ); + mlt_properties_set_int( properties, "_movit.parms.int.inverse", + !mlt_properties_get_int( properties, "invert" ) ); + + uint8_t *a_image, *b_image, *c_image; + + // Get the images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + error = mlt_frame_get_image( c_frame, &c_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect_third_input( service, a_frame, (mlt_service) c_image, c_frame ); + GlslManager::set_effect( service, a_frame, new LumaMixEffect() ); + } + else + { + // Set the Movit parameters. + mlt_properties_set( properties, "_movit.parms.int.inverse", NULL ); + mlt_properties_set( properties, "_movit.parms.float.progress", NULL ); + mlt_properties_set( properties, "_movit.parms.float.transition_width", NULL ); + mlt_properties_set_double( properties, "_movit.parms.float.strength_first", reverse ? mix : inverse ); + mlt_properties_set_double( properties, "_movit.parms.float.strength_second", reverse ? inverse : mix ); + + uint8_t *a_image, *b_image; + + // Get the two images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect( service, a_frame, new MixEffect() ); + } + *image = (uint8_t *) service; + + mlt_service_unlock( service ); + return error; +} + +static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + // Obtain the wipe producer. + char *resource = mlt_properties_get( properties, "resource" ); + char *last_resource = mlt_properties_get( properties, "_resource" ); + mlt_producer producer = (mlt_producer) mlt_properties_get_data( properties, "instance", NULL ); + + // If we haven't created the wipe producer or it has changed + if ( resource ) + if ( !producer || strcmp( resource, last_resource ) ) { + char temp[ 512 ]; + char *extension = strrchr( resource, '.' ); + + // Store the last resource now + mlt_properties_set( properties, "_resource", resource ); + + // This is a hack - the idea is that we can indirectly reference the + // luma modules pgm or png images by a short cut like %luma01.pgm - we then replace + // the % with the full path to the image and use it if it exists, if not, check for + // the file ending in a .png, and failing that, default to a fade in + if ( strchr( resource, '%' ) ) { + FILE *test; + sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 ); + test = fopen( temp, "r" ); + + if ( test == NULL ) + { + strcat( temp, ".png" ); + test = fopen( temp, "r" ); + } + + if ( test ) + fclose( test ); + else + strcpy( temp, "colour:0x00000080" ); + + resource = temp; + extension = strrchr( resource, '.' ); + } + + mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); + producer = mlt_factory_producer( profile, NULL, resource ); + if ( producer != NULL ) { + mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); + } + mlt_properties_set_data( properties, "instance", producer, 0, (mlt_destructor) mlt_producer_close, NULL ); + } + // We may still not have a producer in which case, we do nothing + if ( producer ) { + mlt_frame wipe = NULL; + mlt_position position = mlt_transition_get_position( transition, a_frame ); + mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." ); + mlt_producer_seek( producer, position ); + if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &wipe, 0 ) == 0 ) { + char *name = mlt_properties_get( properties, "_unique_id" ); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), name, wipe, 0, (mlt_destructor) mlt_frame_close, NULL ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES(wipe), "distort", 1 ); + mlt_frame_push_frame( a_frame, wipe ); + } else { + mlt_frame_push_frame( a_frame, NULL ); + } + } else { + mlt_frame_push_frame( a_frame, NULL ); + } + mlt_frame_push_frame( a_frame, b_frame ); + mlt_frame_push_service( a_frame, transition ); + mlt_frame_push_get_image( a_frame, get_image ); + + return a_frame; +} + +extern "C" +mlt_transition transition_movit_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_transition transition = NULL; + GlslManager* glsl = GlslManager::get_instance(); + if ( glsl && ( transition = mlt_transition_new() ) ) { + transition->process = process; + mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "resource", arg ); + + // Inform apps and framework that this is a video only transition + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); + } + return transition; +} diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_luma.yml mlt-0.9.2/src/modules/opengl/transition_movit_luma.yml --- mlt-0.9.0/src/modules/opengl/transition_movit_luma.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_luma.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,56 @@ +schema_version: 0.2 +type: transition +identifier: movit.luma_mix +title: Wipe (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: A generic dissolve and wipe transition processor. + +parameters: + - identifier: resource + argument: yes + title: Wipe File + description: Gradient image or dissolve if not supplied. + type: string + mutable: yes + + - identifier: softness + title: Softness + description: The blurriness of the edges of the transition. + type: float + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + + - identifier: reverse + title: Reverse + type: integer + mutable: yes + description: Reverse the direction of the transition. + default: 0 + minimum: 0 + maximum: 1 + widget: checkbox + + - identifier: invert + title: Invert + type: integer + mutable: yes + description: Invert the wipe. + default: 0 + minimum: 0 + maximum: 1 + widget: checkbox + + - identifier: producer.* + title: Producer + mutable: yes + description: > + Properties may be set on the encapsulated producer that reads resource. + readonly: no diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_mix.cpp mlt-0.9.2/src/modules/opengl/transition_movit_mix.cpp --- mlt-0.9.0/src/modules/opengl/transition_movit_mix.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_mix.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -22,7 +22,7 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include #include #include @@ -30,179 +30,57 @@ #include "mlt_movit_input.h" #include "mlt_flip_effect.h" +using namespace movit; + static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - int error = 0; + int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); + mlt_service service = MLT_TRANSITION_SERVICE( transition ); + mlt_service_lock( service ); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); - // Get the properties of the a frame - mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); - - // Get the movit objects - mlt_service service = MLT_TRANSITION_SERVICE( transition ); - mlt_service_lock( service ); - EffectChain* chain = GlslManager::get_chain( service ); - Effect* effect = (Effect*) mlt_properties_get_data( properties, "movit effect", NULL ); - MltInput* a_input = GlslManager::get_input( service ); - MltInput* b_input = (MltInput*) mlt_properties_get_data( properties, "movit input B", NULL ); - mlt_image_format output_format = *format; - - if ( !chain || !a_input ) { - mlt_service_unlock( service ); - return 2; - } - // Get the transition parameters mlt_position position = mlt_transition_get_position( transition, a_frame ); mlt_position length = mlt_transition_get_length( transition ); int reverse = mlt_properties_get_int( properties, "reverse" ); - double mix = mlt_properties_get( properties, "mix" ) ? + const char* mix_str = mlt_properties_get( properties, "mix" ); + double mix = ( mix_str && strlen( mix_str ) > 0 ) ? mlt_properties_anim_get_double( properties, "mix", position, length ) : mlt_transition_get_progress( transition, a_frame ); double inverse = 1.0 - mix; - // Set the movit parameters - bool ok = effect->set_float( "strength_first", reverse ? mix : inverse ); - ok |= effect->set_float( "strength_second", reverse ? inverse : mix ); - assert( ok ); - - // Get the frames' textures - GLuint* texture_id[2] = {0, 0}; - *format = mlt_image_glsl_texture; - mlt_frame_get_image( a_frame, (uint8_t**) &texture_id[0], format, width, height, 0 ); - a_input->useFBOInput( chain, *texture_id[0] ); - *format = mlt_image_glsl_texture; - mlt_frame_get_image( b_frame, (uint8_t**) &texture_id[1], format, width, height, 0 ); - b_input->useFBOInput( chain, *texture_id[1] ); - - // Set resolution to that of the a_frame - *width = mlt_properties_get_int( a_props, "width" ); - *height = mlt_properties_get_int( a_props, "height" ); - - // Setup rendering to an FBO - GlslManager* glsl = GlslManager::get_instance(); - glsl_fbo fbo = glsl->get_fbo( *width, *height ); - if ( output_format == mlt_image_glsl_texture ) { - glsl_texture texture = glsl->get_texture( *width, *height, GL_RGBA ); - - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, *width, *height ); - - glFinish(); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - *image = (uint8_t*) &texture->texture; - mlt_frame_set_image( a_frame, *image, 0, NULL ); - mlt_properties_set_data( properties, "movit.convert", texture, 0, - (mlt_destructor) GlslManager::release_texture, NULL ); - *format = output_format; - } - else { - // Use a PBO to hold the data we read back with glReadPixels() - // (Intel/DRI goes into a slow path if we don't read to PBO) - GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? - GL_RGBA : GL_RGB; - int img_size = *width * *height * ( gl_format == GL_RGB? 3 : 4 ); - glsl_pbo pbo = glsl->get_pbo( img_size ); - glsl_texture texture = glsl->get_texture( *width, *height, gl_format ); - - if ( fbo && pbo && texture ) { - // Set the FBO - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, *width, *height ); - - // Read FBO into PBO - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); - check_error(); - glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); - check_error(); - glReadPixels( 0, 0, *width, *height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); - check_error(); - - // Copy from PBO - uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); - check_error(); - - *format = gl_format == GL_RGBA ? mlt_image_rgb24a : mlt_image_rgb24; - *image = (uint8_t*) mlt_pool_alloc( img_size ); - mlt_frame_set_image( a_frame, *image, img_size, mlt_pool_release ); - memcpy( *image, buf, img_size ); - - // Release PBO and FBO - glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); - check_error(); - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - glBindTexture( GL_TEXTURE_2D, 0 ); - check_error(); - GlslManager::release_texture( texture ); - } - else { - error = 1; - } - } - if ( fbo ) GlslManager::release_fbo( fbo ); - mlt_service_unlock( service ); + // Set the Movit parameters. + mlt_properties_set_double( properties, "_movit.parms.float.strength_first", reverse ? mix : inverse ); + mlt_properties_set_double( properties, "_movit.parms.float.strength_second", reverse ? inverse : mix ); + + uint8_t *a_image, *b_image; + + // Get the two images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect( service, a_frame, new MixEffect() ); + *image = (uint8_t *) service; + mlt_service_unlock( service ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { - mlt_service service = MLT_TRANSITION_SERVICE(transition); - - if ( !GlslManager::init_chain( service ) ) { - // Create the Movit effect chain - EffectChain* chain = GlslManager::get_chain( service ); - mlt_profile profile = mlt_service_profile( service ); - Input* b_input = new MltInput( profile->width, profile->height ); - ImageFormat output_format; - output_format.color_space = COLORSPACE_sRGB; - output_format.gamma_curve = GAMMA_sRGB; - chain->add_input( b_input ); - chain->add_output( output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED ); - chain->set_dither_bits( 8 ); - - Effect* effect = chain->add_effect( new MixEffect(), - GlslManager::get_input( service ), b_input ); - - // Save these new effects on properties for get_image - mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); - mlt_properties_set_data( properties, "movit effect", effect, 0, NULL, NULL ); - mlt_properties_set_data( properties, "movit input B", b_input, 0, NULL, NULL ); - } - - // Push the transition on to the frame mlt_frame_push_service( a_frame, transition ); - - // Push the b_frame on to the stack mlt_frame_push_frame( a_frame, b_frame ); - - // Push the transition method mlt_frame_push_get_image( a_frame, get_image ); return a_frame; diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_mix.yml mlt-0.9.2/src/modules/opengl/transition_movit_mix.yml --- mlt-0.9.0/src/modules/opengl/transition_movit_mix.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_mix.yml 2014-06-29 20:23:17.000000000 +0000 @@ -35,3 +35,6 @@ description: > Reverse the direction of the transition. default: 0 + minimum: 0 + maximum: 1 + widget: checkbox diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_overlay.cpp mlt-0.9.2/src/modules/opengl/transition_movit_overlay.cpp --- mlt-0.9.0/src/modules/opengl/transition_movit_overlay.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_overlay.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -22,171 +22,46 @@ #include #include -#include "glsl_manager.h" +#include "filter_glsl_manager.h" #include #include #include #include -#include "mlt_movit_input.h" -#include "mlt_flip_effect.h" + +using namespace movit; static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { - int error = 0; + int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); - - // Get the properties of the transition - mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); - - // Get the properties of the a frame - mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); - - // Get the movit objects mlt_service service = MLT_TRANSITION_SERVICE( transition ); mlt_service_lock( service ); - EffectChain* chain = GlslManager::get_chain( service ); - MltInput* a_input = GlslManager::get_input( service ); - MltInput* b_input = (MltInput*) mlt_properties_get_data( properties, "movit input B", NULL ); - mlt_image_format output_format = *format; - - if ( !chain || !a_input ) { - mlt_service_unlock( service ); - return 2; - } - - // Get the frames' textures - GLuint* texture_id[2] = {0, 0}; - *format = mlt_image_glsl_texture; - mlt_frame_get_image( a_frame, (uint8_t**) &texture_id[0], format, width, height, 0 ); - a_input->useFBOInput( chain, *texture_id[0] ); - *format = mlt_image_glsl_texture; - mlt_frame_get_image( b_frame, (uint8_t**) &texture_id[1], format, width, height, 0 ); - b_input->useFBOInput( chain, *texture_id[1] ); - - // Set resolution to that of the a_frame - *width = mlt_properties_get_int( a_props, "width" ); - *height = mlt_properties_get_int( a_props, "height" ); - - // Setup rendering to an FBO - GlslManager* glsl = GlslManager::get_instance(); - glsl_fbo fbo = glsl->get_fbo( *width, *height ); - if ( output_format == mlt_image_glsl_texture ) { - glsl_texture texture = glsl->get_texture( *width, *height, GL_RGBA ); - - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, *width, *height ); - - glFinish(); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - *image = (uint8_t*) &texture->texture; - mlt_frame_set_image( a_frame, *image, 0, NULL ); - mlt_properties_set_data( properties, "movit.convert", texture, 0, - (mlt_destructor) GlslManager::release_texture, NULL ); - *format = output_format; - } - else { - // Use a PBO to hold the data we read back with glReadPixels() - // (Intel/DRI goes into a slow path if we don't read to PBO) - GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? - GL_RGBA : GL_RGB; - int img_size = *width * *height * ( gl_format == GL_RGB? 3 : 4 ); - glsl_pbo pbo = glsl->get_pbo( img_size ); - glsl_texture texture = glsl->get_texture( *width, *height, gl_format ); - - if ( fbo && pbo && texture ) { - // Set the FBO - glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); - check_error(); - glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - - GlslManager::render( service, chain, fbo->fbo, *width, *height ); - - // Read FBO into PBO - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); - check_error(); - glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); - check_error(); - glReadPixels( 0, 0, *width, *height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); - check_error(); - - // Copy from PBO - uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); - check_error(); - - *format = gl_format == GL_RGBA ? mlt_image_rgb24a : mlt_image_rgb24; - *image = (uint8_t*) mlt_pool_alloc( img_size ); - mlt_frame_set_image( a_frame, *image, img_size, mlt_pool_release ); - memcpy( *image, buf, img_size ); - - // Release PBO and FBO - glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); - check_error(); - glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); - check_error(); - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - check_error(); - glBindTexture( GL_TEXTURE_2D, 0 ); - check_error(); - GlslManager::release_texture( texture ); - } - else { - error = 1; - } - } - if ( fbo ) GlslManager::release_fbo( fbo ); - mlt_service_lock( service ); + uint8_t *a_image, *b_image; + + // Get the two images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect( service, a_frame, new OverlayEffect ); + *image = (uint8_t *) service; + + mlt_service_unlock( service ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { - mlt_service service = MLT_TRANSITION_SERVICE(transition); - - if ( !GlslManager::init_chain( service ) ) { - // Create the Movit effect chain - EffectChain* chain = GlslManager::get_chain( service ); - mlt_profile profile = mlt_service_profile( service ); - Input* b_input = new MltInput( profile->width, profile->height ); - ImageFormat output_format; - output_format.color_space = COLORSPACE_sRGB; - output_format.gamma_curve = GAMMA_sRGB; - chain->add_input( b_input ); - chain->add_output( output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED ); - chain->set_dither_bits( 8 ); - - Effect* effect = chain->add_effect( new OverlayEffect(), - GlslManager::get_input( service ), b_input ); - - // Save these new input on properties for get_image - mlt_properties_set_data( MLT_TRANSITION_PROPERTIES(transition), - "movit input B", b_input, 0, NULL, NULL ); - } - - // Push the transition on to the frame mlt_frame_push_service( a_frame, transition ); - - // Push the b_frame on to the stack mlt_frame_push_frame( a_frame, b_frame ); - - // Push the transition method mlt_frame_push_get_image( a_frame, get_image ); return a_frame; diff -Nru mlt-0.9.0/src/modules/opengl/transition_movit_overlay.yml mlt-0.9.2/src/modules/opengl/transition_movit_overlay.yml --- mlt-0.9.0/src/modules/opengl/transition_movit_overlay.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/opengl/transition_movit_overlay.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,7 +1,7 @@ schema_version: 0.1 type: transition identifier: movit.overlay -title: Dissolve (GLSL) +title: Overlay (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson diff -Nru mlt-0.9.0/src/modules/plus/consumer_blipflash.c mlt-0.9.2/src/modules/plus/consumer_blipflash.c --- mlt-0.9.0/src/modules/plus/consumer_blipflash.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/consumer_blipflash.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,427 @@ +/* + * consumer_blipflash.c -- a consumer to measure A/V sync from a blip/flash + * source + * Copyright (C) 2013 Brian Matherly + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// mlt Header files +#include +#include +#include + +// System header files +#include +#include +#include +#include +#include + +// Private constants +#define SAMPLE_FREQ 48000 +#define FLASH_LUMA_THRESHOLD 150 +#define BLIP_THRESHOLD 0.5 + +// Private types +typedef struct +{ + int64_t flash_history[2]; + int flash_history_count; + int64_t blip_history[2]; + int blip_history_count; + int blip_in_progress; + int samples_since_blip; + int blip; + int flash; + int sample_offset; + FILE* out_file; + int report_frames; +} avsync_stats; + +// Forward references. +static int consumer_start( mlt_consumer consumer ); +static int consumer_stop( mlt_consumer consumer ); +static int consumer_is_stopped( mlt_consumer consumer ); +static void *consumer_thread( void *arg ); +static void consumer_close( mlt_consumer consumer ); + +/** Initialize the consumer. +*/ + +mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + // Allocate the consumer + mlt_consumer consumer = mlt_consumer_new( profile ); + mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); + avsync_stats* stats = NULL; + + // If memory allocated and initializes without error + if ( consumer != NULL ) + { + // Set up start/stop/terminated callbacks + consumer->close = consumer_close; + consumer->start = consumer_start; + consumer->stop = consumer_stop; + consumer->is_stopped = consumer_is_stopped; + + stats = mlt_pool_alloc( sizeof( avsync_stats ) ); + stats->flash_history_count = 0; + stats->blip_history_count = 0; + stats->blip_in_progress = 0; + stats->samples_since_blip = 0; + stats->blip = 0; + stats->flash = 0; + stats->sample_offset = INT_MAX; + stats->report_frames = 0; + stats->out_file = stdout; + if ( arg != NULL ) + { + FILE* out_file = fopen( arg, "w" ); + if ( out_file != NULL ) + stats->out_file = out_file; + } + mlt_properties_set_data( consumer_properties, "_stats", stats, 0, NULL, NULL ); + + mlt_properties_set( consumer_properties, "report", "blip" ); + } + + // Return this + return consumer; +} + +/** Start the consumer. +*/ + +static int consumer_start( mlt_consumer consumer ) +{ + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + + // Check that we're not already running + if ( !mlt_properties_get_int( properties, "_running" ) ) + { + // Allocate a thread + pthread_t *thread = calloc( 1, sizeof( pthread_t ) ); + + // Assign the thread to properties + mlt_properties_set_data( properties, "_thread", thread, sizeof( pthread_t ), free, NULL ); + + // Set the running state + mlt_properties_set_int( properties, "_running", 1 ); + + // Create the thread + pthread_create( thread, NULL, consumer_thread, consumer ); + } + return 0; +} + +/** Stop the consumer. +*/ + +static int consumer_stop( mlt_consumer consumer ) +{ + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + + // Check that we're running + if ( mlt_properties_get_int( properties, "_running" ) ) + { + // Get the thread + pthread_t *thread = mlt_properties_get_data( properties, "_thread", NULL ); + + // Stop the thread + mlt_properties_set_int( properties, "_running", 0 ); + + // Wait for termination + if ( thread ) + pthread_join( *thread, NULL ); + } + + return 0; +} + +/** Determine if the consumer is stopped. +*/ + +static int consumer_is_stopped( mlt_consumer consumer ) +{ + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + return !mlt_properties_get_int( properties, "_running" ); +} + +static void detect_flash( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) +{ + int width = 0; + int height = 0; + mlt_image_format format = mlt_image_yuv422; + uint8_t* image = NULL; + int error = mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ); + + if ( !error && format == mlt_image_yuv422 && image != NULL ) + { + int i, j = 0; + int y_accumulator = 0; + + // Add up the luma values from 4 samples in 4 different quadrants. + for( i = 1; i < 3; i++ ) + { + int x = ( width / 3 ) * i; + x = x - x % 2; // Make sure this is a luma sample + for( j = 1; j < 3; j++ ) + { + int y = ( height / 3 ) * j; + y_accumulator += image[ y * height * 2 + x * 2 ]; + } + } + // If the average luma value is > 150, assume it is a flash. + stats->flash = ( y_accumulator / 4 ) > FLASH_LUMA_THRESHOLD; + } + + if( stats->flash ) + { + stats->flash_history[1] = stats->flash_history[0]; + stats->flash_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); + if( stats->flash_history_count < 2 ) + { + stats->flash_history_count++; + } + } +} + +static void detect_blip( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) +{ + int frequency = SAMPLE_FREQ; + int channels = 1; + int samples = mlt_sample_calculator( fps, frequency, pos ); + mlt_audio_format format = mlt_audio_float; + float* buffer = NULL; + int error = mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, &samples ); + + if ( !error && format == mlt_audio_float && buffer != NULL ) + { + int i = 0; + + for( i = 0; i < samples; i++ ) + { + if( !stats->blip_in_progress ) + { + if( buffer[i] > BLIP_THRESHOLD || buffer[i] < -BLIP_THRESHOLD ) + { + // This sample must start a blip + stats->blip_in_progress = 1; + stats->samples_since_blip = 0; + + stats->blip_history[1] = stats->blip_history[0]; + stats->blip_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); + stats->blip_history[0] += i; + if( stats->blip_history_count < 2 ) + { + stats->blip_history_count++; + } + stats->blip = 1; + } + } + else + { + if( buffer[i] > -BLIP_THRESHOLD && buffer[i] < BLIP_THRESHOLD ) + { + if( ++stats->samples_since_blip > frequency / 1000 ) + { + // One ms of silence means the blip is over + stats->blip_in_progress = 0; + stats->samples_since_blip = 0; + } + } + else + { + stats->samples_since_blip = 0; + } + } + } + } +} + +static void calculate_sync( avsync_stats* stats ) +{ + if( stats->blip || stats->flash ) + { + if( stats->flash_history_count > 0 && + stats->blip_history_count > 0 && + stats->blip_history[0] == stats->flash_history[0] ) + { + // The flash and blip occurred at the same time. + stats->sample_offset = 0; + } + if( stats->flash_history_count > 1 && + stats->blip_history_count > 0 && + stats->blip_history[0] <= stats->flash_history[0] && + stats->blip_history[0] >= stats->flash_history[1] ) + { + // The latest blip occurred between two flashes + if( stats->flash_history[0] - stats->blip_history[0] > + stats->blip_history[0] - stats->flash_history[1] ) + { + // Blip is closer to the previous flash. + // F1---B0--------F0 + // ^----^ + // Video leads audio (negative number). + stats->sample_offset = (int)(stats->flash_history[1] - stats->blip_history[0] ); + } + else + { + // Blip is closer to the current flash. + // F1--------B0---F0 + // ^----^ + // Audio leads video (positive number). + stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0]); + } + } + else if( stats->blip_history_count > 1 && + stats->flash_history_count > 0 && + stats->flash_history[0] <= stats->blip_history[0] && + stats->flash_history[0] >= stats->blip_history[1] ) + { + // The latest flash occurred between two blips + if( stats->blip_history[0] - stats->flash_history[0] > + stats->flash_history[0] - stats->blip_history[1] ) + { + // Flash is closer to the previous blip. + // B1---F0--------B0 + // ^----^ + // Audio leads video (positive number). + stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[1]); + } + else + { + // Flash is closer to the latest blip. + // B1--------F0---B0 + // ^----^ + // Video leads audio (negative number). + stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0] ); + } + } + } +} + +static void report_results( avsync_stats* stats, mlt_position pos ) +{ + if( stats->report_frames || stats->blip ) + { + if( stats->sample_offset == INT_MAX ) + { + fprintf( stats->out_file, MLT_POSITION_FMT "\t??\n", pos ); + } + else + { + // Convert to milliseconds. + double ms_offset = (double)stats->sample_offset * 1000.0 / (double)SAMPLE_FREQ; + fprintf( stats->out_file, MLT_POSITION_FMT "\t%02.02f\n", pos, ms_offset ); + } + } + stats->blip = 0; + stats->flash = 0; +} + +/** The main thread - the argument is simply the consumer. +*/ + +static void *consumer_thread( void *arg ) +{ + // Map the argument to the object + mlt_consumer consumer = arg; + + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + + // Convenience functionality + int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); + int terminated = 0; + + // Frame and size + mlt_frame frame = NULL; + + // Loop while running + while( !terminated && mlt_properties_get_int( properties, "_running" ) ) + { + // Get the frame + frame = mlt_consumer_rt_frame( consumer ); + + // Check for termination + if ( terminate_on_pause && frame != NULL ) + terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; + + // Check that we have a frame to work with + if ( frame ) + { + avsync_stats* stats = mlt_properties_get_data( properties, "_stats", NULL ); + double fps = mlt_properties_get_double( properties, "fps" ); + mlt_position pos = mlt_frame_get_position( frame ); + + if( !strcmp( mlt_properties_get( properties, "report" ), "frame" ) ) + { + stats->report_frames = 1; + } + else + { + stats->report_frames = 0; + } + + detect_flash( frame, pos, fps, stats ); + detect_blip( frame, pos, fps, stats ); + calculate_sync( stats ); + report_results( stats, pos ); + + // Close the frame + mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); + mlt_frame_close( frame ); + } + } + + // Indicate that the consumer is stopped + mlt_properties_set_int( properties, "_running", 0 ); + mlt_consumer_stopped( consumer ); + + return NULL; +} + +/** Close the consumer. +*/ + +static void consumer_close( mlt_consumer consumer ) +{ + mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); + avsync_stats* stats = mlt_properties_get_data( consumer_properties, "_stats", NULL ); + + // Stop the consumer + mlt_consumer_stop( consumer ); + + // Close the file + if( stats->out_file != stdout ) + { + fclose( stats->out_file ); + } + + // Clean up memory + mlt_pool_release( stats ); + + // Close the parent + mlt_consumer_close( consumer ); + + // Free the memory + free( consumer ); +} diff -Nru mlt-0.9.0/src/modules/plus/consumer_blipflash.yml mlt-0.9.2/src/modules/plus/consumer_blipflash.yml --- mlt-0.9.0/src/modules/plus/consumer_blipflash.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/consumer_blipflash.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,34 @@ +schema_version: 0.1 +type: consumer +identifier: blipflash +title: Blip Flash +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video + - Audio +description: > + Calculate the A/V sync for a blip flash source. + Sync can be recalculated whenever a blip or a flash is detected. +parameters: + - identifier: argument + title: Report File + type: string + description: > + The file to report the results to. If empty, the results will be reported to standard out. + required: no + widget: filesave + - identifier: report + title: Report Style + type: string + description: > + When to report sync - every frame or only when blips occur. + default: blip + values: + - blip + - frame + mutable: yes + widget: combo diff -Nru mlt-0.9.0/src/modules/plus/ebur128/COPYING mlt-0.9.2/src/modules/plus/ebur128/COPYING --- mlt-0.9.0/src/modules/plus/ebur128/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/ebur128/COPYING 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,19 @@ +Copyright (c) 2011 Jan Kokemüller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff -Nru mlt-0.9.0/src/modules/plus/ebur128/ebur128.c mlt-0.9.2/src/modules/plus/ebur128/ebur128.c --- mlt-0.9.0/src/modules/plus/ebur128/ebur128.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/ebur128/ebur128.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,997 @@ +/* See COPYING file for copyright and license details. */ + +#include "ebur128.h" + +#include +#include +#include /* You may have to define _USE_MATH_DEFINES if you use MSVC */ +#include +#include + +/* This can be replaced by any BSD-like queue implementation. */ +#include "queue.h" + +#ifdef USE_SPEEX_RESAMPLER + #include +#endif + +#define CHECK_ERROR(condition, errorcode, goto_point) \ + if ((condition)) { \ + errcode = (errorcode); \ + goto goto_point; \ + } + +SLIST_HEAD(ebur128_double_queue, ebur128_dq_entry); +struct ebur128_dq_entry { + double z; + SLIST_ENTRY(ebur128_dq_entry) entries; +}; + +struct ebur128_state_internal { + /** Filtered audio data (used as ring buffer). */ + double* audio_data; + /** Size of audio_data array. */ + size_t audio_data_frames; + /** Current index for audio_data. */ + size_t audio_data_index; + /** How many frames are needed for a gating block. Will correspond to 400ms + * of audio at initialization, and 100ms after the first block (75% overlap + * as specified in the 2011 revision of BS1770). */ + unsigned long needed_frames; + /** The channel map. Has as many elements as there are channels. */ + int* channel_map; + /** How many samples fit in 100ms (rounded). */ + unsigned long samples_in_100ms; + /** BS.1770 filter coefficients (nominator). */ + double b[5]; + /** BS.1770 filter coefficients (denominator). */ + double a[5]; + /** BS.1770 filter state. */ + double v[5][5]; + /** Linked list of block energies. */ + struct ebur128_double_queue block_list; + /** Linked list of 3s-block energies, used to calculate LRA. */ + struct ebur128_double_queue short_term_block_list; + int use_histogram; + unsigned long *block_energy_histogram; + unsigned long *short_term_block_energy_histogram; + /** Keeps track of when a new short term block is needed. */ + size_t short_term_frame_counter; + /** Maximum sample peak, one per channel */ + double* sample_peak; + /** Maximum true peak, one per channel */ + double* true_peak; +#ifdef USE_SPEEX_RESAMPLER + SpeexResamplerState* resampler; +#endif + size_t oversample_factor; + float* resampler_buffer_input; + size_t resampler_buffer_input_frames; + float* resampler_buffer_output; + size_t resampler_buffer_output_frames; +}; + +static double relative_gate = -10.0; + +/* Those will be calculated when initializing the library */ +static double relative_gate_factor; +static double minus_twenty_decibels; +static double histogram_energies[1000]; +static double histogram_energy_boundaries[1001]; + +static void ebur128_init_filter(ebur128_state* st) { + int i, j; + + double f0 = 1681.974450955533; + double G = 3.999843853973347; + double Q = 0.7071752369554196; + + double K = tan(M_PI * f0 / (double) st->samplerate); + double Vh = pow(10.0, G / 20.0); + double Vb = pow(Vh, 0.4996667741545416); + + double pb[3] = {0.0, 0.0, 0.0}; + double pa[3] = {1.0, 0.0, 0.0}; + double rb[3] = {1.0, -2.0, 1.0}; + double ra[3] = {1.0, 0.0, 0.0}; + + double a0 = 1.0 + K / Q + K * K ; + pb[0] = (Vh + Vb * K / Q + K * K) / a0; + pb[1] = 2.0 * (K * K - Vh) / a0; + pb[2] = (Vh - Vb * K / Q + K * K) / a0; + pa[1] = 2.0 * (K * K - 1.0) / a0; + pa[2] = (1.0 - K / Q + K * K) / a0; + + /* fprintf(stderr, "%.14f %.14f %.14f %.14f %.14f\n", + b1[0], b1[1], b1[2], a1[1], a1[2]); */ + + f0 = 38.13547087602444; + Q = 0.5003270373238773; + K = tan(M_PI * f0 / (double) st->samplerate); + + ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K); + ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K); + + /* fprintf(stderr, "%.14f %.14f\n", a2[1], a2[2]); */ + + st->d->b[0] = pb[0] * rb[0]; + st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0]; + st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0]; + st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1]; + st->d->b[4] = pb[2] * rb[2]; + + st->d->a[0] = pa[0] * ra[0]; + st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0]; + st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0]; + st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1]; + st->d->a[4] = pa[2] * ra[2]; + + for (i = 0; i < 5; ++i) { + for (j = 0; j < 5; ++j) { + st->d->v[i][j] = 0.0; + } + } +} + +static int ebur128_init_channel_map(ebur128_state* st) { + size_t i; + st->d->channel_map = (int*) malloc(st->channels * sizeof(int)); + if (!st->d->channel_map) return EBUR128_ERROR_NOMEM; + if (st->channels == 4) { + st->d->channel_map[0] = EBUR128_LEFT; + st->d->channel_map[1] = EBUR128_RIGHT; + st->d->channel_map[2] = EBUR128_LEFT_SURROUND; + st->d->channel_map[3] = EBUR128_RIGHT_SURROUND; + } else if (st->channels == 5) { + st->d->channel_map[0] = EBUR128_LEFT; + st->d->channel_map[1] = EBUR128_RIGHT; + st->d->channel_map[2] = EBUR128_CENTER; + st->d->channel_map[3] = EBUR128_LEFT_SURROUND; + st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; + } else { + for (i = 0; i < st->channels; ++i) { + switch (i) { + case 0: st->d->channel_map[i] = EBUR128_LEFT; break; + case 1: st->d->channel_map[i] = EBUR128_RIGHT; break; + case 2: st->d->channel_map[i] = EBUR128_CENTER; break; + case 3: st->d->channel_map[i] = EBUR128_UNUSED; break; + case 4: st->d->channel_map[i] = EBUR128_LEFT_SURROUND; break; + case 5: st->d->channel_map[i] = EBUR128_RIGHT_SURROUND; break; + default: st->d->channel_map[i] = EBUR128_UNUSED; break; + } + } + } + return EBUR128_SUCCESS; +} + +#ifdef USE_SPEEX_RESAMPLER +static int ebur128_init_resampler(ebur128_state* st) { + int errcode = EBUR128_SUCCESS; + + if (st->samplerate < 96000) { + st->d->oversample_factor = 4; + } else if (st->samplerate < 192000) { + st->d->oversample_factor = 2; + } else { + st->d->oversample_factor = 1; + st->d->resampler_buffer_input = NULL; + st->d->resampler_buffer_output = NULL; + st->d->resampler = NULL; + } + + st->d->resampler_buffer_input_frames = st->d->samples_in_100ms * 4; + st->d->resampler_buffer_input = malloc(st->d->resampler_buffer_input_frames * + st->channels * + sizeof(float)); + CHECK_ERROR(!st->d->resampler_buffer_input, EBUR128_ERROR_NOMEM, exit) + + st->d->resampler_buffer_output_frames = + st->d->resampler_buffer_input_frames * + st->d->oversample_factor; + st->d->resampler_buffer_output = malloc + (st->d->resampler_buffer_output_frames * + st->channels * + sizeof(float)); + CHECK_ERROR(!st->d->resampler_buffer_output, EBUR128_ERROR_NOMEM, free_input) + + st->d->resampler = speex_resampler_init + ((spx_uint32_t) st->channels, + (spx_uint32_t) st->samplerate, + (spx_uint32_t) (st->samplerate * st->d->oversample_factor), + 8, NULL); + CHECK_ERROR(!st->d->resampler, EBUR128_ERROR_NOMEM, free_output) + + return errcode; + +free_output: + free(st->d->resampler_buffer_output); + st->d->resampler_buffer_output = NULL; +free_input: + free(st->d->resampler_buffer_input); + st->d->resampler_buffer_input = NULL; +exit: + return errcode; +} + +static void ebur128_destroy_resampler(ebur128_state* st) { + free(st->d->resampler_buffer_input); + st->d->resampler_buffer_input = NULL; + free(st->d->resampler_buffer_output); + st->d->resampler_buffer_output = NULL; + speex_resampler_destroy(st->d->resampler); + st->d->resampler = NULL; +} +#endif + +void ebur128_get_version(int* major, int* minor, int* patch) { + *major = EBUR128_VERSION_MAJOR; + *minor = EBUR128_VERSION_MINOR; + *patch = EBUR128_VERSION_PATCH; +} + +ebur128_state* ebur128_init(unsigned int channels, + unsigned long samplerate, + int mode) { + int errcode, result; + ebur128_state* st; + unsigned int i; + + st = (ebur128_state*) malloc(sizeof(ebur128_state)); + CHECK_ERROR(!st, 0, exit) + st->d = (struct ebur128_state_internal*) + malloc(sizeof(struct ebur128_state_internal)); + CHECK_ERROR(!st->d, 0, free_state) + st->channels = channels; + errcode = ebur128_init_channel_map(st); + CHECK_ERROR(errcode, 0, free_internal) + + st->d->sample_peak = (double*) malloc(channels * sizeof(double)); + CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map) + st->d->true_peak = (double*) malloc(channels * sizeof(double)); + CHECK_ERROR(!st->d->true_peak, 0, free_sample_peak) + for (i = 0; i < channels; ++i) { + st->d->sample_peak[i] = 0.0; + st->d->true_peak[i] = 0.0; + } + + st->d->use_histogram = mode & EBUR128_MODE_HISTOGRAM ? 1 : 0; + + st->samplerate = samplerate; + st->d->samples_in_100ms = (st->samplerate + 5) / 10; + st->mode = mode; + if ((mode & EBUR128_MODE_S) == EBUR128_MODE_S) { + st->d->audio_data_frames = st->d->samples_in_100ms * 30; + } else if ((mode & EBUR128_MODE_M) == EBUR128_MODE_M) { + st->d->audio_data_frames = st->d->samples_in_100ms * 4; + } else { + goto free_true_peak; + } + st->d->audio_data = (double*) malloc(st->d->audio_data_frames * + st->channels * + sizeof(double)); + CHECK_ERROR(!st->d->audio_data, 0, free_true_peak) + ebur128_init_filter(st); + + if (st->d->use_histogram) { + st->d->block_energy_histogram = malloc(1000 * sizeof(unsigned long)); + CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data) + for (i = 0; i < 1000; ++i) { + st->d->block_energy_histogram[i] = 0; + } + } else { + st->d->block_energy_histogram = NULL; + } + if (st->d->use_histogram) { + st->d->short_term_block_energy_histogram = malloc(1000 * sizeof(unsigned long)); + CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram) + for (i = 0; i < 1000; ++i) { + st->d->short_term_block_energy_histogram[i] = 0; + } + } else { + st->d->short_term_block_energy_histogram = NULL; + } + SLIST_INIT(&st->d->block_list); + SLIST_INIT(&st->d->short_term_block_list); + st->d->short_term_frame_counter = 0; + +#ifdef USE_SPEEX_RESAMPLER + result = ebur128_init_resampler(st); + CHECK_ERROR(result, 0, free_short_term_block_energy_histogram) +#endif + + /* the first block needs 400ms of audio data */ + st->d->needed_frames = st->d->samples_in_100ms * 4; + /* start at the beginning of the buffer */ + st->d->audio_data_index = 0; + + /* initialize static constants */ + relative_gate_factor = pow(10.0, relative_gate / 10.0); + minus_twenty_decibels = pow(10.0, -20.0 / 10.0); + histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0); + if (st->d->use_histogram) { + for (i = 0; i < 1000; ++i) { + histogram_energies[i] = pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0); + } + for (i = 1; i < 1001; ++i) { + histogram_energy_boundaries[i] = pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0); + } + } + + return st; + +free_short_term_block_energy_histogram: + free(st->d->short_term_block_energy_histogram); +free_block_energy_histogram: + free(st->d->block_energy_histogram); +free_audio_data: + free(st->d->audio_data); +free_true_peak: + free(st->d->true_peak); +free_sample_peak: + free(st->d->sample_peak); +free_channel_map: + free(st->d->channel_map); +free_internal: + free(st->d); +free_state: + free(st); +exit: + return NULL; +} + +void ebur128_destroy(ebur128_state** st) { + struct ebur128_dq_entry* entry; + free((*st)->d->block_energy_histogram); + free((*st)->d->short_term_block_energy_histogram); + free((*st)->d->audio_data); + free((*st)->d->channel_map); + free((*st)->d->sample_peak); + free((*st)->d->true_peak); + while (!SLIST_EMPTY(&(*st)->d->block_list)) { + entry = SLIST_FIRST(&(*st)->d->block_list); + SLIST_REMOVE_HEAD(&(*st)->d->block_list, entries); + free(entry); + } + while (!SLIST_EMPTY(&(*st)->d->short_term_block_list)) { + entry = SLIST_FIRST(&(*st)->d->short_term_block_list); + SLIST_REMOVE_HEAD(&(*st)->d->short_term_block_list, entries); + free(entry); + } +#ifdef USE_SPEEX_RESAMPLER + ebur128_destroy_resampler(*st); +#endif + + free((*st)->d); + free(*st); + *st = NULL; +} + +static int ebur128_use_speex_resampler(ebur128_state* st) { +#ifdef USE_SPEEX_RESAMPLER + return ((st->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK); +#else + (void) st; + return 0; +#endif +} + +static void ebur128_check_true_peak(ebur128_state* st, size_t frames) { +#ifdef USE_SPEEX_RESAMPLER + size_t c, i; + spx_uint32_t in_len = (spx_uint32_t) frames; + spx_uint32_t out_len = (spx_uint32_t) st->d->resampler_buffer_output_frames; + speex_resampler_process_interleaved_float( + st->d->resampler, + st->d->resampler_buffer_input, &in_len, + st->d->resampler_buffer_output, &out_len); + for (c = 0; c < st->channels; ++c) { + for (i = 0; i < out_len; ++i) { + if (st->d->resampler_buffer_output[i * st->channels + c] > + st->d->true_peak[c]) { + st->d->true_peak[c] = + st->d->resampler_buffer_output[i * st->channels + c]; + } else if (-st->d->resampler_buffer_output[i * st->channels + c] > + st->d->true_peak[c]) { + st->d->true_peak[c] = + -st->d->resampler_buffer_output[i * st->channels + c]; + } + } + } +#else + (void) st; (void) frames; +#endif +} + +#ifdef __SSE2_MATH__ +#include +#define TURN_ON_FTZ \ + unsigned int mxcsr = _mm_getcsr(); \ + _mm_setcsr(mxcsr | _MM_FLUSH_ZERO_ON); +#define TURN_OFF_FTZ _mm_setcsr(mxcsr); +#define FLUSH_MANUALLY +#else +#warning "manual FTZ is being used, please enable SSE2 (-msse2 -mfpmath=sse)" +#define TURN_ON_FTZ +#define TURN_OFF_FTZ +#define FLUSH_MANUALLY \ + st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \ + st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \ + st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \ + st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; +#endif + +#define EBUR128_FILTER(type, min_scale, max_scale) \ +static void ebur128_filter_##type(ebur128_state* st, const type* src, \ + size_t frames) { \ + static double scaling_factor = -((double) min_scale) > (double) max_scale ? \ + -((double) min_scale) : (double) max_scale; \ + double* audio_data = st->d->audio_data + st->d->audio_data_index; \ + size_t i, c; \ + \ + TURN_ON_FTZ \ + \ + if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK) { \ + for (c = 0; c < st->channels; ++c) { \ + double max = 0.0; \ + for (i = 0; i < frames; ++i) { \ + if (src[i * st->channels + c] > max) { \ + max = src[i * st->channels + c]; \ + } else if (-src[i * st->channels + c] > max) { \ + max = -1.0 * src[i * st->channels + c]; \ + } \ + } \ + max /= scaling_factor; \ + if (max > st->d->sample_peak[c]) st->d->sample_peak[c] = max; \ + } \ + } \ + if (ebur128_use_speex_resampler(st)) { \ + for (c = 0; c < st->channels; ++c) { \ + for (i = 0; i < frames; ++i) { \ + st->d->resampler_buffer_input[i * st->channels + c] = \ + (float) (src[i * st->channels + c] / scaling_factor); \ + } \ + } \ + ebur128_check_true_peak(st, frames); \ + } \ + for (c = 0; c < st->channels; ++c) { \ + int ci = st->d->channel_map[c] - 1; \ + if (ci < 0) continue; \ + else if (ci > 4) ci = 0; /* dual mono */ \ + for (i = 0; i < frames; ++i) { \ + st->d->v[ci][0] = (double) (src[i * st->channels + c] / scaling_factor) \ + - st->d->a[1] * st->d->v[ci][1] \ + - st->d->a[2] * st->d->v[ci][2] \ + - st->d->a[3] * st->d->v[ci][3] \ + - st->d->a[4] * st->d->v[ci][4]; \ + audio_data[i * st->channels + c] = \ + st->d->b[0] * st->d->v[ci][0] \ + + st->d->b[1] * st->d->v[ci][1] \ + + st->d->b[2] * st->d->v[ci][2] \ + + st->d->b[3] * st->d->v[ci][3] \ + + st->d->b[4] * st->d->v[ci][4]; \ + st->d->v[ci][4] = st->d->v[ci][3]; \ + st->d->v[ci][3] = st->d->v[ci][2]; \ + st->d->v[ci][2] = st->d->v[ci][1]; \ + st->d->v[ci][1] = st->d->v[ci][0]; \ + } \ + FLUSH_MANUALLY \ + } \ + TURN_OFF_FTZ \ +} +EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX) +EBUR128_FILTER(int, INT_MIN, INT_MAX) +EBUR128_FILTER(float, -1.0f, 1.0f) +EBUR128_FILTER(double, -1.0, 1.0) + +static double ebur128_energy_to_loudness(double energy) { + return 10 * (log(energy) / log(10.0)) - 0.691; +} + +static size_t find_histogram_index(double energy) { + size_t index_min = 0; + size_t index_max = 1000; + size_t index_mid; + + do { + index_mid = (index_min + index_max) / 2; + if (energy >= histogram_energy_boundaries[index_mid]) { + index_min = index_mid; + } else { + index_max = index_mid; + } + } while (index_max - index_min != 1); + + return index_min; +} + +static int ebur128_calc_gating_block(ebur128_state* st, size_t frames_per_block, + double* optional_output) { + size_t i, c; + double sum = 0.0; + double channel_sum; + for (c = 0; c < st->channels; ++c) { + if (st->d->channel_map[c] == EBUR128_UNUSED) continue; + channel_sum = 0.0; + if (st->d->audio_data_index < frames_per_block * st->channels) { + for (i = 0; i < st->d->audio_data_index / st->channels; ++i) { + channel_sum += st->d->audio_data[i * st->channels + c] * + st->d->audio_data[i * st->channels + c]; + } + for (i = st->d->audio_data_frames - + (frames_per_block - + st->d->audio_data_index / st->channels); + i < st->d->audio_data_frames; ++i) { + channel_sum += st->d->audio_data[i * st->channels + c] * + st->d->audio_data[i * st->channels + c]; + } + } else { + for (i = st->d->audio_data_index / st->channels - frames_per_block; + i < st->d->audio_data_index / st->channels; + ++i) { + channel_sum += st->d->audio_data[i * st->channels + c] * + st->d->audio_data[i * st->channels + c]; + } + } + if (st->d->channel_map[c] == EBUR128_LEFT_SURROUND || + st->d->channel_map[c] == EBUR128_RIGHT_SURROUND) { + channel_sum *= 1.41; + } else if (st->d->channel_map[c] == EBUR128_DUAL_MONO) { + channel_sum *= 2.0; + } + sum += channel_sum; + } + sum /= (double) frames_per_block; + if (optional_output) { + *optional_output = sum; + return EBUR128_SUCCESS; + } else if (sum >= histogram_energy_boundaries[0]) { + if (st->d->use_histogram) { + ++st->d->block_energy_histogram[find_histogram_index(sum)]; + } else { + struct ebur128_dq_entry* block; + block = (struct ebur128_dq_entry*) malloc(sizeof(struct ebur128_dq_entry)); + if (!block) return EBUR128_ERROR_NOMEM; + block->z = sum; + SLIST_INSERT_HEAD(&st->d->block_list, block, entries); + } + return EBUR128_SUCCESS; + } else { + return EBUR128_SUCCESS; + } +} + +int ebur128_set_channel(ebur128_state* st, + unsigned int channel_number, + int value) { + if (channel_number >= st->channels) { + return 1; + } + if (value == EBUR128_DUAL_MONO && + (st->channels != 1 || channel_number != 0)) { + fprintf(stderr, "EBUR128_DUAL_MONO only works with mono files!\n"); + return 1; + } + st->d->channel_map[channel_number] = value; + return 0; +} + +int ebur128_change_parameters(ebur128_state* st, + unsigned int channels, + unsigned long samplerate) { + int errcode; + if (channels == st->channels && + samplerate == st->samplerate) { + return 2; + } + free(st->d->audio_data); + st->d->audio_data = NULL; + + if (channels != st->channels) { + unsigned int i; + + free(st->d->channel_map); st->d->channel_map = NULL; + free(st->d->sample_peak); st->d->sample_peak = NULL; + free(st->d->true_peak); st->d->true_peak = NULL; + st->channels = channels; + +#ifdef USE_SPEEX_RESAMPLER + ebur128_destroy_resampler(st); + ebur128_init_resampler(st); +#endif + + errcode = ebur128_init_channel_map(st); + CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) + + st->d->sample_peak = (double*) malloc(channels * sizeof(double)); + CHECK_ERROR(!st->d->sample_peak, EBUR128_ERROR_NOMEM, exit) + st->d->true_peak = (double*) malloc(channels * sizeof(double)); + CHECK_ERROR(!st->d->true_peak, EBUR128_ERROR_NOMEM, exit) + for (i = 0; i < channels; ++i) { + st->d->sample_peak[i] = 0.0; + st->d->true_peak[i] = 0.0; + } + } + if (samplerate != st->samplerate) { + st->samplerate = samplerate; + ebur128_init_filter(st); + } + if ((st->mode & EBUR128_MODE_S) == EBUR128_MODE_S) { + st->d->audio_data_frames = st->d->samples_in_100ms * 30; + } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M) { + st->d->audio_data_frames = st->d->samples_in_100ms * 4; + } else { + return 1; + } + st->d->audio_data = (double*) malloc(st->d->audio_data_frames * + st->channels * + sizeof(double)); + CHECK_ERROR(!st->d->audio_data, EBUR128_ERROR_NOMEM, exit) + + /* the first block needs 400ms of audio data */ + st->d->needed_frames = st->d->samples_in_100ms * 4; + /* start at the beginning of the buffer */ + st->d->audio_data_index = 0; + /* reset short term frame counter */ + st->d->short_term_frame_counter = 0; + + return 0; + +exit: + return 1; +} + + +static int ebur128_energy_shortterm(ebur128_state* st, double* out); +#define EBUR128_ADD_FRAMES(type) \ +int ebur128_add_frames_##type(ebur128_state* st, \ + const type* src, size_t frames) { \ + size_t src_index = 0; \ + while (frames > 0) { \ + if (frames >= st->d->needed_frames) { \ + ebur128_filter_##type(st, src + src_index, st->d->needed_frames); \ + src_index += st->d->needed_frames * st->channels; \ + frames -= st->d->needed_frames; \ + st->d->audio_data_index += st->d->needed_frames * st->channels; \ + /* calculate the new gating block */ \ + if ((st->mode & EBUR128_MODE_I) == EBUR128_MODE_I) { \ + if (ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL)) {\ + return EBUR128_ERROR_NOMEM; \ + } \ + } \ + if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ + st->d->short_term_frame_counter += st->d->needed_frames; \ + if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \ + struct ebur128_dq_entry* block; \ + double st_energy; \ + ebur128_energy_shortterm(st, &st_energy); \ + if (st_energy >= histogram_energy_boundaries[0]) { \ + if (st->d->use_histogram) { \ + ++st->d->short_term_block_energy_histogram[ \ + find_histogram_index(st_energy)];\ + } else { \ + block = (struct ebur128_dq_entry*) \ + malloc(sizeof(struct ebur128_dq_entry)); \ + if (!block) return EBUR128_ERROR_NOMEM; \ + block->z = st_energy; \ + SLIST_INSERT_HEAD(&st->d->short_term_block_list, block, entries);\ + } \ + } \ + st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \ + } \ + } \ + /* 100ms are needed for all blocks besides the first one */ \ + st->d->needed_frames = st->d->samples_in_100ms; \ + /* reset audio_data_index when buffer full */ \ + if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) {\ + st->d->audio_data_index = 0; \ + } \ + } else { \ + ebur128_filter_##type(st, src + src_index, frames); \ + st->d->audio_data_index += frames * st->channels; \ + if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ + st->d->short_term_frame_counter += frames; \ + } \ + st->d->needed_frames -= frames; \ + frames = 0; \ + } \ + } \ + return EBUR128_SUCCESS; \ +} +EBUR128_ADD_FRAMES(short) +EBUR128_ADD_FRAMES(int) +EBUR128_ADD_FRAMES(float) +EBUR128_ADD_FRAMES(double) + +static int ebur128_gated_loudness(ebur128_state** sts, size_t size, + double* out) { + struct ebur128_dq_entry* it; + double relative_threshold = 0.0; + double gated_loudness = 0.0; + size_t above_thresh_counter = 0; + size_t i, j, start_index; + + for (i = 0; i < size; i++) { + if (sts[i] && (sts[i]->mode & EBUR128_MODE_I) != EBUR128_MODE_I) { + return EBUR128_ERROR_INVALID_MODE; + } + } + + for (i = 0; i < size; i++) { + if (!sts[i]) continue; + if (sts[i]->d->use_histogram) { + for (j = 0; j < 1000; ++j) { + relative_threshold += sts[i]->d->block_energy_histogram[j] * + histogram_energies[j]; + above_thresh_counter += sts[i]->d->block_energy_histogram[j]; + } + } else { + SLIST_FOREACH(it, &sts[i]->d->block_list, entries) { + ++above_thresh_counter; + relative_threshold += it->z; + } + } + } + if (!above_thresh_counter) { + *out = -HUGE_VAL; + return EBUR128_SUCCESS; + } + relative_threshold /= (double) above_thresh_counter; + relative_threshold *= relative_gate_factor; + above_thresh_counter = 0; + if (relative_threshold < histogram_energy_boundaries[0]) { + start_index = 0; + } else { + start_index = find_histogram_index(relative_threshold); + if (relative_threshold > histogram_energies[start_index]) { + ++start_index; + } + } + for (i = 0; i < size; i++) { + if (!sts[i]) continue; + if (sts[i]->d->use_histogram) { + for (j = start_index; j < 1000; ++j) { + gated_loudness += sts[i]->d->block_energy_histogram[j] * + histogram_energies[j]; + above_thresh_counter += sts[i]->d->block_energy_histogram[j]; + } + } else { + SLIST_FOREACH(it, &sts[i]->d->block_list, entries) { + if (it->z >= relative_threshold) { + ++above_thresh_counter; + gated_loudness += it->z; + } + } + } + } + if (!above_thresh_counter) { + *out = -HUGE_VAL; + return EBUR128_SUCCESS; + } + gated_loudness /= (double) above_thresh_counter; + *out = ebur128_energy_to_loudness(gated_loudness); + return EBUR128_SUCCESS; +} + +int ebur128_loudness_global(ebur128_state* st, double* out) { + return ebur128_gated_loudness(&st, 1, out); +} + +int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, + double* out) { + return ebur128_gated_loudness(sts, size, out); +} + +static int ebur128_energy_in_interval(ebur128_state* st, + size_t interval_frames, + double* out) { + if (interval_frames > st->d->audio_data_frames) { + return EBUR128_ERROR_INVALID_MODE; + } + ebur128_calc_gating_block(st, interval_frames, out); + return EBUR128_SUCCESS; +} + +static int ebur128_energy_shortterm(ebur128_state* st, double* out) { + return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30, out); +} + +int ebur128_loudness_momentary(ebur128_state* st, double* out) { + double energy; + int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4, + &energy); + if (error) { + return error; + } else if (energy <= 0.0) { + *out = -HUGE_VAL; + return EBUR128_SUCCESS; + } + *out = ebur128_energy_to_loudness(energy); + return EBUR128_SUCCESS; +} + +int ebur128_loudness_shortterm(ebur128_state* st, double* out) { + double energy; + int error = ebur128_energy_shortterm(st, &energy); + if (error) { + return error; + } else if (energy <= 0.0) { + *out = -HUGE_VAL; + return EBUR128_SUCCESS; + } + *out = ebur128_energy_to_loudness(energy); + return EBUR128_SUCCESS; +} + +static int ebur128_double_cmp(const void *p1, const void *p2) { + const double* d1 = (const double*) p1; + const double* d2 = (const double*) p2; + return (*d1 > *d2) - (*d1 < *d2); +} + +/* EBU - TECH 3342 */ +int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, + double* out) { + size_t i, j; + struct ebur128_dq_entry* it; + double* stl_vector; + size_t stl_size; + double* stl_relgated; + size_t stl_relgated_size; + double stl_power, stl_integrated; + /* High and low percentile energy */ + double h_en, l_en; + int use_histogram = 0; + + for (i = 0; i < size; ++i) { + if (sts[i]) { + if ((sts[i]->mode & EBUR128_MODE_LRA) != EBUR128_MODE_LRA) { + return EBUR128_ERROR_INVALID_MODE; + } + if (i == 0 && sts[i]->mode & EBUR128_MODE_HISTOGRAM) { + use_histogram = 1; + } else if (use_histogram != !!(sts[i]->mode & EBUR128_MODE_HISTOGRAM)) { + return EBUR128_ERROR_INVALID_MODE; + } + } + } + + if (use_histogram) { + unsigned long hist[1000] = { 0 }; + size_t percentile_low, percentile_high; + size_t index; + + stl_size = 0; + stl_power = 0.0; + for (i = 0; i < size; ++i) { + if (!sts[i]) continue; + for (j = 0; j < 1000; ++j) { + hist[j] += sts[i]->d->short_term_block_energy_histogram[j]; + stl_size += sts[i]->d->short_term_block_energy_histogram[j]; + stl_power += sts[i]->d->short_term_block_energy_histogram[j] + * histogram_energies[j]; + } + } + if (!stl_size) { + *out = 0.0; + return EBUR128_SUCCESS; + } + + stl_power /= stl_size; + stl_integrated = minus_twenty_decibels * stl_power; + + if (stl_integrated < histogram_energy_boundaries[0]) { + index = 0; + } else { + index = find_histogram_index(stl_integrated); + if (stl_integrated > histogram_energies[index]) { + ++index; + } + } + stl_size = 0; + for (j = index; j < 1000; ++j) { + stl_size += hist[j]; + } + if (!stl_size) { + *out = 0.0; + return EBUR128_SUCCESS; + } + + percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5); + percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5); + + stl_size = 0; + j = index; + while (stl_size <= percentile_low) { + stl_size += hist[j++]; + } + l_en = histogram_energies[j - 1]; + while (stl_size <= percentile_high) { + stl_size += hist[j++]; + } + h_en = histogram_energies[j - 1]; + *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); + return EBUR128_SUCCESS; + + } else { + stl_size = 0; + for (i = 0; i < size; ++i) { + if (!sts[i]) continue; + SLIST_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { + ++stl_size; + } + } + if (!stl_size) { + *out = 0.0; + return EBUR128_SUCCESS; + } + stl_vector = (double*) malloc(stl_size * sizeof(double)); + if (!stl_vector) + return EBUR128_ERROR_NOMEM; + + for (j = 0, i = 0; i < size; ++i) { + if (!sts[i]) continue; + SLIST_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { + stl_vector[j] = it->z; + ++j; + } + } + qsort(stl_vector, stl_size, sizeof(double), ebur128_double_cmp); + stl_power = 0.0; + for (i = 0; i < stl_size; ++i) { + stl_power += stl_vector[i]; + } + stl_power /= (double) stl_size; + stl_integrated = minus_twenty_decibels * stl_power; + + stl_relgated = stl_vector; + stl_relgated_size = stl_size; + while (stl_relgated_size > 0 && *stl_relgated < stl_integrated) { + ++stl_relgated; + --stl_relgated_size; + } + + if (stl_relgated_size) { + h_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.95 + 0.5)]; + l_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.1 + 0.5)]; + free(stl_vector); + *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); + return EBUR128_SUCCESS; + } else { + free(stl_vector); + *out = 0.0; + return EBUR128_SUCCESS; + } + } +} + +int ebur128_loudness_range(ebur128_state* st, double* out) { + return ebur128_loudness_range_multiple(&st, 1, out); +} + +int ebur128_sample_peak(ebur128_state* st, + unsigned int channel_number, + double* out) { + if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { + return EBUR128_ERROR_INVALID_MODE; + } else if (channel_number >= st->channels) { + return EBUR128_ERROR_INVALID_CHANNEL_INDEX; + } + *out = st->d->sample_peak[channel_number]; + return EBUR128_SUCCESS; +} + +#ifdef USE_SPEEX_RESAMPLER +int ebur128_true_peak(ebur128_state* st, + unsigned int channel_number, + double* out) { + if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { + return EBUR128_ERROR_INVALID_MODE; + } else if (channel_number >= st->channels) { + return EBUR128_ERROR_INVALID_CHANNEL_INDEX; + } + *out = st->d->true_peak[channel_number] > st->d->sample_peak[channel_number] + ? st->d->true_peak[channel_number] + : st->d->sample_peak[channel_number]; + return EBUR128_SUCCESS; +} +#endif diff -Nru mlt-0.9.0/src/modules/plus/ebur128/ebur128.h mlt-0.9.2/src/modules/plus/ebur128/ebur128.h --- mlt-0.9.0/src/modules/plus/ebur128/ebur128.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/ebur128/ebur128.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,288 @@ +/* See COPYING file for copyright and license details. */ + +#ifndef EBUR128_H_ +#define EBUR128_H_ + +/** \file ebur128.h + * \brief libebur128 - a library for loudness measurement according to + * the EBU R128 standard. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EBUR128_VERSION_MAJOR 1 +#define EBUR128_VERSION_MINOR 0 +#define EBUR128_VERSION_PATCH 1 + +#include /* for size_t */ + +/** \enum channel + * Use these values when setting the channel map with ebur128_set_channel(). + */ +enum channel { + EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */ + EBUR128_LEFT, /**< left channel */ + EBUR128_RIGHT, /**< right channel */ + EBUR128_CENTER, /**< center channel */ + EBUR128_LEFT_SURROUND, /**< left surround channel */ + EBUR128_RIGHT_SURROUND, /**< right surround channel */ + EBUR128_DUAL_MONO /**< a channel that is counted twice */ +}; + +/** \enum error + * Error return values. + */ +enum error { + EBUR128_SUCCESS = 0, + EBUR128_ERROR_NOMEM, + EBUR128_ERROR_INVALID_MODE, + EBUR128_ERROR_INVALID_CHANNEL_INDEX, + EBUR128_ERROR_NO_CHANGE +}; + +/** \enum mode + * Use these values in ebur128_init (or'ed). Try to use the lowest possible + * modes that suit your needs, as performance will be better. + */ +enum mode { + /** can call ebur128_loudness_momentary */ + EBUR128_MODE_M = (1 << 0), + /** can call ebur128_loudness_shortterm */ + EBUR128_MODE_S = (1 << 1) | EBUR128_MODE_M, + /** can call ebur128_gated_loudness_* */ + EBUR128_MODE_I = (1 << 2) | EBUR128_MODE_M, + /** can call ebur128_loudness_range */ + EBUR128_MODE_LRA = (1 << 3) | EBUR128_MODE_S, + /** can call ebur128_sample_peak */ + EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | EBUR128_MODE_M, + /** can call ebur128_true_peak */ + EBUR128_MODE_TRUE_PEAK = (1 << 5) | EBUR128_MODE_M + | EBUR128_MODE_SAMPLE_PEAK, + /** uses histogram algorithm to calculate loudness */ + EBUR128_MODE_HISTOGRAM = (1 << 6) +}; + +/** forward declaration of ebur128_state_internal */ +struct ebur128_state_internal; + +/** \brief Contains information about the state of a loudness measurement. + * + * You should not need to modify this struct directly. + */ +typedef struct { + int mode; /**< The current mode. */ + unsigned int channels; /**< The number of channels. */ + unsigned long samplerate; /**< The sample rate. */ + struct ebur128_state_internal* d; /**< Internal state. */ +} ebur128_state; + +/** \brief Get library version number. Do not pass null pointers here. + * + * @param major major version number of library + * @param minor minor version number of library + * @param patch patch version number of library + */ +void ebur128_get_version(int* major, int* minor, int* patch); + +/** \brief Initialize library state. + * + * @param channels the number of channels. + * @param samplerate the sample rate. + * @param mode see the mode enum for possible values. + * @return an initialized library state. + */ +ebur128_state* ebur128_init(unsigned int channels, + unsigned long samplerate, + int mode); + +/** \brief Destroy library state. + * + * @param st pointer to a library state. + */ +void ebur128_destroy(ebur128_state** st); + +/** \brief Set channel type. + * + * The default is: + * - 0 -> EBUR128_LEFT + * - 1 -> EBUR128_RIGHT + * - 2 -> EBUR128_CENTER + * - 3 -> EBUR128_UNUSED + * - 4 -> EBUR128_LEFT_SURROUND + * - 5 -> EBUR128_RIGHT_SURROUND + * + * @param st library state. + * @param channel_number zero based channel index. + * @param value channel type from the "channel" enum. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. + */ +int ebur128_set_channel(ebur128_state* st, + unsigned int channel_number, + int value); + +/** \brief Change library parameters. + * + * Note that the channel map will be reset when setting a different number of + * channels. The current unfinished block will be lost. + * + * @param st library state. + * @param channels new number of channels. + * @param samplerate new sample rate. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be + * invalid and must be destroyed. + * - EBUR128_ERROR_NO_CHANGE if channels and sample rate were not changed. + */ +int ebur128_change_parameters(ebur128_state* st, + unsigned int channels, + unsigned long samplerate); + +/** \brief Add frames to be processed. + * + * @param st library state. + * @param src array of source frames. Channels must be interleaved. + * @param frames number of frames. Not number of samples! + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_NOMEM on memory allocation error. + */ +int ebur128_add_frames_short(ebur128_state* st, + const short* src, + size_t frames); +/** \brief See \ref ebur128_add_frames_short */ +int ebur128_add_frames_int(ebur128_state* st, + const int* src, + size_t frames); +/** \brief See \ref ebur128_add_frames_short */ +int ebur128_add_frames_float(ebur128_state* st, + const float* src, + size_t frames); +/** \brief See \ref ebur128_add_frames_short */ +int ebur128_add_frames_double(ebur128_state* st, + const double* src, + size_t frames); + +/** \brief Get global integrated loudness in LUFS. + * + * @param st library state. + * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative + * infinity. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. + */ +int ebur128_loudness_global(ebur128_state* st, double* out); +/** \brief Get global integrated loudness in LUFS across multiple instances. + * + * @param sts array of library states. + * @param size length of sts + * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative + * infinity. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. + */ +int ebur128_loudness_global_multiple(ebur128_state** sts, + size_t size, + double* out); + +/** \brief Get momentary loudness (last 400ms) in LUFS. + * + * @param st library state. + * @param out momentary loudness in LUFS. -HUGE_VAL if result is negative + * infinity. + * @return + * - EBUR128_SUCCESS on success. + */ +int ebur128_loudness_momentary(ebur128_state* st, double* out); +/** \brief Get short-term loudness (last 3s) in LUFS. + * + * @param st library state. + * @param out short-term loudness in LUFS. -HUGE_VAL if result is negative + * infinity. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_S" has not been set. + */ +int ebur128_loudness_shortterm(ebur128_state* st, double* out); + +/** \brief Get loudness range (LRA) of programme in LU. + * + * Calculates loudness range according to EBU 3342. + * + * @param st library state. + * @param out loudness range (LRA) in LU. Will not be changed in case of + * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be + * returned in this case. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_NOMEM in case of memory allocation error. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. + */ +int ebur128_loudness_range(ebur128_state* st, double* out); +/** \brief Get loudness range (LRA) in LU across multiple instances. + * + * Calculates loudness range according to EBU 3342. + * + * @param sts array of library states. + * @param size length of sts + * @param out loudness range (LRA) in LU. Will not be changed in case of + * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be + * returned in this case. + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_NOMEM in case of memory allocation error. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. + */ +int ebur128_loudness_range_multiple(ebur128_state** sts, + size_t size, + double* out); + +/** \brief Get maximum sample peak of selected channel in float format. + * + * @param st library state + * @param channel_number channel to analyse + * @param out maximum sample peak in float format (1.0 is 0 dBFS) + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not + * been set. + * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. + */ +int ebur128_sample_peak(ebur128_state* st, + unsigned int channel_number, + double* out); + +/** \brief Get maximum true peak of selected channel in float format. + * + * Uses an implementation defined algorithm to calculate the true peak. Do not + * try to compare resulting values across different versions of the library, + * as the algorithm may change. + * + * The current implementation uses the Speex resampler with quality level 8 to + * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for + * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. + * + * @param st library state + * @param channel_number channel to analyse + * @param out maximum true peak in float format (1.0 is 0 dBFS) + * @return + * - EBUR128_SUCCESS on success. + * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not + * been set. + * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. + */ +int ebur128_true_peak(ebur128_state* st, + unsigned int channel_number, + double* out); + +#ifdef __cplusplus +} +#endif + +#endif /* EBUR128_H_ */ diff -Nru mlt-0.9.0/src/modules/plus/ebur128/queue.h mlt-0.9.2/src/modules/plus/ebur128/queue.h --- mlt-0.9.0/src/modules/plus/ebur128/queue.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/ebur128/queue.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,676 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.68.2.4 2011/05/24 16:06:26 mdf Exp $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#ifdef _KERNEL + +/* + * XXX insque() and remque() are an old way of handling certain queues. + * They bogusly assumes that all queue heads look alike. + */ + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +#ifdef __CC_SUPPORTS___INLINE + +static __inline void +insque(void *a, void *b) +{ + struct quehead *element = (struct quehead *)a, + *head = (struct quehead *)b; + + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; +} + +static __inline void +remque(void *a) +{ + struct quehead *element = (struct quehead *)a; + + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +#else /* !__CC_SUPPORTS___INLINE */ + +void insque(void *a, void *b); +void remque(void *a); + +#endif /* __CC_SUPPORTS___INLINE */ + +#endif /* _KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff -Nru mlt-0.9.0/src/modules/plus/factory.c mlt-0.9.2/src/modules/plus/factory.c --- mlt-0.9.0/src/modules/plus/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -22,10 +22,17 @@ #include #include +extern mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_rgblut_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_producer producer_count_init( const char *arg ); extern mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) @@ -37,15 +44,29 @@ MLT_REPOSITORY { + MLT_REGISTER( consumer_type, "blipflash", consumer_blipflash_init ); MLT_REGISTER( filter_type, "affine", filter_affine_init ); MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init ); + MLT_REGISTER( filter_type, "dynamictext", filter_dynamictext_init ); MLT_REGISTER( filter_type, "invert", filter_invert_init ); + MLT_REGISTER( filter_type, "lift_gamma_gain", filter_lift_gamma_gain_init ); + MLT_REGISTER( filter_type, "loudness", filter_loudness_init ); + MLT_REGISTER( filter_type, "rgblut", filter_rgblut_init ); MLT_REGISTER( filter_type, "sepia", filter_sepia_init ); + MLT_REGISTER( producer_type, "blipflash", producer_blipflash_init ); + MLT_REGISTER( producer_type, "count", producer_count_init ); MLT_REGISTER( transition_type, "affine", transition_affine_init ); + MLT_REGISTER_METADATA( consumer_type, "blipflash", metadata, "consumer_blipflash.yml" ); MLT_REGISTER_METADATA( filter_type, "affine", metadata, "filter_affine.yml" ); MLT_REGISTER_METADATA( filter_type, "charcoal", metadata, "filter_charcoal.yml" ); + MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" ); MLT_REGISTER_METADATA( filter_type, "invert", metadata, "filter_invert.yml" ); + MLT_REGISTER_METADATA( filter_type, "lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" ); + MLT_REGISTER_METADATA( filter_type, "loudness", metadata, "filter_loudness.yml" ); + MLT_REGISTER_METADATA( filter_type, "rgblut", metadata, "filter_rgblut.yml" ); MLT_REGISTER_METADATA( filter_type, "sepia", metadata, "filter_sepia.yml" ); + MLT_REGISTER_METADATA( producer_type, "blipflash", metadata, "producer_blipflash.yml" ); + MLT_REGISTER_METADATA( producer_type, "count", metadata, "producer_count.yml" ); MLT_REGISTER_METADATA( transition_type, "affine", metadata, "transition_affine.yml" ); } diff -Nru mlt-0.9.0/src/modules/plus/filter_affine.c mlt-0.9.2/src/modules/plus/filter_affine.c --- mlt-0.9.0/src/modules/plus/filter_affine.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_affine.c 2014-06-29 20:23:17.000000000 +0000 @@ -28,10 +28,10 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the filter - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); @@ -39,7 +39,6 @@ // Get the image int error = 0; *format = mlt_image_rgb24a; - //mlt_frame_get_image( this, image, format, width, height, 0 ); // Only process if we have no error and a valid colour space if ( error == 0 ) @@ -67,8 +66,8 @@ if ( producer != NULL && transition != NULL ) { - mlt_position position = mlt_filter_get_position( filter, this ); - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_position in = mlt_filter_get_in( filter ); mlt_position out = mlt_filter_get_out( filter ); double consumer_ar = mlt_profile_sar( profile ); @@ -78,22 +77,21 @@ mlt_producer_set_in_and_out( producer, in, out ); } mlt_producer_seek( producer, in + position ); - mlt_frame_set_position( this, position ); + mlt_frame_set_position( frame, position ); mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." ); mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "transition." ); mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &a_frame, 0 ); mlt_frame_set_position( a_frame, in + position ); -// mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame ), "distort", 1 ); // Special case - aspect_ratio = 0 - if ( mlt_frame_get_aspect_ratio( this ) == 0 ) - mlt_frame_set_aspect_ratio( this, consumer_ar ); + if ( mlt_frame_get_aspect_ratio( frame ) == 0 ) + mlt_frame_set_aspect_ratio( frame, consumer_ar ); if ( mlt_frame_get_aspect_ratio( a_frame ) == 0 ) mlt_frame_set_aspect_ratio( a_frame, consumer_ar ); // Add the affine transition onto the frame stack mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); - mlt_transition_process( transition, a_frame, this ); + mlt_transition_process( transition, a_frame, frame ); if ( mlt_properties_get_int( properties, "use_normalised" ) ) { @@ -105,8 +103,8 @@ mlt_frame_get_image( a_frame, image, format, width, height, writable ); mlt_properties_set_data( frame_properties, "affine_frame", a_frame, 0, (mlt_destructor)mlt_frame_close, NULL ); - mlt_frame_set_image( this, *image, *width * *height * 4, NULL ); - mlt_frame_set_alpha( this, mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL ); + mlt_frame_set_image( frame, *image, *width * *height * 4, NULL ); + mlt_frame_set_alpha( frame, mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL ); } else { @@ -120,10 +118,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -134,13 +132,13 @@ mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "background", arg ? arg : "colour:0" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "background", arg ? arg : "colour:0" ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/plus/filter_charcoal.c mlt-0.9.2/src/modules/plus/filter_charcoal.c --- mlt-0.9.0/src/modules/plus/filter_charcoal.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_charcoal.c 2014-06-29 20:23:17.000000000 +0000 @@ -66,24 +66,27 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the filter - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); // Get the image *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { // Get the charcoal scatter value - int x_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "x_scatter" ); - int y_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "y_scatter" ); - float scale = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "scale" ); - float mix = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "mix" ); - int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ); + int x_scatter = mlt_properties_anim_get_double( properties, "x_scatter", position, length ); + int y_scatter = mlt_properties_anim_get_double( properties, "y_scatter", position, length ); + float scale = mlt_properties_anim_get_double( properties, "scale" ,position, length); + float mix = mlt_properties_anim_get_double( properties, "mix", position, length); + int invert = mlt_properties_anim_get_int( properties, "invert", position, length); // We'll process pixel by pixel int x = 0; @@ -138,7 +141,7 @@ *image = temp; // Store new and destroy old - mlt_frame_set_image( this, *image, *width * *height * 2, mlt_pool_release ); + mlt_frame_set_image( frame, *image, *width * *height * 2, mlt_pool_release ); } return error; @@ -147,10 +150,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; @@ -161,15 +164,15 @@ mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "x_scatter", 1 ); - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "y_scatter", 1 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "scale", 1.5 ); - mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "mix", 0.0 ); + filter->process = filter_process; + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "x_scatter", 1 ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "y_scatter", 1 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "scale", 1.5 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "mix", 0.0 ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/plus/filter_dynamictext.c mlt-0.9.2/src/modules/plus/filter_dynamictext.c --- mlt-0.9.0/src/modules/plus/filter_dynamictext.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_dynamictext.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,375 @@ +/* + * filter_dynamictext.c -- dynamic text overlay filter + * Copyright (C) 2011 Ushodaya Enterprises Limited + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include // for stat() +#include // for stat() +#include // for stat() +#include // for strftime() and gtime() + +#define MAX_TEXT_LEN 512 + +/** Get the next token and indicate whether it is enclosed in "# #". +*/ +static int get_next_token(char* str, int* pos, char* token, int* is_keyword) +{ + int token_pos = 0; + int str_len = strlen( str ); + + if( (*pos) >= str_len || str[*pos] == '\0' ) + { + return 0; + } + + if( str[*pos] == '#' ) + { + *is_keyword = 1; + (*pos)++; + } + else + { + *is_keyword = 0; + } + + while( *pos < str_len && token_pos < MAX_TEXT_LEN - 1) + { + if( str[*pos] == '\\' && str[(*pos) + 1] == '#' ) + { + // Escape Sequence - "#" preceeded by "\" - copy the # into the token. + token[token_pos] = '#'; + token_pos++; + (*pos)++; // skip "\" + (*pos)++; // skip "#" + } + else if( str[*pos] == '#' ) + { + if( *is_keyword ) + { + // Found the end of the keyword + (*pos)++; + } + break; + } + else + { + token[token_pos] = str[*pos]; + token_pos++; + (*pos)++; + } + } + + token[token_pos] = '\0'; + + return 1; +} + +static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text ) +{ + int frames = mlt_frame_get_position( frame ); + double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); + char tc[12] = ""; + if (fps == 0) + { + strncat( text, "-", MAX_TEXT_LEN - strlen( text ) - 1 ); + } + else + { + int seconds = frames / fps; + frames = frames % lrint( fps ); + int minutes = seconds / 60; + seconds = seconds % 60; + int hours = minutes / 60; + minutes = minutes % 60; + sprintf(tc, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames); + strncat( text, tc, MAX_TEXT_LEN - strlen( text ) - 1 ); + } +} + +static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text ) +{ + int pos = mlt_frame_get_position( frame ); + char s[12]; + snprintf( s, sizeof( s ) - 1, "%d", pos ); + strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 ); +} + +static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + char* filename = mlt_properties_get( producer_properties, "resource"); + struct stat file_info; + + if( !stat(filename, &file_info)) + { + struct tm* time_info = gmtime( &(file_info.st_mtime) ); + char date[11] = ""; + strftime( date, 11, "%Y/%m/%d", time_info ); + strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); + } +} + +static void get_localfiledate_str( mlt_filter filter, mlt_frame frame, char* text ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + char* filename = mlt_properties_get( producer_properties, "resource" ); + struct stat file_info; + + if( !stat( filename, &file_info ) ) + { + struct tm* time_info = localtime( &(file_info.st_mtime) ); + char date[11] = ""; + strftime( date, 11, "%Y/%m/%d", time_info ); + strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); + } +} + +static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + strncat( text, mlt_properties_get( producer_properties, "resource" ), MAX_TEXT_LEN - strlen( text ) - 1 ); +} + +/** Perform substitution for keywords that are enclosed in "# #". +*/ +static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame) +{ + char keyword[MAX_TEXT_LEN] = ""; + int pos = 0; + int is_keyword = 0; + + while ( get_next_token(value, &pos, keyword, &is_keyword) ) + { + if(!is_keyword) + { + strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 ); + } + else if ( !strcmp( keyword, "timecode" ) ) + { + get_timecode_str( filter, frame, result ); + } + else if ( !strcmp( keyword, "frame" ) ) + { + get_frame_str( filter, frame, result ); + } + else if ( !strcmp( keyword, "filedate" ) ) + { + get_filedate_str( filter, frame, result ); + } + else if ( !strcmp( keyword, "localfiledate" ) ) + { + get_localfiledate_str( filter, frame, result ); + } + else if ( !strcmp( keyword, "resource" ) ) + { + get_resource_str( filter, frame, result ); + } + else + { + // replace keyword with property value from this frame + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + char *frame_value = mlt_properties_get( frame_properties, keyword ); + if( frame_value ) + { + strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 ); + } + } + } +} + +static void setup_producer( mlt_filter filter, mlt_producer producer, mlt_frame frame ) +{ + mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + char* dynamic_text = mlt_properties_get( my_properties, "argument" ); + + // Check for keywords in dynamic text + if ( dynamic_text ) + { + // Apply keyword substitution before passing the text to the filter. + char result[MAX_TEXT_LEN] = ""; + substitute_keywords( filter, result, dynamic_text, frame ); + mlt_properties_set( producer_properties, "text", (char*)result ); + } + + // Pass the properties to the pango producer + mlt_properties_set( producer_properties, "family", mlt_properties_get( my_properties, "family" ) ); + mlt_properties_set( producer_properties, "size", mlt_properties_get( my_properties, "size" ) ); + mlt_properties_set( producer_properties, "weight", mlt_properties_get( my_properties, "weight" ) ); + mlt_properties_set( producer_properties, "fgcolour", mlt_properties_get( my_properties, "fgcolour" ) ); + mlt_properties_set( producer_properties, "bgcolour", mlt_properties_get( my_properties, "bgcolour" ) ); + mlt_properties_set( producer_properties, "olcolour", mlt_properties_get( my_properties, "olcolour" ) ); + mlt_properties_set( producer_properties, "pad", mlt_properties_get( my_properties, "pad" ) ); + mlt_properties_set( producer_properties, "outline", mlt_properties_get( my_properties, "outline" ) ); + mlt_properties_set( producer_properties, "align", mlt_properties_get( my_properties, "halign" ) ); +} + +static void setup_transition( mlt_filter filter, mlt_transition transition ) +{ + mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); + + mlt_properties_set( transition_properties, "geometry", mlt_properties_get( my_properties, "geometry" ) ); + mlt_properties_set( transition_properties, "halign", mlt_properties_get( my_properties, "halign" ) ); + mlt_properties_set( transition_properties, "valign", mlt_properties_get( my_properties, "valign" ) ); + mlt_properties_set_int( transition_properties, "out", mlt_properties_get_int( my_properties, "_out" ) ); + mlt_properties_set_int( transition_properties, "refresh", 1 ); +} + + +/** Get the image. +*/ +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_filter filter = mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_producer producer = mlt_properties_get_data( properties, "_producer", NULL ); + mlt_transition transition = mlt_properties_get_data( properties, "_transition", NULL ); + mlt_frame text_frame = NULL; + mlt_position position = 0; + + // Configure this filter + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + setup_producer( filter, producer, frame ); + setup_transition( filter, transition ); + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + + // Make sure the producer is in the correct position + position = mlt_filter_get_position( filter, frame ); + mlt_producer_seek( producer, position ); + + // Get the b frame and process with transition if successful + if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &text_frame, 0 ) == 0 ) + { + // Get the a and b frame properties + mlt_properties a_props = MLT_FRAME_PROPERTIES( frame ); + mlt_properties b_props = MLT_FRAME_PROPERTIES( text_frame ); + + // Set the frame and text_frame to be in the same position and have same consumer requirements + mlt_frame_set_position( text_frame, position ); + mlt_frame_set_position( frame, position ); + mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) ); + + // Apply all filters that are attached to this filter to the b frame + mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), text_frame, 0 ); + + // Process the frame + mlt_transition_process( transition, frame, text_frame ); + + // Get the image + *format = mlt_image_yuv422; + error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + + // Close the b frame + mlt_frame_close( text_frame ); + } + + return error; +} + +/** Filter processing. +*/ +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + // Get the properties of the frame + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + + // Save the frame out point + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_out", mlt_properties_get_int( properties, "out" ) ); + + // Push the filter on to the stack + mlt_frame_push_service( frame, filter ); + + // Push the get_image on to the stack + mlt_frame_push_get_image( frame, filter_get_image ); + + return frame; +} + +/** Constructor for the filter. +*/ +mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new(); + mlt_transition transition = mlt_factory_transition( profile, "composite", NULL ); + mlt_producer producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "qtext:" ); + + // Use pango if qtext is not available. + if( !producer ) + producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" ); + + if ( filter && transition && producer ) + { + mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); + + // Register the transition for reuse/destruction + mlt_properties_set_data( my_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); + + // Register the producer for reuse/destruction + mlt_properties_set_data( my_properties, "_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); + + // Ensure that we loop + mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); + + // Assign default values + mlt_properties_set( my_properties, "argument", arg ? arg: "#timecode#" ); + mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100" ); + mlt_properties_set( my_properties, "family", "Sans" ); + mlt_properties_set( my_properties, "size", "48" ); + mlt_properties_set( my_properties, "weight", "400" ); + mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); + mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); + mlt_properties_set( my_properties, "olcolour", "0x00000000" ); + mlt_properties_set( my_properties, "pad", "0" ); + mlt_properties_set( my_properties, "halign", "left" ); + mlt_properties_set( my_properties, "valign", "top" ); + mlt_properties_set( my_properties, "outline", "0" ); + + mlt_properties_set_int( my_properties, "_filter_private", 1 ); + + filter->process = filter_process; + } + else + { + if( filter ) + { + mlt_filter_close( filter ); + } + + if( transition ) + { + mlt_transition_close( transition ); + } + + if( producer ) + { + mlt_producer_close( producer ); + } + + filter = NULL; + } + return filter; +} diff -Nru mlt-0.9.0/src/modules/plus/filter_dynamictext.yml mlt-0.9.2/src/modules/plus/filter_dynamictext.yml --- mlt-0.9.0/src/modules/plus/filter_dynamictext.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_dynamictext.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,142 @@ +schema_version: 0.1 +type: filter +identifier: dynamictext +title: Dynamic text +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video +description: Overlay dynamic text onto the video +notes: > + The dynamic text filter will search for keywords in the text to be overlayed + and will replace those keywords on a frame-by-frame basis. + +parameters: + - identifier: argument + title: Dynamic text + type: string + description: | + The text to overlay. May include keywords enclosed in "#". + Keywords include: + * #timecode# - timecode of the frame (based on framerate and position) + * #frame# - frame number of the frame + * #filedate# - modification date of the file + Keywords may also be any frame property (e.g. #meta.media.0.codec.frame_rate#) + The # may be escaped with "\". + required: yes + readonly: no + default: > #trick to escape "#" character + #timecode# + widget: text + - identifier: geometry + title: Geometry + type: geometry + description: A set of X/Y coordinates by which to adjust the text. + default: 0%/0%:100%x100%:100 + - identifier: family + title: Font family + type: string + description: > + The typeface of the font. + default: Sans + readonly: no + mutable: yes + widget: combo + - identifier: size + title: Font size + type: integer + description: > + The size in pixels of the font. + default: 48 + readonly: no + mutable: yes + widget: spinner + - identifier: weight + title: Font weight + type: integer + description: The weight of the font. + minimum: 100 + maximum: 1000 + default: 400 + readonly: no + mutable: yes + widget: spinner + - identifier: fgcolour + title: Foreground color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + default: 0x000000ff + readonly: no + mutable: yes + widget: color + - identifier: bgcolour + title: Background color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + default: 0x00000020 + readonly: no + mutable: yes + widget: color + - identifier: olcolour + title: Outline color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + readonly: no + mutable: yes + widget: color + - identifier: outline + title: Outline Width + type: string + description: > + The width of the outline in pixels. + readonly: no + default: 0 + minimum: 0 + maximum: 3 + mutable: yes + widget: spinner + - identifier: pad + title: Padding + type: integer + description: > + The number of pixels to pad the background rectangle beyond edges of text. + readonly: no + default: 0 + mutable: yes + widget: spinner + - identifier: halign + title: Horizontal alignment + description: > + Set the horizontal alignment within the geometry rectangle. + type: string + default: left + values: + - left + - centre + - right + mutable: yes + widget: combo + - identifier: valign + title: Vertical alignment + description: > + Set the vertical alignment within the geometry rectangle. + type: string + default: top + values: + - top + - middle + - bottom + mutable: yes + widget: combo diff -Nru mlt-0.9.0/src/modules/plus/filter_invert.c mlt-0.9.2/src/modules/plus/filter_invert.c --- mlt-0.9.0/src/modules/plus/filter_invert.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_invert.c 2014-06-29 20:23:17.000000000 +0000 @@ -34,13 +34,14 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the image - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); + int mask = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "alpha" ); *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) @@ -57,7 +58,7 @@ if ( mask ) { - uint8_t *alpha = mlt_frame_get_alpha_mask( this ); + uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); int size = *width * *height; memset( alpha, mask, size ); } @@ -69,10 +70,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } @@ -82,9 +83,11 @@ mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) - this->process = filter_process; - return this; + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = filter_process; + } + return filter; } diff -Nru mlt-0.9.0/src/modules/plus/filter_lift_gamma_gain.c mlt-0.9.2/src/modules/plus/filter_lift_gamma_gain.c --- mlt-0.9.0/src/modules/plus/filter_lift_gamma_gain.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_lift_gamma_gain.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,246 @@ +/* + * filter_lift_gamma_gain.cpp + * Copyright (C) 2014 Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +typedef struct +{ + uint8_t rlut[256]; + uint8_t glut[256]; + uint8_t blut[256]; + double rlift, glift, blift; + double rgamma, ggamma, bgamma; + double rgain, ggain, bgain; +} private_data; + +static void refresh_lut( mlt_filter filter, mlt_frame frame ) +{ + private_data* private = (private_data*)filter->child; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length ); + double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length ); + double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length ); + double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length ); + double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length ); + double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length ); + double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length ); + double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length ); + double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length ); + + // Only regenerate the LUT if something changed. + if( private->rlift != rlift || private->glift != glift || private->blift != blift || + private->rgamma != rgamma || private->ggamma != ggamma || private->bgamma != bgamma || + private->rgain != rgain || private->ggain != ggain || private->bgain != bgain ) + { + int i = 0; + for( i = 0; i < 256; i++ ) + { + // Convert to gamma 2.2 + double gamma22 = pow( (double)i / 255.0, 1.0 / 2.2 ); + double r = gamma22; + double g = gamma22; + double b = gamma22; + + // Apply lift + r += rlift * ( 1.0 - r ); + g += glift * ( 1.0 - g ); + b += blift * ( 1.0 - b ); + + // Apply gamma + r = pow( r, 2.2 / rgamma ); + g = pow( g, 2.2 / ggamma ); + b = pow( b, 2.2 / bgamma ); + + // Apply gain + r *= pow( rgain, 1.0 / rgamma ); + g *= pow( ggain, 1.0 / ggamma ); + b *= pow( bgain, 1.0 / bgamma ); + + // Clamp values + r = r < 0.0 ? 0.0 : r > 1.0 ? 1.0 : r; + g = g < 0.0 ? 0.0 : g > 1.0 ? 1.0 : g; + b = b < 0.0 ? 0.0 : b > 1.0 ? 1.0 : b; + + // Update LUT + private->rlut[ i ] = (int)(r * 255.0); + private->glut[ i ] = (int)(g * 255.0); + private->blut[ i ] = (int)(b * 255.0); + } + + // Store the values that created the LUT so that + // changes can be detected. + private->rlift = rlift; + private->glift = glift; + private->blift = blift; + private->rgamma = rgamma; + private->ggamma = ggamma; + private->bgamma = bgamma; + private->rgain = rgain; + private->ggain = ggain; + private->bgain = bgain; + } +} + +static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height ) +{ + private_data* private = (private_data*)filter->child; + int total = width * height + 1; + uint8_t* sample = image; + + switch( format ) + { + case mlt_image_rgb24: + while( --total ) + { + *sample = private->rlut[ *sample ]; + sample++; + *sample = private->glut[ *sample ]; + sample++; + *sample = private->blut[ *sample ]; + sample++; + } + break; + case mlt_image_rgb24a: + while( --total ) + { + *sample = private->rlut[ *sample ]; + sample++; + *sample = private->glut[ *sample ]; + sample++; + *sample = private->blut[ *sample ]; + sample++; + sample++; // Skip alpha + } + break; + default: + mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) ); + break; + } +} + +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + int error = 0; + + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + + // Regenerate the LUT if necessary + refresh_lut( filter, frame ); + + // Make sure the format is acceptable + if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) + { + *format = mlt_image_rgb24; + } + + // Get the image + writable = 1; + error = mlt_frame_get_image( frame, image, format, width, height, writable ); + + // Apply the LUT + if( !error ) + { + apply_lut( filter, *image, *format, *width, *height ); + } + + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + + return error; +} + +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, filter_get_image ); + return frame; +} + +static void filter_close( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + + if ( private ) + { + free( private ); + } + filter->child = NULL; + filter->close = NULL; + filter->parent.close = NULL; + mlt_service_close( &filter->parent ); +} + +mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new(); + private_data* private = (private_data*)calloc( 1, sizeof(private_data) ); + int i = 0; + + if ( filter && private ) + { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + + // Initialize private data + for( i = 0; i < 256; i++ ) + { + private->rlut[i] = i; + private->glut[i] = i; + private->blut[i] = i; + } + private->rlift = private->glift = private->blift = 0.0; + private->rgamma = private->ggamma = private->bgamma = 1.0; + private->rgain = private->ggain = private->bgain = 1.0; + + // Initialize filter properties + mlt_properties_set_double( properties, "lift_r", private->rlift ); + mlt_properties_set_double( properties, "lift_g", private->glift ); + mlt_properties_set_double( properties, "lift_b", private->blift ); + mlt_properties_set_double( properties, "gamma_r", private->rgamma ); + mlt_properties_set_double( properties, "gamma_g", private->ggamma ); + mlt_properties_set_double( properties, "gamma_b", private->bgamma ); + mlt_properties_set_double( properties, "gain_r", private->rgain ); + mlt_properties_set_double( properties, "gain_g", private->ggain ); + mlt_properties_set_double( properties, "gain_b", private->bgain ); + + filter->close = filter_close; + filter->process = filter_process; + filter->child = private; + } + else + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" ); + + if( filter ) + { + mlt_filter_close( filter ); + filter = NULL; + } + + if( private ) + { + free( private ); + } + } + + return filter; +} diff -Nru mlt-0.9.0/src/modules/plus/filter_lift_gamma_gain.yml mlt-0.9.2/src/modules/plus/filter_lift_gamma_gain.yml --- mlt-0.9.0/src/modules/plus/filter_lift_gamma_gain.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_lift_gamma_gain.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,89 @@ +schema_version: 0.1 +type: filter +identifier: lift_gamma_gain +title: Lift, Gamma, and Gain +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video +description: > + A simple lift/gamma/gain effect, used for color grading. +notes: > + Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, + although all parameters affect the entire curve. Mathematically speaking, + it is a bit unusual to look at gamma as a color, but it works pretty well + in practice. + The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). + The lift is a case where we actually would _not_ want linear light; + since black by definition becomes equal to the lift color, we want lift to + be pretty close to black, but in linear light that means lift affects the + rest of the curve relatively little. Thus, we actually convert to gamma 2.2 + before lift, and then back again afterwards. (Gain and gamma are, + up to constants, commutative with the de-gamma operation.) + +parameters: + - identifier: lift_r + title: Lift Red + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_g + title: Lift Green + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_b + title: Lift Blue + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: gamma_r + title: Gamma Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_g + title: Gamma Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_b + title: Gamma Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_r + title: Gain Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_g + title: Gain Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_b + title: Gain Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes diff -Nru mlt-0.9.0/src/modules/plus/filter_loudness.c mlt-0.9.2/src/modules/plus/filter_loudness.c --- mlt-0.9.0/src/modules/plus/filter_loudness.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_loudness.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,267 @@ +/* + * filter_ebur128.c -- normalize audio according to EBU R128 + * Copyright (C) 2014 Brian Matherly + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "ebur128/ebur128.h" + +#define MAX_RESULT_SIZE 512 + +typedef struct +{ + ebur128_state* state; +} analyze_data; + +typedef struct +{ + double in_loudness; + double in_range; + double in_peak; +} apply_data; + +typedef struct +{ + analyze_data* analyze; + apply_data* apply; + mlt_position last_position; +} private_data; + +static void destroy_analyze_data( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + ebur128_destroy( &private->analyze->state ); + free( private->analyze ); + private->analyze = NULL; +} + +static void init_analyze_data( mlt_filter filter, int channels, int samplerate ) +{ + private_data* private = (private_data*)filter->child; + private->analyze = (analyze_data*)calloc( 1, sizeof(analyze_data) ); + private->analyze->state = ebur128_init( (unsigned int)channels, (unsigned long)samplerate, EBUR128_MODE_I | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK ); + private->last_position = 0; +} + +static void destroy_apply_data( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + free( private->apply ); + private->apply = NULL; +} + +static void init_apply_data( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + char* results = mlt_properties_get( properties, "results" ); + int scan_return = 0; + + private->apply = (apply_data*)calloc( 1, sizeof(apply_data) ); + + scan_return = sscanf( results,"L: %lf\tR: %lf\tP %lf", &private->apply->in_loudness, &private->apply->in_range, &private->apply->in_peak ); + if( scan_return != 3 ) + { + mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to load results: %s\n", results ); + destroy_apply_data( filter ); + return; + } + else + { + mlt_log_info( MLT_FILTER_SERVICE( filter ), "Loaded Results: L: %lf\tR: %lf\tP %lf\n", private->apply->in_loudness, private->apply->in_range, private->apply->in_peak ); + } +} + +static void analyze( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + private_data* private = (private_data*)filter->child; + mlt_position pos = mlt_filter_get_position( filter, frame ); + + // If any frames are skipped, analysis data will be incomplete. + if( private->analyze && pos != private->last_position + 1 ) + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n" ); + destroy_analyze_data( filter ); + } + + // Analyze Audio + if( !private->analyze && pos == 0 ) + { + init_analyze_data( filter, *channels, *frequency ); + } + + if( private->analyze ) + { + ebur128_add_frames_float( private->analyze->state, *buffer, *samples ); + + if ( pos + 1 == mlt_filter_get_length2( filter, frame ) ) + { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + double loudness = 0.0; + double range = 0.0; + double tmpPeak = 0.0; + double peak = 0.0; + int i = 0; + char result[MAX_RESULT_SIZE]; + ebur128_loudness_global( private->analyze->state, &loudness ); + ebur128_loudness_range( private->analyze->state, &range ); + + for ( i = 0; i < *channels; i++ ) + { + ebur128_sample_peak( private->analyze->state, i, &tmpPeak ); + if( tmpPeak > peak ) + { + peak = tmpPeak; + } + } + + snprintf( result, MAX_RESULT_SIZE, "L: %lf\tR: %lf\tP %lf", loudness, range, peak ); + result[ MAX_RESULT_SIZE - 1 ] = '\0'; + mlt_log_info( MLT_FILTER_SERVICE( filter ), "Stored results: %s", result ); + mlt_properties_set( properties, "results", result ); + destroy_analyze_data( filter ); + } + + private->last_position = pos; + } +} + +static void apply( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + private_data* private = (private_data*)filter->child; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + + // Analyze Audio + if( !private->apply ) + { + init_apply_data( filter ); + } + + if( private->apply ) + { + double target_db = mlt_properties_get_double( properties, "program" ); + double delta_db = target_db - private->apply->in_loudness; + double coeff = delta_db > -90.0f ? powf(10.0f, delta_db * 0.05f) : 0.0f; + float* p = *buffer; + int count = *samples * *channels; + while( count-- ) + { + *p = *p * coeff; + p++; + } + } +} + +/** Get the audio. +*/ + +static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + mlt_filter filter = mlt_frame_pop_audio( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + + // Get the producer's audio + *format = mlt_audio_f32le; + mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); + + char* results = mlt_properties_get( properties, "results" ); + if( results && strcmp( results, "" ) ) + { + apply( filter, frame, buffer, format, frequency, channels, samples ); + } + else + { + analyze( filter, frame, buffer, format, frequency, channels, samples ); + } + + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + + return 0; +} + +/** Filter processing. +*/ + +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_audio( frame, filter ); + mlt_frame_push_audio( frame, filter_get_audio ); + return frame; +} + +/** Destructor for the filter. +*/ + +static void filter_close( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + + if ( private ) + { + if ( private->analyze ) + { + destroy_analyze_data( filter ); + } + free( private ); + } + filter->child = NULL; + filter->close = NULL; + filter->parent.close = NULL; + mlt_service_close( &filter->parent ); +} + +/** Constructor for the filter. +*/ + +mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + private_data* data = (private_data*)calloc( 1, sizeof(private_data) ); + + if ( filter && data ) + { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set( properties, "program", "-23.0" ); + + data->analyze = NULL; + + filter->close = filter_close; + filter->process = filter_process; + filter->child = data; + } + else + { + if( filter ) + { + mlt_filter_close( filter ); + filter = NULL; + } + + if( data ) + { + free( data ); + } + } + + return filter; +} diff -Nru mlt-0.9.0/src/modules/plus/filter_loudness.yml mlt-0.9.2/src/modules/plus/filter_loudness.yml --- mlt-0.9.0/src/modules/plus/filter_loudness.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_loudness.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,41 @@ +schema_version: 0.1 +type: filter +identifier: loudness +title: Loudness +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Audio +description: Correct audio loudness as recommended by EBU R128. +notes: > + This filter requires two passes. The first pass performs analysis and stores + the result in the "results" property. The second pass applies the results to + the audio in order to achieve the desired loudness over the range of the + filter. + +parameters: + - identifier: results + title: Analysis Results + type: string + description: > + Set after analysis. Used during application. + Loudness information about the original audio. + When results are not supplied, the filter computes the results and stores + them in this property when the last frame has been processed. + mutable: no + + - identifier: program + title: Target Program Loudness + type: float + description: > + Used during application. + The target program loudness in LUFS (Loudness Units Full Scale). + readonly: no + mutable: yes + default: -23.0 + minimum: -50.0 + maximum: -10.0 + unit: LUFS diff -Nru mlt-0.9.0/src/modules/plus/filter_rgblut.c mlt-0.9.2/src/modules/plus/filter_rgblut.c --- mlt-0.9.0/src/modules/plus/filter_rgblut.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_rgblut.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,129 @@ +/* + * filter_rgblut.c -- generic RGB look-up table filter with string interface + * Copyright (C) 2014 Janne Liljeblad + * Author: Janne Liljeblad + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +/** Fill channel lut with integers parsed from property string. +*/ +static void fill_channel_lut(int lut[], char* channel_table_str) +{ + mlt_tokeniser tokeniser = mlt_tokeniser_init(); + mlt_tokeniser_parse_new( tokeniser, channel_table_str, ";" ); + + // Only create lut from string if tokens count exactly right + if ( tokeniser->count == 256 ) + { + // Fill lut with token values + int i; + int val; + for( i = 0; i < 256; i++ ) + { + val = atoi(tokeniser->tokens[i]); + lut[i] = val; + } + } + else + { + // Fill lut with linear no-op table + int i; + for( i = 0; i < 256; i++ ) + { + lut[i] = i; + } + } + mlt_tokeniser_close( tokeniser ); +} + +/** Do it :-). +*/ +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + // Get the image + mlt_filter filter = mlt_frame_pop_service( frame ); + + *format = mlt_image_rgb24; + int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); + + // Only process if we have no error and a valid colour space + if ( error == 0 ) + { + + // Create lut tables from properties for each RGB channel + char* r_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "R_table" ); + int r_lut[256]; + fill_channel_lut( r_lut, r_str ); + + char* g_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "G_table" ); + int g_lut[256]; + fill_channel_lut( g_lut, g_str ); + + char* b_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "B_table" ); + int b_lut[256]; + fill_channel_lut( b_lut, b_str ); + + // Apply look-up tables into image + int i = *width * *height + 1; + uint8_t *p = *image; + uint8_t *r = *image; + while ( --i ) + { + *p ++ = r_lut[ *r ++ ]; + *p ++ = g_lut[ *r ++ ]; + *p ++ = b_lut[ *r ++ ]; + } + } + + return error; +} + +/** Filter processing. +*/ + +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + // Push the frame filter + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, filter_get_image ); + return frame; +} + +/** Constructor for the filter. +*/ + +mlt_filter filter_rgblut_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "R_table", "unset" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "G_table", "unset" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "B_table", "unset" ); + } + return filter; +} + diff -Nru mlt-0.9.0/src/modules/plus/filter_rgblut.yml mlt-0.9.2/src/modules/plus/filter_rgblut.yml --- mlt-0.9.0/src/modules/plus/filter_rgblut.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_rgblut.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,47 @@ +schema_version: 0.1 +type: filter +identifier: rgblut +title: RGBLUT +version: 1 +copyright: Janne Liljeblad +creator: Janne Liljeblad +license: LGPLv2.1 +language: en +tags: + - Video +description: + Converts input strings with exactly 256 semicolon separated integer values in + range 0 - 255 to look-up tables that are then used to modify R, G, B values. + + This creates a generic string interface for color correction. + +parameters: + - identifier: R_table + title: Red channel look-up table + type: string + default: unset + description: > + Value is tokenised using semicolon separator into exactly 256 integer + values in range 0 - 255 and a look-up table for red channel values is + created and applied to image. If tokenising of value fails a linear table + that returns input values unchanged is used instead. + + - identifier: G_table + title: Green channel look-up table + type: string + default: unset + description: > + Value is tokenised using semicolon separator into exactly 256 integer + values in range 0 - 255 and a look-up table for green channel values is + created and applied to image. If tokenising of value fails a linear table + that returns input values unchanged is used instead. + + - identifier: B_table + title: Blue channel look-up table + type: string + default: unset + description: > + Value is tokenised using semicolon separator into exactly 256 integer + values in range 0 - 255 and a look-up table for green channel values is + created and applied to image. If tokenising of value fails a linear table + that returns input values unchanged is used instead. diff -Nru mlt-0.9.0/src/modules/plus/filter_sepia.c mlt-0.9.2/src/modules/plus/filter_sepia.c --- mlt-0.9.0/src/modules/plus/filter_sepia.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_sepia.c 2014-06-29 20:23:17.000000000 +0000 @@ -28,14 +28,17 @@ /** Do it :-). */ -static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the filter - mlt_filter filter = mlt_frame_pop_service( this ); + mlt_filter filter = mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); // Get the image *format = mlt_image_yuv422; - int error = mlt_frame_get_image( this, image, format, width, height, 1 ); + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 && *image ) @@ -48,8 +51,8 @@ int t; // Get u and v values - int u = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "u" ); - int v = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "v" ); + int u = mlt_properties_anim_get_int( properties, "u", position, length ); + int v = mlt_properties_anim_get_int( properties, "v", position, length ); // Loop through image while( h -- ) @@ -76,10 +79,10 @@ /** Filter processing. */ -static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter - mlt_frame_push_service( frame, this ); + mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } @@ -89,13 +92,13 @@ mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { - mlt_filter this = mlt_filter_new( ); - if ( this != NULL ) + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) { - this->process = filter_process; - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "u", "75" ); - mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "v", "150" ); + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "u", "75" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "v", "150" ); } - return this; + return filter; } diff -Nru mlt-0.9.0/src/modules/plus/filter_sepia.yml mlt-0.9.2/src/modules/plus/filter_sepia.yml --- mlt-0.9.0/src/modules/plus/filter_sepia.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/filter_sepia.yml 2014-06-29 20:23:17.000000000 +0000 @@ -9,3 +9,19 @@ language: en tags: - Video +description: > + Apply a color tint. Default values give a sepia tone like an old photograph. +parameters: + - identifier: u + type: integer + title: Yellow-Blue + minimum: 0 + maximum: 255 + default: 75 + + - identifier: v + type: integer + title: Cyan-Red + minimum: 0 + maximum: 255 + default: 150 diff -Nru mlt-0.9.0/src/modules/plus/Makefile mlt-0.9.2/src/modules/plus/Makefile --- mlt-0.9.0/src/modules/plus/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -1,17 +1,25 @@ CFLAGS += -I../.. -LDFLAGS += -L../../framework -lmlt -lm +LDFLAGS += -L../../framework -lmlt -lm -lpthread include ../../../config.mak TARGET = ../libmltplus$(LIBSUF) -OBJS = factory.o \ +OBJS = consumer_blipflash.o \ + factory.o \ filter_affine.o \ filter_charcoal.o \ + filter_dynamictext.o \ filter_invert.o \ + filter_lift_gamma_gain.o \ + filter_loudness.o \ + filter_rgblut.o \ filter_sepia.o \ - transition_affine.o + producer_blipflash.o \ + producer_count.o \ + transition_affine.o \ + ebur128/ebur128.o SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.0/src/modules/plus/producer_blipflash.c mlt-0.9.2/src/modules/plus/producer_blipflash.c --- mlt-0.9.0/src/modules/plus/producer_blipflash.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/producer_blipflash.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,305 @@ +/* + * producer_blipflash.c -- blip/flash generating producer + * Copyright (C) 2013 Brian Matherly + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +/** Fill an audio buffer with 1kHz "blip" samples. +*/ + +static void fill_blip( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) +{ + int new_size = samples * channels * sizeof( float ); + int old_size = 0; + float* blip = mlt_properties_get_data( producer_properties, "_blip", &old_size ); + + if( !blip || new_size > old_size ) + { + blip = mlt_pool_alloc( new_size ); + + // Fill the blip buffer + if ( blip != NULL ) + { + int s = 0; + int c = 0; + + for( s = 0; s < samples; s++ ) + { + float f = 1000.0; + float t = (float)s/(float)frequency; + // Add 90 deg so the blip always starts at 1 for easy detection. + float phase = M_PI / 2; + float value = sin( 2*M_PI*f*t + phase ); + + for( c = 0; c < channels; c++ ) + { + float* sample_ptr = ((float*) blip) + (c * samples) + s; + *sample_ptr = value; + } + } + } + // Cache the audio blip to save from regenerating it with every blip. + mlt_properties_set_data( producer_properties, "_blip", blip, new_size, mlt_pool_release, NULL ); + }; + + if( blip ) memcpy( buffer, blip, new_size ); +} + +static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) +{ + mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + int size = *samples * *channels * sizeof( float ); + double fps = mlt_producer_get_fps( producer ); + int frames = mlt_frame_get_position( frame ) + mlt_properties_get_int( producer_properties, "offset" ); + int seconds = frames / fps; + + // Correct the returns if necessary + *format = mlt_audio_float; + *frequency = *frequency <= 0 ? 48000 : *frequency; + *channels = *channels <= 0 ? 2 : *channels; + *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, frames ) : *samples; + + // Allocate the buffer + *buffer = mlt_pool_alloc( size ); + + // Determine if this should be a blip or silence. + frames = frames % lrint( fps ); + seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); + if( seconds == 0 && frames == 0 ) + { + fill_blip( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); + } + else + { + // Fill silence. + memset( *buffer, 0, size ); + } + + // Set the buffer for destruction + mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); + + return 0; +} + +/** Fill an image buffer with either white (flash) or black as requested. +*/ + +static void fill_image( mlt_properties producer_properties, char* color, uint8_t* buffer, mlt_image_format format, int width, int height ) +{ + + int new_size = mlt_image_format_size( format, width, height, NULL ); + int old_size = 0; + uint8_t* image = mlt_properties_get_data( producer_properties, color, &old_size ); + + if( !image || new_size > old_size ) + { + // Need to create a new cached image. + image = mlt_pool_alloc( new_size ); + + if ( image != NULL ) + { + uint8_t r, g, b; + uint8_t* p = image; + + if( !strcmp( color, "_flash" ) ) + { + r = g = b = 255; // White + } + else + { + r = g = b = 0; // Black + } + + switch( format ) + { + default: + case mlt_image_yuv422: + { + int uneven = width % 2; + int count = ( width - uneven ) / 2 + 1; + uint8_t y, u, v; + + RGB2YUV_601_SCALED( r, g, b, y, u, v ); + int i = height + 1; + while ( --i ) + { + int j = count; + while ( --j ) + { + *p ++ = y; + *p ++ = u; + *p ++ = y; + *p ++ = v; + } + if ( uneven ) + { + *p ++ = y; + *p ++ = u; + } + } + break; + } + case mlt_image_rgb24: + { + int i = width * height + 1; + while ( --i ) + { + *p ++ = r; + *p ++ = g; + *p ++ = b; + } + break; + } + case mlt_image_rgb24a: + { + int i = width * height + 1; + while ( --i ) + { + *p ++ = r; + *p ++ = g; + *p ++ = b; + *p ++ = 255; // alpha + } + break; + } + } + // Cache the image to save from regenerating it with every frame. + mlt_properties_set_data( producer_properties, color, image, new_size, mlt_pool_release, NULL ); + } + } + + if( image ) memcpy( buffer, image, new_size ); +} + +static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable ) +{ + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + int size = 0; + double fps = mlt_producer_get_fps( producer ); + int frames = mlt_frame_get_position( frame ); + int seconds = frames / fps; + + mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); + + // Correct the returns if necessary + if( *format != mlt_image_yuv422 && *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) + *format = mlt_image_yuv422; + if( *width <= 0 ) + *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; + if ( *height <= 0 ) + *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; + + // Allocate the buffer + size = mlt_image_format_size( *format, *width, *height, NULL ); + *buffer = mlt_pool_alloc( size ); + + // Determine if this should be a flash or black. + frames = frames % lrint( fps ); + seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); + if( seconds == 0 && frames == 0 ) + { + fill_image( producer_properties, "_flash", *buffer, *format, *width, *height ); + } + else + { + fill_image( producer_properties, "_black", *buffer, *format, *width, *height ); + } + + mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); + + // Create the alpha channel + int alpha_size = *width * *height; + uint8_t *alpha = mlt_pool_alloc( alpha_size ); + if ( alpha ) + memset( alpha, 255, alpha_size ); + + // Update the frame + mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); + mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); + mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) ); + mlt_properties_set_int( properties, "progressive", 1 ); + mlt_properties_set_int( properties, "meta.media.width", *width ); + mlt_properties_set_int( properties, "meta.media.height", *height ); + + return 0; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + // Generate a frame + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + + if ( *frame != NULL ) + { + // Obtain properties of frame + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); + + // Save the producer to be used later + mlt_properties_set_data( frame_properties, "_producer_blipflash", producer, 0, NULL, NULL ); + + // Update time code on the frame + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + // Configure callbacks + mlt_frame_push_get_image( *frame, producer_get_image ); + mlt_frame_push_audio( *frame, producer_get_audio ); + } + + // Calculate the next time code + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer this ) +{ + this->close = NULL; + mlt_producer_close( this ); + free( this ); +} + +/** Initialize. +*/ + +mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + // Create a new producer object + mlt_producer producer = mlt_producer_new( profile ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + // Initialize the producer + if ( producer ) + { + mlt_properties_set_int( producer_properties, "period", 1 ); + mlt_properties_set_int( producer_properties, "offset", 0 ); + + // Callback registration + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; + } + + return producer; +} diff -Nru mlt-0.9.0/src/modules/plus/producer_blipflash.yml mlt-0.9.2/src/modules/plus/producer_blipflash.yml --- mlt-0.9.0/src/modules/plus/producer_blipflash.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/producer_blipflash.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,36 @@ +schema_version: 0.1 +type: producer +identifier: blipflash +title: Blip Flash +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Audio + - Video +description: > + Generate periodic synchronized audio blips and video flashes. + Blips are a 1kHz tone and last the duration of the flash frame. +parameters: + - identifier: period + title: Flash Period + type: integer + description: > + The period between flashes in seconds. + default: 1 + readonly: no + mutable: yes + widget: spinner + - identifier: offset + title: Audio Offset + type: integer + description: > + The number of frames to offset the audio. + A positive number results in audio earlier than video. + An negative number results in audio later than video. + default: 0 + readonly: no + mutable: yes + widget: spinner diff -Nru mlt-0.9.0/src/modules/plus/producer_count.c mlt-0.9.2/src/modules/plus/producer_count.c --- mlt-0.9.0/src/modules/plus/producer_count.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/producer_count.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,619 @@ +/* + * producer_count.c -- counting producer + * Copyright (C) 2013 Brian Matherly + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +/* Private Constants */ +#define MAX_TEXT_LEN 512 +#define LINE_PIXEL_VALUE 0x00 +#define RING_PIXEL_VALUE 0xff +#define CLOCK_PIXEL_VALUE 0x50 +#define FRAME_BACKGROUND_COLOR "0xd0d0d0ff" +#define TEXT_BACKGROUND_COLOR "0x00000000" +#define TEXT_FOREGROUND_COLOR "0x000000ff" +// Ratio of graphic elements relative to image size +#define LINE_WIDTH_RATIO 1 +#define OUTER_RING_RATIO 90 +#define INNER_RING_RATIO 80 +#define TEXT_SIZE_RATIO 70 + +static inline void mix_pixel( uint8_t* image, int width, int x, int y, int value, float mix ) +{ + uint8_t* p = image + (( y * width ) + x ) * 4; + + if( mix != 1.0 ) + { + value = ((float)value * mix) + ((float)*p * (1.0 - mix)); + } + + *p = value; + p++; + *p = value; + p++; + *p = value; +} + +/** Fill an audio buffer with 1kHz samples. +*/ + +static void fill_beep( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) +{ + int s = 0; + int c = 0; + + for( s = 0; s < samples; s++ ) + { + float f = 1000.0; + float t = (float)s/(float)frequency; + float value = sin( 2*M_PI*f*t ); + + for( c = 0; c < channels; c++ ) + { + float* sample_ptr = buffer + (c * samples) + s; + *sample_ptr = value; + } + } +} + +static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) +{ + mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_count", NULL ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + char* sound = mlt_properties_get( producer_properties, "sound" ); + double fps = mlt_producer_get_fps( producer ); + int position = mlt_frame_get_position( frame ); + int size = 0; + int do_beep = 0; + + if( fps == 0 ) fps = 25; + + // Correct the returns if necessary + *format = mlt_audio_float; + *frequency = *frequency <= 0 ? 48000 : *frequency; + *channels = *channels <= 0 ? 2 : *channels; + *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples; + + // Allocate the buffer + size = *samples * *channels * sizeof( float ); + *buffer = mlt_pool_alloc( size ); + + // Determine if this should be a tone or silence. + if( strcmp( sound, "none") ) + { + if( !strcmp( sound, "2pop" ) ) + { + int out = mlt_properties_get_int( producer_properties, "out" ); + int frames = out - position; + + if( frames == lrint( fps * 2 ) ) + { + do_beep = 1; + } + } + else if( !strcmp( sound, "frame0" ) ) + { + int frames = position; + + // Apply the direction + char* direction = mlt_properties_get( producer_properties, "direction" ); + if( !strcmp( direction, "down" ) ) + { + int out = mlt_properties_get_int( producer_properties, "out" ); + frames = out - position; + } + + frames = position % lrint( fps ); + if( frames == 0 ) + { + do_beep = 1; + } + } + } + + if( do_beep ) + { + fill_beep( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); + } + else + { + // Fill silence. + memset( *buffer, 0, size ); + } + + // Set the buffer for destruction + mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); + return 0; +} + +static mlt_frame get_background_frame( mlt_producer producer ) +{ + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_frame bg_frame = NULL; + mlt_producer color_producer = mlt_properties_get_data( producer_properties, "_color_producer", NULL ); + + if( !color_producer ) + { + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + color_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "colour:" ); + mlt_properties_set_data( producer_properties, "_color_producer", color_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); + + mlt_properties color_properties = MLT_PRODUCER_PROPERTIES( color_producer ); + mlt_properties_set( color_properties, "colour", FRAME_BACKGROUND_COLOR ); + } + + if( color_producer ) + { + mlt_producer_seek( color_producer, 0 ); + mlt_service_get_frame( MLT_PRODUCER_SERVICE( color_producer ), &bg_frame, 0 ); + } + + return bg_frame; +} + +static mlt_frame get_text_frame( mlt_producer producer, mlt_position position ) +{ + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_producer text_producer = mlt_properties_get_data( producer_properties, "_text_producer", NULL ); + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + mlt_frame text_frame = NULL; + + if( !text_producer ) + { + text_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "qtext:" ); + + // Save the producer for future use. + mlt_properties_set_data( producer_properties, "_text_producer", text_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); + + // Calculate the font size. + char font_size[MAX_TEXT_LEN]; + snprintf( font_size, MAX_TEXT_LEN - 1, "%dpx", profile->height * TEXT_SIZE_RATIO / 100 ); + + // Configure the producer. + mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer ); + mlt_properties_set( text_properties, "size", font_size ); + mlt_properties_set( text_properties, "weight", "400" ); + mlt_properties_set( text_properties, "fgcolour", TEXT_FOREGROUND_COLOR ); + mlt_properties_set( text_properties, "bgcolour", TEXT_BACKGROUND_COLOR ); + mlt_properties_set( text_properties, "pad", "0" ); + mlt_properties_set( text_properties, "outline", "0" ); + mlt_properties_set( text_properties, "align", "center" ); + } + + if( text_producer ) + { + mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer ); + char* direction = mlt_properties_get( producer_properties, "direction" ); + char* style = mlt_properties_get( producer_properties, "style" ); + char text[MAX_TEXT_LEN] = ""; + int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25; + + // Apply the direction + if( !strcmp( direction, "down" ) ) + { + int out = mlt_properties_get_int( producer_properties, "out" ); + position = out - position; + } + + // Calculate clock values + int seconds = position / fps; + int frames = MLT_POSITION_MOD(position, fps); + int minutes = seconds / 60; + seconds = seconds % 60; + int hours = minutes / 60; + minutes = minutes % 60; + + // Apply the time style + if( !strcmp( style, "frames" ) ) + { + snprintf( text, MAX_TEXT_LEN - 1, MLT_POSITION_FMT, position ); + } + else if( !strcmp( style, "timecode" ) ) + { + snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames ); + } + else if( !strcmp( style, "clock" ) ) + { + snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d", hours, minutes, seconds ); + } + else if( !strcmp( style, "seconds+1" ) ) + { + snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds + 1 ); + } + else // seconds + { + snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds ); + } + + mlt_properties_set( text_properties, "text", text ); + + // Get the frame. + mlt_service_get_frame( MLT_PRODUCER_SERVICE( text_producer ), &text_frame, 0 ); + } + + return text_frame; +} + +static void draw_ring( uint8_t* image, mlt_profile profile, int radius, int line_width ) +{ + float sar = mlt_profile_sar( profile ); + int x_center = profile->width / 2; + int y_center = profile->height / 2; + int max_radius = radius + line_width; + int a = max_radius + 1; + int b = 0; + + line_width += 1; // Compensate for aliasing. + + // Scan through each pixel in one quadrant of the circle. + while( a-- ) + { + b = ( max_radius / sar ) + 1.0; + while( b-- ) + { + // Use Pythagorean theorem to determine the distance from this pixel to the center. + float a2 = a*a; + float b2 = b*sar*b*sar; + float c = sqrtf( a2 + b2 ); + float distance = c - radius; + + if( distance > 0 && distance < line_width ) + { + // This pixel is within the ring. + float mix = 1.0; + + if( distance < 1.0 ) + { + // Antialias the outside of the ring + mix = distance; + } + else if( (float)line_width - distance < 1.0 ) + { + // Antialias the inside of the ring + mix = (float)line_width - distance; + } + + // Apply this value to all 4 quadrants of the circle. + mix_pixel( image, profile->width, x_center + b, y_center - a, RING_PIXEL_VALUE, mix ); + mix_pixel( image, profile->width, x_center - b, y_center - a, RING_PIXEL_VALUE, mix ); + mix_pixel( image, profile->width, x_center + b, y_center + a, RING_PIXEL_VALUE, mix ); + mix_pixel( image, profile->width, x_center - b, y_center + a, RING_PIXEL_VALUE, mix ); + } + } + } +} + +static void draw_cross( uint8_t* image, mlt_profile profile, int line_width ) +{ + int x = 0; + int y = 0; + int i = 0; + + // Draw a horizontal line + i = line_width; + while( i-- ) + { + y = ( profile->height - line_width ) / 2 + i; + x = profile->width - 1; + while( x-- ) + { + mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); + } + } + + // Draw a vertical line + line_width = lrint((float)line_width * mlt_profile_sar( profile )); + i = line_width; + while( i-- ) + { + x = ( profile->width - line_width ) / 2 + i; + y = profile->height - 1; + while( y-- ) + { + mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); + } + } +} + +static void draw_clock( uint8_t* image, mlt_profile profile, int angle, int line_width ) +{ + float sar = mlt_profile_sar( profile ); + int q = 0; + int x_center = profile->width / 2; + int y_center = profile->height / 2; + + line_width += 1; // Compensate for aliasing. + + // Look at each quadrant of the frame to see what should be done. + for( q = 1; q <= 4; q++ ) + { + int max_angle = q * 90; + int x_sign = ( q == 1 || q == 2 ) ? 1 : -1; + int y_sign = ( q == 1 || q == 4 ) ? 1 : -1; + int x_start = x_center * x_sign; + int y_start = y_center * y_sign; + + // Compensate for rounding error of even lengths + // (there is no "middle" pixel so everything is offset). + if( x_sign == 1 && profile->width % 2 == 0 ) x_start--; + if( y_sign == -1 && profile->height % 2 == 0 ) y_start++; + + if( angle >= max_angle ) + { + // This quadrant is completely behind the clock hand. Fill it in. + int dx = x_start + x_sign; + while( dx ) + { + dx -= x_sign; + int dy = y_start + y_sign; + while( dy ) + { + dy -= y_sign; + mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); + } + } + } + else if ( max_angle - angle < 90 ) + { + // This quadrant is partially filled + // Calculate a point (vx,vy) that lies on the line created by the angle from 0,0. + int vx = 0; + int vy = y_start; + float lv = 0; + + // Assume maximum y and calculate the corresponding x value + // for a point at the other end of this line. + if( x_sign * y_sign == 1 ) + { + vx = x_sign * sar * y_center / tan( ( max_angle - angle ) * M_PI / 180.0 ); + } + else + { + vx = x_sign * sar * y_center * tan( ( max_angle - angle ) * M_PI / 180.0 ); + } + + // Calculate the length of the line defined by vx,vy + lv = sqrtf((float)(vx*vx)*sar*sar + (float)vy*vy); + + // Scan through each pixel in the quadrant counting up/down to 0,0. + int dx = x_start + x_sign; + while( dx ) + { + dx -= x_sign; + int dy = y_start + y_sign; + while( dy ) + { + dy -= y_sign; + // Calculate the cross product to determine which side of + // the line this pixel lies on. + int xp = vx * (vy - dy) - vy * (vx - dx); + xp = xp * -1; // Easier to work with positive. Positive number means "behind" the line. + if( xp > 0 ) + { + // This pixel is behind the clock hand and should be filled in. + // Calculate the distance from the pixel to the line to determine + // if it is part of the clock hand. + float distance = (float)xp / lv; + int val = CLOCK_PIXEL_VALUE; + float mix = 1.0; + + if( distance < line_width ) + { + // This pixel makes up the clock hand. + val = LINE_PIXEL_VALUE; + + if( distance < 1.0 ) + { + // Antialias the outside of the clock hand + mix = distance; + } + else if( (float)line_width - distance < 1.0 ) + { + // Antialias the inside of the clock hand + mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); + mix = (float)line_width - distance; + } + } + + mix_pixel( image, profile->width, x_center + dx, y_center - dy, val, mix ); + } + } + } + } + } +} + +static void add_clock_to_frame( mlt_producer producer, mlt_frame frame, mlt_position position ) +{ + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + uint8_t* image = NULL; + mlt_image_format format = mlt_image_rgb24a; + int size = 0; + int width = profile->width; + int height = profile->height; + int line_width = LINE_WIDTH_RATIO * (width > height ? height : width) / 100; + int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25; + int radius = (width > height ? height : width) / 2; + char* direction = mlt_properties_get( producer_properties, "direction" ); + int clock_angle = 0; + + mlt_frame_get_image( frame, &image, &format, &width, &height, 1 ); + + // Calculate the angle for the clock. + if( !strcmp( direction, "down" ) ) + { + int out = mlt_producer_get_out( producer ); + int frames = fps - MLT_POSITION_MOD( (out - position), fps); + clock_angle = lrint( (frames + 1) * 360 / fps ); + } + else + { + int frames = MLT_POSITION_MOD(position, fps); + clock_angle = lrint( (frames + 1) * 360 / fps ); + } + + draw_clock( image, profile, clock_angle, line_width ); + draw_cross( image, profile, line_width ); + draw_ring( image, profile, ( radius * OUTER_RING_RATIO ) / 100, line_width ); + draw_ring( image, profile, ( radius * INNER_RING_RATIO ) / 100, line_width ); + + size = mlt_image_format_size( format, width, height, NULL ); + mlt_frame_set_image( frame, image, size, mlt_pool_release ); +} + + +static void add_text_to_bg( mlt_producer producer, mlt_frame bg_frame, mlt_frame text_frame ) +{ + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_transition transition = mlt_properties_get_data( producer_properties, "_transition", NULL ); + + if( !transition ) + { + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + transition = mlt_factory_transition( profile, "composite", NULL ); + + // Save the transition for future use. + mlt_properties_set_data( producer_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); + + // Configure the transition. + mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); + mlt_properties_set( transition_properties, "geometry", "0%/0%:100%x100%:100" ); + mlt_properties_set( transition_properties, "halign", "center" ); + mlt_properties_set( transition_properties, "valign", "middle" ); + } + + if( transition && bg_frame && text_frame ) + { + // Apply the transition. + mlt_transition_process( transition, bg_frame, text_frame ); + } +} + +static int producer_get_image( mlt_frame frame, uint8_t** image, mlt_image_format* format, int* width, int* height, int writable ) +{ + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_position position = mlt_frame_original_position( frame ); + mlt_producer producer = mlt_properties_get_data( properties, "_producer_count", NULL ); + mlt_frame bg_frame = NULL; + mlt_frame text_frame = NULL; + int error = 1; + int size = 0; + char* background = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "background" ); + + mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); + + bg_frame = get_background_frame( producer ); + if( !strcmp( background, "clock" ) ) + { + add_clock_to_frame( producer, bg_frame, position ); + } + text_frame = get_text_frame( producer, position ); + add_text_to_bg( producer, bg_frame, text_frame ); + + if( bg_frame ) + { + // Get the image from the background frame. + error = mlt_frame_get_image( bg_frame, image, format, width, height, writable ); + size = mlt_image_format_size( *format, *width, *height, NULL ); + // Detach the image from the bg_frame so it is not released. + mlt_frame_set_image( bg_frame, *image, size, NULL ); + // Attach the image to the input frame. + mlt_frame_set_image( frame, *image, size, mlt_pool_release ); + mlt_frame_close( bg_frame ); + } + + if( text_frame ) + { + mlt_frame_close( text_frame ); + } + + mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); + + return error; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + // Generate a frame + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + + if ( *frame != NULL ) + { + // Obtain properties of frame + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); + + // Save the producer to be used later + mlt_properties_set_data( frame_properties, "_producer_count", producer, 0, NULL, NULL ); + + // Update time code on the frame + mlt_frame_set_position( *frame, mlt_producer_frame( producer ) ); + + mlt_properties_set_int( frame_properties, "progressive", 1 ); + mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); + mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); + mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); + + // Configure callbacks + mlt_frame_push_get_image( *frame, producer_get_image ); + mlt_frame_push_audio( *frame, producer_get_audio ); + } + + // Calculate the next time code + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer this ) +{ + this->close = NULL; + mlt_producer_close( this ); + free( this ); +} + +/** Initialize. +*/ + +mlt_producer producer_count_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + // Create a new producer object + mlt_producer producer = mlt_producer_new( profile ); + + // Initialize the producer + if ( producer ) + { + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_properties_set( properties, "direction", "down" ); + mlt_properties_set( properties, "style", "seconds+1" ); + mlt_properties_set( properties, "sound", "none" ); + mlt_properties_set( properties, "background", "clock" ); + + // Callback registration + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; + } + + return producer; +} diff -Nru mlt-0.9.0/src/modules/plus/producer_count.yml mlt-0.9.2/src/modules/plus/producer_count.yml --- mlt-0.9.0/src/modules/plus/producer_count.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plus/producer_count.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,73 @@ +schema_version: 0.1 +type: producer +identifier: count +title: Count +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Audio + - Video +description: > + Generate frames with a counter and synchronized tone. + The counter can go up or down. +parameters: + - identifier: direction + title: Count Direction + description: Whether to count up or down. + type: string + default: down + values: + - up + - down + mutable: yes + widget: combo + - identifier: style + title: Counter Style + description: | + The style of the counter. + * seconds - seconds counting up from or down to 0 + * seconds+1 - seconds counting up from or down to 1 + * frames - frames + * timecode - timecode in the format HH:MM:SS:FF + * clock - clock in the format HH:MM:SS + type: string + default: seconds+1 + values: + - seconds + - seconds+1 + - frames + - timecode + - clock + mutable: yes + widget: combo + - identifier: sound + title: Sound + description: | + The sound to be produced. + * silent - No sound + * 2pop - A 1kHz beep exactly two seconds before the out point + * frame0 - A 1kHz beep at frame 0 of every second + type: string + default: silent + values: + - none + - 2pop + - frame0 + mutable: yes + widget: combo + - identifier: background + title: Background + description: | + The background style. + * none - No background + * clock - Film style clock animation + type: string + default: clock + values: + - none + - clock + mutable: yes + widget: combo diff -Nru mlt-0.9.0/src/modules/plusgpl/cJSON.c mlt-0.9.2/src/modules/plusgpl/cJSON.c --- mlt-0.9.0/src/modules/plusgpl/cJSON.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/cJSON.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,497 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +// cJSON +// JSON parser in C. + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +// Internal constructor. +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +// Delete a cJSON structure. +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +// Parse the input text to generate a number, and populate the result into item. +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + // Could use sscanf for this? + if (*num=='-') sign=-1,num++; // Has sign? + if (*num=='0') num++; // is zero + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); // Number? + if (*num=='.') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} // Fractional part? + if (*num=='e' || *num=='E') // Exponent? + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; // With sign? + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); // Number? + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); // number = +/- number.fraction * 10^+/- exponent + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +// Render the number nicely from the given item into a string. +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); // 2^64+1 can be represented in 21 chars. + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); // This is a nice tradeoff. + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +// Parse the input text into an unescaped cstring, and populate item. +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; + if (*str!='\"') return 0; // not a string! + + while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes. + + out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly. + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && (unsigned char)*ptr>31) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. + sscanf(ptr+1,"%4x",&uc); // get the unicode char. + len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; + + switch (len) { + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len;ptr+=4; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +// Render the cstring provided to an escaped version that can be printed. +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0; + + if (!str) return cJSON_strdup(""); + ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: ptr2--; break; // eviscerate with prejudice. + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +// Invote print_string_ptr (which is useful) on an item. +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +// Predeclare these prototypes. +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +// Utility to jump whitespace and cr/lf +static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;} + +// Parse an object - create a new root, and populate. +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c=cJSON_New_Item(); + if (!c) return 0; /* memory fail */ + + if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} + return c; +} + +// Render a cJSON item/entity/structure to text. +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +// Parser core - when encountering text, process appropriately. +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; // Fail on null. + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + return 0; // failure. +} + +// Render a value to text. +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +// Build an array from input text. +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') return 0; // not an array! + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; // empty array. + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; // memory fail + value=skip(parse_value(child,skip(value))); // skip any spacing, get the value. + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; // memory fail + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; // memory fail + } + + if (*value==']') return value+1; // end of array + return 0; // malformed. +} + +// Render an array to text +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + // How many entries in the array? + while (child) numentries++,child=child->next; + // Allocate an array to hold the values for each + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + // Retrieve all the results: + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + // If we didn't fail, try to malloc the output string + if (!fail) out=cJSON_malloc(len); + // If that fails, we fail. + if (!out) fail=1; + + // Handle failure. + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; // empty array. + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; // fail! + value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; // memory fail + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; // fail! + value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. + if (!value) return 0; + } + + if (*value=='}') return value+1; // end of array + return 0; // malformed. +} + +// Render an object to text. +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + // Count the number of entries. + while (child) numentries++,child=child->next; + // Allocate space for the names and the objects + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + names=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!names) {cJSON_free(entries);return 0;} + memset(entries,0,sizeof(char*)*numentries); + memset(names,0,sizeof(char*)*numentries); + + // Collect all the results into our arrays: + child=item->child;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + // Try to allocate the output string + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + // Handle failure + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +// Utility for array list handling. +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +// Utility for handling references. +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +// Add item to array/object. +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +// Replace array/object items with new ones. +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +// Create basic types: +cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +// Create Arrays: +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} diff -Nru mlt-0.9.0/src/modules/plusgpl/cJSON.h mlt-0.9.2/src/modules/plusgpl/cJSON.h --- mlt-0.9.0/src/modules/plusgpl/cJSON.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/cJSON.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,124 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +// cJSON Types: +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +// The cJSON structure: +typedef struct cJSON { + struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem + struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. + + int type; // The type of the item, as above. + + char *valuestring; // The item's string, if type==cJSON_String + int valueint; // The item's number, if type==cJSON_Number + double valuedouble; // The item's number, if type==cJSON_Number + + char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object. +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +// Supply malloc, realloc and free functions to cJSON +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. +extern cJSON *cJSON_Parse(const char *value); +// Render a cJSON entity to text for transfer/storage. Free the char* when finished. +extern char *cJSON_Print(cJSON *item); +// Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. +extern char *cJSON_PrintUnformatted(cJSON *item); +// Delete a cJSON entity and all subentities. +extern void cJSON_Delete(cJSON *c); + +// Returns the number of items in an array (or object). +extern int cJSON_GetArraySize(cJSON *array); +// Retrieve item number "item" from array "array". Returns NULL if unsuccessful. +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +// Get item "string" from object. Case insensitive. +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +// These calls create a cJSON item of the appropriate type. +extern cJSON *cJSON_CreateNull(); +extern cJSON *cJSON_CreateTrue(); +extern cJSON *cJSON_CreateFalse(); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(); +extern cJSON *cJSON_CreateObject(); + +// These utilities create an Array of count items. +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +// Append item to the specified array/object. +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +// Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +// Remove/Detatch items from Arrays/Objects. +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +// Update array items. +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru mlt-0.9.0/src/modules/plusgpl/consumer_cbrts.c mlt-0.9.2/src/modules/plusgpl/consumer_cbrts.c --- mlt-0.9.0/src/modules/plusgpl/consumer_cbrts.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/consumer_cbrts.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,944 @@ +/* + * consumer_cbrts.c -- output constant bitrate MPEG-2 transport stream + * + * Copyright (C) 2010-2014 Broadcasting Center Europe S.A. http://www.bce.lu + * an RTL Group Company http://www.rtlgroup.com + * Author: Dan Dennedy + * Some ideas and portions come from OpenCaster, Copyright (C) Lorenzo Pallara + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include + +#define TSP_BYTES (188) +#define MAX_PID (8192) +#define SCR_HZ (27000000ULL) +#define NULL_PID (0x1fff) +#define PAT_PID (0) +#define SDT_PID (0x11) +#define PCR_SMOOTHING (12) +#define PCR_PERIOD_MS (20) + +#define PIDOF( packet ) ( ntohs( *( ( uint16_t* )( packet + 1 ) ) ) & 0x1fff ) +#define HASPCR( packet ) ( (packet[3] & 0x20) && (packet[4] != 0) && (packet[5] & 0x10) ) +#define CCOF( packet ) ( packet[3] & 0x0f ) +#define ADAPTOF( packet ) ( ( packet[3] >> 4 ) & 0x03 ) + +typedef struct consumer_cbrts_s *consumer_cbrts; + +struct consumer_cbrts_s +{ + struct mlt_consumer_s parent; + mlt_consumer avformat; + pthread_t thread; + int joined; + int running; + mlt_position last_position; + mlt_event event_registered; + int fd; + uint8_t *leftover_data[TSP_BYTES]; + int leftover_size; + mlt_deque packets; + mlt_deque packets2; + uint64_t previous_pcr; + uint64_t previous_packet_count; + uint64_t packet_count; + int is_stuffing_set; + pthread_t remux_thread; + pthread_mutex_t deque_mutex; + pthread_cond_t deque_cond; + int is_remuxing; + uint8_t pcr_count; + uint16_t pmt_pid; + int is_si_sdt; + int is_si_pat; + int is_si_pmt; + int dropped; + uint8_t continuity_count[MAX_PID]; + uint64_t output_counter; +}; + +typedef struct { + int size; + int period; + int packet_count; + uint16_t pid; + uint8_t data[4096]; +} ts_section; + +static uint8_t null_packet[ TSP_BYTES ]; + +/** Forward references to static functions. +*/ + +static int consumer_start( mlt_consumer parent ); +static int consumer_stop( mlt_consumer parent ); +static int consumer_is_stopped( mlt_consumer parent ); +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); +static void on_data_received( mlt_properties properties, mlt_consumer consumer, uint8_t *buf, int size ); + +mlt_consumer consumer_cbrts_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + consumer_cbrts self = calloc( sizeof( struct consumer_cbrts_s ), 1 ); + if ( self && mlt_consumer_init( &self->parent, self, profile ) == 0 ) + { + // Get the parent consumer object + mlt_consumer parent = &self->parent; + + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); + + // Create child consumers + self->avformat = mlt_factory_consumer( profile, "avformat", NULL ); + parent->close = consumer_close; + parent->start = consumer_start; + parent->stop = consumer_stop; + parent->is_stopped = consumer_is_stopped; + self->joined = 1; + self->packets = mlt_deque_init(); + self->packets2 = mlt_deque_init(); + + // Create the null packet + memset( null_packet, 0xFF, TSP_BYTES ); + null_packet[0] = 0x47; + null_packet[1] = 0x1f; + null_packet[2] = 0xff; + null_packet[3] = 0x10; + + // Create the deque mutex and condition + pthread_mutex_init( &self->deque_mutex, NULL ); + pthread_cond_init( &self->deque_cond, NULL ); + + // Set consumer property defaults + mlt_properties_set_int( properties, "real_time", -1 ); + + return parent; + } + free( self ); + return NULL; +} + +static ts_section* load_section( const char *filename ) +{ + ts_section *section = NULL; + int fd; + + if ( !filename ) + return NULL; + if ( ( fd = open( filename, O_RDONLY ) ) < 0 ) + { + mlt_log_error( NULL, "cbrts consumer failed to load section file %s\n", filename ); + return NULL; + } + + section = malloc( sizeof(*section) ); + memset( section, 0xff, sizeof(*section) ); + section->size = 0; + + if ( read( fd, section->data, 3 ) ) + { + // get the size + uint16_t *p = (uint16_t*) §ion->data[1]; + section->size = p[0]; + section->size = ntohs( section->size ) & 0x0FFF; + + // read the data + if ( section->size <= sizeof(section->data) - 3 ) + { + ssize_t has_read = 0; + while ( has_read < section->size ) + { + ssize_t n = read( fd, section->data + 3 + has_read, section->size ); + if ( n > 0 ) + has_read += n; + else + break; + } + section->size += 3; + } + else + { + mlt_log_error( NULL, "Section too big - skipped.\n" ); + } + } + close( fd ); + + return section; +} + +static void load_sections( consumer_cbrts self, mlt_properties properties ) +{ + int n = mlt_properties_count( properties ); + + // Store the sections with automatic cleanup + // and make it easy to iterate over the data sections. + mlt_properties si_properties = mlt_properties_get_data( properties, "si.properties", NULL ); + if ( !si_properties ) + { + si_properties = mlt_properties_new(); + mlt_properties_set_data( properties, "si.properties", si_properties, 0, (mlt_destructor) mlt_properties_close, NULL ); + } + + while ( n-- ) + { + // Look for si..file=filename + const char *name = mlt_properties_get_name( properties, n ); + if ( strncmp( "si.", name, 3 ) == 0 + && strncmp( ".file", name + strlen( name ) - 5, 5 ) == 0 ) + { + size_t len = strlen( name ); + char *si_name = strdup( name + 3 ); + char si_pid[len + 1]; + + si_name[len - 3 - 5] = 0; + strcpy( si_pid, "si." ); + strcat( si_pid, si_name ); + strcat( si_pid, ".pid" ); + + // Look for si..pid= + if ( mlt_properties_get( properties, si_pid ) ) + { + char *filename = mlt_properties_get_value( properties, n ); + + ts_section *section = load_section( filename ); + if ( section ) + { + // Determine the periodicity of the section, if supplied + char si_time[len + 1]; + + strcpy( si_time, "si." ); + strcat( si_time, si_name ); + strcat( si_time, ".time" ); + + int time = mlt_properties_get_int( properties, si_time ); + if ( time == 0 ) + time = 200; + + // Set flags if we are replacing PAT or SDT + if ( strncasecmp( "pat", si_name, 3) == 0 ) + self->is_si_pat = 1; + else if ( strncasecmp( "pmt", si_name, 3) == 0 ) + self->is_si_pmt = 1; + else if ( strncasecmp( "sdt", si_name, 3) == 0 ) + self->is_si_sdt = 1; + + // Calculate the period and get the PID + uint64_t muxrate = mlt_properties_get_int( properties, "muxrate" ); + section->period = ( muxrate * time ) / ( TSP_BYTES * 8 * 1000 ); + // output one immediately + section->packet_count = section->period - 1; + mlt_log_verbose( NULL, "SI %s time=%d period=%d file=%s\n", si_name, time, section->period, filename ); + section->pid = mlt_properties_get_int( properties, si_pid ); + + mlt_properties_set_data( si_properties, si_name, section, section->size, free, NULL ); + } + } + free( si_name ); + } + } +} + +static void write_section( consumer_cbrts self, ts_section *section ) +{ + uint8_t *packet; + const uint8_t *data_ptr = section->data; + uint8_t *p; + int size = section->size; + int first, len; + + while ( size > 0 ) + { + first = ( section->data == data_ptr ); + p = packet = malloc( TSP_BYTES ); + *p++ = 0x47; + *p = ( section->pid >> 8 ); + if ( first ) + *p |= 0x40; + p++; + *p++ = section->pid; + *p++ = 0x10; // continuity count will be written later + if ( first ) + *p++ = 0; /* 0 offset */ + len = TSP_BYTES - ( p - packet ); + if ( len > size ) + len = size; + memcpy( p, data_ptr, len ); + p += len; + /* add known padding data */ + len = TSP_BYTES - ( p - packet ); + if ( len > 0 ) + memset( p, 0xff, len ); + + mlt_deque_push_back( self->packets2, packet ); + self->packet_count++; + + data_ptr += len; + size -= len; + } +} + +static void write_sections( consumer_cbrts self ) +{ + mlt_properties properties = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( &self->parent ), "si.properties", NULL ); + + if ( properties ) + { + int n = mlt_properties_count( properties ); + while ( n-- ) + { + ts_section *section = mlt_properties_get_data_at( properties, n, NULL ); + if ( ++section->packet_count == section->period ) + { + section->packet_count = 0; + write_section( self, section ); + } + } + } +} + +static uint64_t get_pcr( uint8_t *packet ) +{ + uint64_t pcr = 0; + pcr += (uint64_t) packet[6] << 25; + pcr += packet[7] << 17; + pcr += packet[8] << 9; + pcr += packet[9] << 1; + pcr += packet[10] >> 7; + pcr *= 300; // convert 90KHz to 27MHz + pcr += (packet[10] & 0x01) << 8; + pcr += packet[11]; + return pcr; +} + +static void set_pcr( uint8_t *packet, uint64_t pcr ) +{ + uint64_t pcr_base = pcr / 300; + uint64_t pcr_ext = pcr % 300; + packet[6] = pcr_base >> 25; + packet[7] = pcr_base >> 17; + packet[8] = pcr_base >> 9; + packet[9] = pcr_base >> 1; + packet[10] = (( pcr_base & 1 ) << 7) | 0x7E | (( pcr_ext & 0x100 ) >> 8); + packet[11] = pcr_ext; +} + +static uint64_t update_pcr( consumer_cbrts self, uint64_t muxrate, unsigned packets ) +{ + return self->previous_pcr + packets * TSP_BYTES * 8 * SCR_HZ / muxrate; +} + +static double measure_bitrate( consumer_cbrts self, uint64_t pcr, int drop ) +{ + double muxrate = 0; + + if ( self->is_stuffing_set || self->previous_pcr ) + { + muxrate = ( self->packet_count - self->previous_packet_count - drop ) * TSP_BYTES * 8; + if ( pcr >= self->previous_pcr ) + muxrate /= (double) ( pcr - self->previous_pcr ) / SCR_HZ; + else + muxrate /= ( ( (double) ( 1ULL << 33 ) - 1 ) * 300 - self->previous_pcr + pcr ) / SCR_HZ; + mlt_log_debug( NULL, "measured TS bitrate %.1f bits/sec PCR %"PRIu64"\n", muxrate, pcr ); + } + + return muxrate; +} + +static int writen( int fd, const void *buf, size_t count ) +{ + int result = 0; + int written = 0; + while ( written < count ) + { + if ( ( result = write( fd, buf + written, count - written ) ) < 0 ) + { + mlt_log_error( NULL, "Failed to write: %s\n", strerror( errno ) ); + break; + } + written += result; + } + return result; +} + +static int insert_pcr( consumer_cbrts self, uint16_t pid, uint8_t cc, uint64_t pcr ) +{ + uint8_t packet[ TSP_BYTES ]; + uint8_t *p = packet; + + *p++ = 0x47; + *p++ = pid >> 8; + *p++ = pid; + *p++ = 0x20 | cc; // Adaptation only + // Continuity Count field does not increment (see 13818-1 section 2.4.3.3) + *p++ = TSP_BYTES - 5; // Adaptation Field Length + *p++ = 0x10; // Adaptation flags: PCR present + set_pcr( packet, pcr ); + p += 6; // 6 pcr bytes + memset( p, 0xff, TSP_BYTES - ( p - packet ) ); // stuffing + + return writen( self->fd, packet, TSP_BYTES ); +} + +static int output_cbr( consumer_cbrts self, uint64_t input_rate, uint64_t output_rate, uint64_t *pcr ) +{ + int n = mlt_deque_count( self->packets2 ); + unsigned output_packets = 0; + unsigned packets_since_pcr = 0; + int result = 0; + int dropped = 0; + int warned = 0; + uint16_t pcr_pid = 0; + uint8_t cc = 0xff; + float ms_since_pcr; + float ms_to_end; + uint64_t input_counter = 0; + uint64_t last_input_counter; + + mlt_log_debug( NULL, "%s: n %i output_counter %"PRIu64" input_rate %"PRIu64"\n", __FUNCTION__, n, self->output_counter, input_rate ); + while ( n-- && result >= 0 ) + { + uint8_t *packet = mlt_deque_pop_front( self->packets2 ); + uint16_t pid = PIDOF( packet ); + + // Check for overflow + if ( input_rate > output_rate && !HASPCR( packet ) && + pid != SDT_PID && pid != PAT_PID && pid != self->pmt_pid ) + { + if ( !warned ) + { + mlt_log_warning( MLT_CONSUMER_SERVICE( &self->parent ), "muxrate too low %"PRIu64" > %"PRIu64"\n", input_rate, output_rate ); + warned = 1; + } + + // Skip this packet + free( packet ); + + // Compute new input_rate based on dropped count + input_rate = measure_bitrate( self, *pcr, ++dropped ); + + continue; + } + + if ( HASPCR( packet ) ) + { + pcr_pid = pid; + set_pcr( packet, update_pcr( self, output_rate, output_packets ) ); + packets_since_pcr = 0; + } + + // Rewrite the continuity counter if not only adaptation field + if ( ADAPTOF( packet ) != 2 ) + { + packet[3] = ( packet[3] & 0xf0 ) | self->continuity_count[ pid ]; + self->continuity_count[ pid ] = ( self->continuity_count[ pid ] + 1 ) & 0xf; + } + if ( pcr_pid && pid == pcr_pid ) + cc = CCOF( packet ); + + result = writen( self->fd, packet, TSP_BYTES ); + free( packet ); + if ( result < 0 ) + break; + output_packets++; + packets_since_pcr++; + self->output_counter += TSP_BYTES * 8 * output_rate; + input_counter += TSP_BYTES * 8 * input_rate; + + // See if we need to output a dummy packet with PCR + ms_since_pcr = (float) ( packets_since_pcr + 1 ) * 8 * TSP_BYTES * 1000 / output_rate; + ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; + if ( pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0 ) + { + uint64_t new_pcr = update_pcr( self, output_rate, output_packets ); + if ( ms_since_pcr > 40 ) + mlt_log_warning( NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end ); + if ( ( result = insert_pcr( self, pcr_pid, cc, new_pcr ) ) < 0 ) + break; + packets_since_pcr = 0; + output_packets++; + input_counter += TSP_BYTES * 8 * input_rate; + } + + // Output null packets as needed + while ( input_counter + ( TSP_BYTES * 8 * input_rate ) <= self->output_counter ) + { + // See if we need to output a dummy packet with PCR + ms_since_pcr = (float) ( packets_since_pcr + 1 ) * 8 * TSP_BYTES * 1000 / output_rate; + ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; + + if ( pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0 ) + { + uint64_t new_pcr = update_pcr( self, output_rate, output_packets ); + if ( ms_since_pcr > 40 ) + mlt_log_warning( NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end ); + if ( ( result = insert_pcr( self, pcr_pid, cc, new_pcr ) ) < 0 ) + break; + packets_since_pcr = 0; + } + else + { + // Otherwise output a null packet + if ( ( result = writen( self->fd, null_packet, TSP_BYTES ) ) < 0 ) + break; + packets_since_pcr++; + } + output_packets++; + + // Increment input + last_input_counter = input_counter; + input_counter += TSP_BYTES * 8 * input_rate; + + // Handle wrapping + if ( last_input_counter > input_counter ) + { + last_input_counter -= self->output_counter; + self->output_counter = 0; + input_counter = last_input_counter + TSP_BYTES * 8 * input_rate; + } + } + } + + // Reset counters leaving a residual output count + if ( input_counter < self->output_counter ) + self->output_counter -= input_counter; + else + self->output_counter = 0; + + // Warn if the PCR interval is too large or small + ms_since_pcr = (float) packets_since_pcr * 8 * TSP_BYTES * 1000 / output_rate; + if ( ms_since_pcr > 40 ) + mlt_log_warning( NULL, "exceeded PCR interval %.2f ms\n", ms_since_pcr ); + else if ( ms_since_pcr < PCR_PERIOD_MS / 2.0 ) + mlt_log_debug( NULL, "PCR interval too short %.2f ms\n", ms_since_pcr ); + + // Update the current PCR based on number of packets output + *pcr = update_pcr( self, output_rate, output_packets ); + + return result; +} + +static void get_pmt_pid( consumer_cbrts self, uint8_t *packet ) +{ + // Skip 5 bytes of TSP header + 8 bytes of section header + 2 bytes of service ID + uint16_t *p = ( uint16_t* )( packet + 5 + 8 + 2 ); + self->pmt_pid = ntohs( p[0] ) & 0x1fff; + mlt_log_debug(NULL, "PMT PID 0x%04x\n", self->pmt_pid ); + return; +} + +static void *remux_thread( void *arg ) +{ + consumer_cbrts self = arg; + mlt_service service = MLT_CONSUMER_SERVICE( &self->parent ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); + double output_rate = mlt_properties_get_int( properties, "muxrate" ); + int remux = !mlt_properties_get_int( properties, "noremux" ); + int i; + int result = 0; + + while ( self->is_remuxing ) + { + pthread_mutex_lock( &self->deque_mutex ); + while ( self->is_remuxing && mlt_deque_count( self->packets ) < 10 ) + pthread_cond_wait( &self->deque_cond, &self->deque_mutex ); + pthread_mutex_unlock( &self->deque_mutex ); + + // Dequeue the packets and write them + i = mlt_deque_count( self->packets ); + mlt_log_debug( service, "%s: count %d\n", __FUNCTION__, i ); + while ( self->is_remuxing && i-- && result >= 0 ) + { + if ( remux ) + write_sections( self ); + + pthread_mutex_lock( &self->deque_mutex ); + uint8_t *packet = mlt_deque_pop_front( self->packets ); + pthread_mutex_unlock( &self->deque_mutex ); + uint16_t pid = PIDOF( packet ); + + // Sanity checks + if ( packet[0] != 0x47 ) + { + mlt_log_panic( service, "NOT SYNC BYTE 0x%02x\n", packet[0] ); + exit(1); + } + if ( remux && pid == NULL_PID ) + { + mlt_log_panic( service, "NULL PACKET\n" ); + exit(1); + } + + // Measure the bitrate between consecutive PCRs + if ( HASPCR( packet ) ) + { + if ( self->pcr_count++ % PCR_SMOOTHING == 0 ) + { + uint64_t pcr = get_pcr( packet ); + double input_rate = measure_bitrate( self, pcr, 0 ); + if ( input_rate > 0 ) + { + self->is_stuffing_set = 1; + if ( remux && input_rate > 1.0 ) + { + result = output_cbr( self, input_rate, output_rate, &pcr ); + set_pcr( packet, pcr ); + } + } + self->previous_pcr = pcr; + self->previous_packet_count = self->packet_count; + } + } + if ( remux ) + { + mlt_deque_push_back( self->packets2, packet ); + } + else + { + if ( self->is_stuffing_set ) + result = writen( self->fd, packet, TSP_BYTES ); + free( packet ); + } + self->packet_count++; + } + } + return NULL; +} + +static void start_remux_thread( consumer_cbrts self ) +{ + self->is_remuxing = 1; + int rtprio = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "rtprio" ); + if ( rtprio > 0 ) + { + // Use realtime priority class + struct sched_param priority; + pthread_attr_t thread_attributes; + pthread_attr_init( &thread_attributes ); + priority.sched_priority = rtprio; + pthread_attr_setschedpolicy( &thread_attributes, SCHED_FIFO ); + pthread_attr_setschedparam( &thread_attributes, &priority ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM ); + if ( pthread_create( &self->remux_thread, &thread_attributes, remux_thread, self ) < 0 ) + { + mlt_log_info( MLT_CONSUMER_SERVICE( &self->parent ), + "failed to initialize remux thread with realtime priority\n" ); + pthread_create( &self->remux_thread, &thread_attributes, remux_thread, self ); + } + pthread_attr_destroy( &thread_attributes ); + } + else + { + // Use normal priority class + pthread_create( &self->remux_thread, NULL, remux_thread, self ); + } +} + +static void stop_remux_thread( consumer_cbrts self ) +{ + if ( self->is_remuxing ) + { + self->is_remuxing = 0; + + // Broadcast to the condition in case it's waiting + pthread_mutex_lock( &self->deque_mutex ); + int n = mlt_deque_count( self->packets ); + while ( n-- ) + free( mlt_deque_pop_back( self->packets ) ); + pthread_cond_broadcast( &self->deque_cond ); + pthread_mutex_unlock( &self->deque_mutex ); + + // Join the thread + pthread_join( self->remux_thread, NULL ); + + n = mlt_deque_count( self->packets2 ); + while ( n-- ) + free( mlt_deque_pop_back( self->packets2 ) ); + } +} + +static inline int filter_packet( consumer_cbrts self, uint8_t *packet ) +{ + uint16_t pid = PIDOF( packet ); + + // We are going to keep the existing PMT; replace all other signaling sections. + int result = ( pid == NULL_PID ) + || ( pid == PAT_PID && self->is_si_pat ) + || ( pid == self->pmt_pid && self->is_si_pmt ) + || ( pid == SDT_PID && self->is_si_sdt ); + + // Get the PMT PID from the PAT + if ( pid == PAT_PID && !self->pmt_pid ) + { + get_pmt_pid( self, packet ); + if ( self->is_si_pmt ) + result = 1; + } + + return result; +} + +static void on_data_received( mlt_properties properties, mlt_consumer consumer, uint8_t *buf, int size ) +{ + if ( size > 0 ) + { + consumer_cbrts self = (consumer_cbrts) consumer->child; + mlt_service service = MLT_CONSUMER_SERVICE( consumer ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + int noremux = mlt_properties_get_int( properties, "noremux" ); + + // Sanity check + if ( self->leftover_size == 0 && buf[0] != 0x47 ) + { + mlt_log_verbose(MLT_CONSUMER_SERVICE( consumer ), "NOT SYNC BYTE 0x%02x\n", buf[0] ); + while ( size && buf[0] != 0x47 ) + { + buf++; + size--; + } + if ( size <= 0 ) + exit(1); + } + + // Enqueue the packets + int num_packets = ( self->leftover_size + size ) / TSP_BYTES; + int remaining = ( self->leftover_size + size ) % TSP_BYTES; + uint8_t *packet = NULL; + int i; + +// mlt_log_verbose( service, "%s: packets %d remaining %i\n", __FUNCTION__, num_packets, self->leftover_size ); + + pthread_mutex_lock( &self->deque_mutex ); + if ( self->leftover_size ) + { + packet = malloc( TSP_BYTES ); + memcpy( packet, self->leftover_data, self->leftover_size ); + memcpy( packet + self->leftover_size, buf, TSP_BYTES - self->leftover_size ); + buf += TSP_BYTES - self->leftover_size; + --num_packets; + + // Filter out null packets + if ( noremux || !filter_packet( self, packet ) ) + mlt_deque_push_back( self->packets, packet ); + else + free( packet ); + } + for ( i = 0; i < num_packets; i++, buf += TSP_BYTES ) + { + packet = malloc( TSP_BYTES ); + memcpy( packet, buf, TSP_BYTES ); + + // Filter out null packets + if ( noremux || !filter_packet( self, packet ) ) + mlt_deque_push_back( self->packets, packet ); + else + free( packet ); + } + pthread_cond_broadcast( &self->deque_cond ); + pthread_mutex_unlock( &self->deque_mutex ); + + self->leftover_size = remaining; + memcpy( self->leftover_data, buf, self->leftover_size ); + + if ( !self->is_remuxing ) + start_remux_thread( self ); + mlt_log_debug( service, "%s: %p 0x%x (%d)\n", __FUNCTION__, buf, *buf, size % TSP_BYTES ); + + // Do direct output +// result = writen( self->fd, buf, size ); + } +} + +static int consumer_start( mlt_consumer parent ) +{ + consumer_cbrts self = parent->child; + + if ( !self->running ) + { + mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); + mlt_properties avformat = MLT_CONSUMER_PROPERTIES(self->avformat); + + // Cleanup after a possible abort + consumer_stop( parent ); + + // Pass properties down + mlt_properties_pass( avformat, properties, "" ); + mlt_properties_set_data( avformat, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL ); + mlt_properties_set_data( avformat, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL ); + mlt_properties_set_int( avformat, "put_mode", 1 ); + mlt_properties_set_int( avformat, "real_time", -1 ); + mlt_properties_set_int( avformat, "buffer", 2 ); + mlt_properties_set_int( avformat, "terminate_on_pause", 0 ); + mlt_properties_set_int( avformat, "muxrate", 1 ); + mlt_properties_set_int( avformat, "redirect", 1 ); + mlt_properties_set( avformat, "f", "mpegts" ); + self->dropped = 0; + self->fd = STDOUT_FILENO; + + // Load the DVB PSI/SI sections + load_sections( self, properties ); + + // Start the FFmpeg consumer and our thread + mlt_consumer_start( self->avformat ); + pthread_create( &self->thread, NULL, consumer_thread, self ); + self->running = 1; + self->joined = 0; + } + + return 0; +} + +static int consumer_stop( mlt_consumer parent ) +{ + // Get the actual object + consumer_cbrts self = parent->child; + + if ( !self->joined ) + { + mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); + int app_locked = mlt_properties_get_int( properties, "app_locked" ); + void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL ); + void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL ); + + if ( app_locked && unlock ) unlock( ); + + // Kill the threads and clean up + self->running = 0; +#ifndef WIN32 + if ( self->thread ) +#endif + pthread_join( self->thread, NULL ); + self->joined = 1; + + if ( self->avformat ) + mlt_consumer_stop( self->avformat ); + stop_remux_thread( self ); + if ( self->fd > 1 ) + close( self->fd ); + + if ( app_locked && lock ) lock( ); + } + return 0; +} + +static int consumer_is_stopped( mlt_consumer parent ) +{ + consumer_cbrts self = parent->child; + return !self->running; +} + +static void *consumer_thread( void *arg ) +{ + // Identify the arg + consumer_cbrts self = arg; + + // Get the consumer and producer + mlt_consumer consumer = &self->parent; + + // Get the properties + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + + // internal intialization + mlt_frame frame = NULL; + int last_position = -1; + + // Loop until told not to + while( self->running ) + { + // Get a frame from the attached producer + frame = mlt_consumer_rt_frame( consumer ); + + // Ensure that we have a frame + if ( self->running && frame ) + { + if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" ) != 1 ) + { + mlt_frame_close( frame ); + mlt_log_warning( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++self->dropped ); + continue; + } + + // Get the speed of the frame + double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); + + // Optimisation to reduce latency + if ( speed == 1.0 ) + { + if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) + mlt_consumer_purge( self->avformat ); + last_position = mlt_frame_get_position( frame ); + } + else + { + //mlt_consumer_purge( this->play ); + last_position = -1; + } + + mlt_consumer_put_frame( self->avformat, frame ); + mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); + + // Setup event listener as a callback from consumer avformat + if ( !self->event_registered ) + self->event_registered = mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->avformat ), + consumer, "avformat-write", (mlt_listener) on_data_received ); + } + else + { + if ( frame ) mlt_frame_close( frame ); + mlt_consumer_put_frame( self->avformat, NULL ); + self->running = 0; + } + } + return NULL; +} + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_cbrts self = parent->child; + + // Stop the consumer + mlt_consumer_stop( parent ); + + // Close the child consumers + mlt_consumer_close( self->avformat ); + + // Now clean up the rest + mlt_deque_close( self->packets ); + mlt_deque_close( self->packets2 ); + mlt_consumer_close( parent ); + + // Finally clean up this + free( self ); +} diff -Nru mlt-0.9.0/src/modules/plusgpl/consumer_cbrts.yml mlt-0.9.2/src/modules/plusgpl/consumer_cbrts.yml --- mlt-0.9.0/src/modules/plusgpl/consumer_cbrts.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/consumer_cbrts.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,37 @@ +schema_version: 0.1 +type: consumer +identifier: cbrts +title: CBR MPEG2-TS +version: 1 +copyright: Copyright (C) 2010-2014 Broadcasting Center Europe S.A. http://www.bce.lu +license: GPLv2 +language: en +creator: Dan Dennedy +tags: + - Audio + - Video +description: Constant bit-rate MPEG-2 Transport Stream +notes: | + All properties, except some key operational properties such as real_time and + terminate_on_pause, set on the this consumer are passed onto an encapsulated + avformat consumer - no special prefix required. While some avformat + properties can accept a "k" suffix, this consumer requires "muxrate" but does + not understand the "k" suffix; so, specify the value in bytes per second. + The stream is always output to STDOUT at this time. + + You can rewrite and insert table sections into the transport stream. If you + choose to rewrite the PMT sections, then you need to know how libavformat + sets the PIDs on the elementary streams. Currently, the video stream is 256 + (0x100) and audio streams start at 257, incrementing from there. There are + conventions for property names to pass the .sec files to the consumer. + + The conventions are: + si.
.file= + si.
.pid= + si.
.time= +
is really anything, but typically: pat, sdt, nit, eit, etc. + "pat," "pmt," and "sdt" are special such that when supplied, they cause + libavformat's corresponding sections to be filtered out and replaced with + yours. You should always use PID 16 for NIT, 17 for SDT, and of course, + 0 for PAT; PMT may be anything, but libavformat uses 4095 (0xfff). + The time property indicates the frequency to insert the section - every N milliseconds. diff -Nru mlt-0.9.0/src/modules/plusgpl/factory.c mlt-0.9.2/src/modules/plusgpl/factory.c --- mlt-0.9.0/src/modules/plusgpl/factory.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2008-2014 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +extern mlt_consumer consumer_cbrts_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_lumaliftgaingamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); + +static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) +{ + char file[ PATH_MAX ]; + snprintf( file, PATH_MAX, "%s/plusgpl/%s", mlt_environment( "MLT_DATA" ), (char*) data ); + return mlt_properties_parse_yaml( file ); +} + +MLT_REPOSITORY +{ + MLT_REGISTER( consumer_type, "cbrts", consumer_cbrts_init ); + MLT_REGISTER( filter_type, "BurningTV", filter_burn_init ); + MLT_REGISTER( filter_type, "burningtv", filter_burn_init ); + MLT_REGISTER( filter_type, "lumaliftgaingamma", filter_lumaliftgaingamma_init ); + MLT_REGISTER( filter_type, "rotoscoping", filter_rotoscoping_init ); + MLT_REGISTER( filter_type, "telecide", filter_telecide_init ); + + MLT_REGISTER_METADATA( consumer_type, "cbrts", metadata, "consumer_cbrts.yml" ); + MLT_REGISTER_METADATA( filter_type, "BurningTV", metadata, "filter_burningtv.yml" ); + MLT_REGISTER_METADATA( filter_type, "burningtv", metadata, "filter_burningtv.yml" ); + MLT_REGISTER_METADATA( filter_type, "lumaliftgaingamma", metadata, "filter_lumaliftgaingamma.yml" ); + MLT_REGISTER_METADATA( filter_type, "rotoscoping", metadata, "filter_rotoscoping.yml" ); +} diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_burn.c mlt-0.9.2/src/modules/plusgpl/filter_burn.c --- mlt-0.9.0/src/modules/plusgpl/filter_burn.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_burn.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * filter_burn.c -- burning filter + * Copyright (C) 2007 Stephane Fillod + * + * Filter taken from EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * BurningTV - burns incoming objects. + * Copyright (C) 2001-2002 FUKUCHI Kentaro + * + * Fire routine is taken from Frank Jan Sorensen's demo program. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include +#include +#include +#include +#include "utils.h" + + +#define MaxColor 120 +#define Decay 15 +#define MAGIC_THRESHOLD "50" + +static RGB32 palette[256]; + +static void makePalette(void) +{ + int i, r, g, b; + uint8_t *p = (uint8_t*) palette; + + for(i=0; i> 8)); + i++; + } + i += 2; + } + } + + return error; +} + +/** Filter processing. +*/ + +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + // Push the frame filter + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, filter_get_image ); + + return frame; +} + +/** Constructor for the filter. +*/ + +mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "foreground", "0" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "threshold", MAGIC_THRESHOLD ); + } + if (!palette[128]) + { + makePalette(); + } + return filter; +} + diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_burningtv.yml mlt-0.9.2/src/modules/plusgpl/filter_burningtv.yml --- mlt-0.9.0/src/modules/plusgpl/filter_burningtv.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_burningtv.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,13 @@ +schema_version: 0.1 +type: filter +identifier: burningtv +title: BurningTV +version: 1 +copyright: FUKUCHI Kentaro, Stephane Fillod +creator: FUKUCHI Kentaro, Stephane Fillod +contributor: + - Jan Sorensen +license: GPLv2 +language: en +tags: + - Video diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_lumaliftgaingamma.c mlt-0.9.2/src/modules/plusgpl/filter_lumaliftgaingamma.c --- mlt-0.9.0/src/modules/plusgpl/filter_lumaliftgaingamma.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_lumaliftgaingamma.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,169 @@ +/* + * filter_lumaliftgaingamma.c -- Lift Gain Gamma filter for luma correction + * Copyright (C) 2014 Janne Liljeblad + * Author: Janne Liljeblad + * + * This filter is a port from Gimp and is distributed under a compatible license. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include +#include +#include +#include + +static double clamp( double value, double low, double high) +{ + if (value < low) + value = low; + if (value > high) + value = high; + return value; +} + +static double lut_value( double value, double lift, double gain, double gamma ) +{ + double nvalue; + double power; + + value = clamp( value + lift, 0, 1 ); + + if( gain < 0.0) + value = value * (1.0 + gain); + else + value = value + ((1.0 - value) * gain); + + if(gamma < 0.0) + { + if (value > 0.5) + nvalue = 1.0 - value; + else + nvalue = value; + + if (nvalue < 0.0) + nvalue = 0.0; + + nvalue = 0.5 * pow (nvalue * 2.0 , (double) (1.0 + gamma)); + + if (value > 0.5) + value = 1.0 - nvalue; + else + value = nvalue; + } + else + { + if (value > 0.5) + nvalue = 1.0 - value; + else + nvalue = value; + + if (nvalue < 0.0) + nvalue = 0.0; + + power = (gamma == 1.0) ? 127 : 1.0 / (1.0 - gamma); + nvalue = 0.5 * pow (2.0 * nvalue, power); + + if (value > 0.5) + value = 1.0 - nvalue; + else + value = nvalue; + } + + return value; +} + +static void fill_lgg_lut(int lgg_lut[], double lift, double gain, double gamma) +{ + int i; + double val; + for( i = 0; i < 256; i++ ) + { + val = (double) i / 255.0; + lgg_lut[ i ] = (int) (lut_value( val, lift, gain, gamma ) * 255.0); + } +} + +/** Do image filtering. +*/ +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + // Get the image + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + + *format = mlt_image_rgb24; + int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); + + // Only process if we have no error and a valid colour space + if ( error == 0 ) + { + // Get values and force accepted ranges + double lift = mlt_properties_anim_get_double( properties, "lift", position, length ); + double gain = mlt_properties_anim_get_double( properties, "gain", position, length ); + double gamma = mlt_properties_anim_get_double( properties, "gamma", position, length ); + lift = clamp( lift, -0.5, 0.5 ); + gain = clamp( gain, -0.5, 0.5 ); + gamma = clamp( gamma, -1.0, 1.0 ); + + // Build lut + int lgg_lut[256]; + fill_lgg_lut( lgg_lut, lift, gain, gamma); + + // Filter + int i = *width * *height + 1; + uint8_t *p = *image; + uint8_t *r = *image; + while ( --i ) + { + *p ++ = lgg_lut[ *r ++ ]; + *p ++ = lgg_lut[ *r ++ ]; + *p ++ = lgg_lut[ *r ++ ]; + } + } + + return error; +} + +/** Filter processing. +*/ +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + // Push the frame filter + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, filter_get_image ); + return frame; +} + +/** Constructor for the filter. +*/ +mlt_filter filter_lumaliftgaingamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = filter_process; + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "lift", "0" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gain", "0" ); + mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gamma", "0" ); + } + return filter; +} + diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_lumaliftgaingamma.yml mlt-0.9.2/src/modules/plusgpl/filter_lumaliftgaingamma.yml --- mlt-0.9.0/src/modules/plusgpl/filter_lumaliftgaingamma.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_lumaliftgaingamma.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,45 @@ +schema_version: 0.1 +type: filter +identifier: lumaliftgaingamma +title: LumaLiftGainGamma +version: 1 +copyright: Janne Liljeblad +creator: Janne Liljeblad +license: GPL +language: en +tags: + - Video +description: > + Filter can be used to apply lift, gain and gamma correction to + luma values of image. + +parameters: + - identifier: lift + title: Lift + type: float + minimum: -0.5 + maximum: 0.5 + default: 0 + description: > + Adds a value computed using parameter value + to color channel values. + + - identifier: gain + title: Gain + type: float + minimum: -0.5 + maximum: 0.5 + default: 0 + description: > + Multiplies color channel values by value + computed using parameter value. + + - identifier: gamma + title: Gamma + type: float + minimum: -1.0 + maximum: 1.0 + default: 0 + description: > + Applies a gamma correction to all + color channel values. diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_rotoscoping.c mlt-0.9.2/src/modules/plusgpl/filter_rotoscoping.c --- mlt-0.9.0/src/modules/plusgpl/filter_rotoscoping.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_rotoscoping.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,630 @@ +/* + * rotoscoping.c -- keyframable vector based rotoscoping + * Copyright (C) 2011 Till Theato + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "cJSON.h" + +#include +#include +#include +#include + +#define MAX( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) ) +#define MIN( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) +#define SQR( x ) ( x ) * ( x ) + +/** x, y tuple with double precision */ +typedef struct PointF +{ + double x; + double y; +} PointF; + +typedef struct BPointF +{ + struct PointF h1; + struct PointF p; + struct PointF h2; +} BPointF; + +enum MODES { MODE_RGB, MODE_ALPHA, MODE_LUMA }; +const char *MODESTR[3] = { "rgb", "alpha", "luma" }; + +enum ALPHAOPERATIONS { ALPHA_CLEAR, ALPHA_MAX, ALPHA_MIN, ALPHA_ADD, ALPHA_SUB }; +const char *ALPHAOPERATIONSTR[5] = { "clear", "max", "min", "add", "sub" }; + + +/** Returns the index of \param string in \param stringList. + * Useful for assigning string parameters to enums. */ +static int stringValue( const char *string, const char **stringList, int max ) +{ + int i; + for ( i = 0; i < max; i++ ) + if ( strcmp( stringList[i], string ) == 0 ) + return i; + return 0; +} + +/** Sets "spline_is_dirty" to 1 if property "spline" was changed. + * We then know when to parse the json stored in "spline" */ +static void rotoPropertyChanged( mlt_service owner, mlt_filter this, char *name ) +{ + if ( !strcmp( name, "spline" ) ) + mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_spline_is_dirty", 1 ); +} + +/** Linear interp */ +static inline void lerp( const PointF *a, const PointF *b, PointF *result, double t ) +{ + result->x = a->x + ( b->x - a->x ) * t; + result->y = a->y + ( b->y - a->y ) * t; +} + +/** Linear interp. with t = 0.5 + * Speed gain? */ +static inline void lerpHalf( const PointF *a, const PointF *b, PointF *result ) +{ + result->x = ( a->x + b->x ) * .5; + result->y = ( a->y + b->y ) * .5; +} + +/** Helper for using qsort with an array of integers. */ +int ncompare( const void *a, const void *b ) +{ + return *(const int*)a - *(const int*)b; +} + +/** Turns a json array with two children into a point (x, y tuple). */ +static void jsonGetPoint( cJSON *json, PointF *point ) +{ + if ( cJSON_GetArraySize( json ) == 2 ) + { + point->x = json->child->valuedouble; + point->y = json->child->next->valuedouble; + } +} + +/** + * Turns the array of json elements into an array of Bézier points. + * \param array cJSON array. values have to be Bézier points: handle 1, point , handl2 + * ( [ [ [h1x, h1y], [px, py], [h2x, h2y] ], ... ] ) + * \param points pointer to array of points. Will be allocated and filled with the points in \param array + * \return number of points + */ +static int json2BCurves( cJSON *array, BPointF **points ) +{ + int count = cJSON_GetArraySize( array ); + cJSON *child = array->child; + *points = mlt_pool_alloc( count * sizeof( BPointF ) ); + + int i = 0; + do + { + if ( child && cJSON_GetArraySize( child ) == 3 ) + { + jsonGetPoint( child->child , &(*points)[i].h1 ); + jsonGetPoint( child->child->next, &(*points)[i].p ); + jsonGetPoint( child->child->next->next, &(*points)[i].h2 ); + i++; + } + } while ( child && ( child = child->next ) ); + + if ( i < count ) + *points = mlt_pool_realloc( *points, i * sizeof( BPointF ) ); + + return i; +} + +/** Blurs \param src horizontally. \See funtion blur. */ +static void blurHorizontal( uint8_t *src, uint8_t *dst, int width, int height, int radius) +{ + int x, y, kx, yOff, total, amount, amountInit; + amountInit = radius * 2 + 1; + for (y = 0; y < height; ++y) + { + total = 0; + yOff = y * width; + // Process entire window for first pixel + int size = MIN(radius + 1, width); + for ( kx = 0; kx < size; ++kx ) + total += src[yOff + kx]; + dst[yOff] = total / ( radius + 1 ); + // Subsequent pixels just update window total + for ( x = 1; x < width; ++x ) + { + amount = amountInit; + // Subtract pixel leaving window + if ( x - radius - 1 >= 0 ) + total -= src[yOff + x - radius - 1]; + else + amount -= radius - x; + // Add pixel entering window + if ( x + radius < width ) + total += src[yOff + x + radius]; + else + amount -= radius - width + x; + dst[yOff + x] = total / amount; + } + } +} + +/** Blurs \param src vertically. \See funtion blur. */ +static void blurVertical( uint8_t *src, uint8_t *dst, int width, int height, int radius) +{ + int x, y, ky, total, amount, amountInit; + amountInit = radius * 2 + 1; + for (x = 0; x < width; ++x) + { + total = 0; + int size = MIN(radius + 1, height); + for ( ky = 0; ky < size; ++ky ) + total += src[x + ky * width]; + dst[x] = total / ( radius + 1 ); + for ( y = 1; y < height; ++y ) + { + amount = amountInit; + if ( y - radius - 1 >= 0 ) + total -= src[( y - radius - 1 ) * width + x]; + else + amount -= radius - y; + if ( y + radius < height ) + total += src[( y + radius ) * width + x]; + else + amount -= radius - height + y; + dst[y * width + x] = total / amount; + } + } +} + +/** + * Blurs the \param map using a simple "average" blur. + * \param map Will be blured; 1bpp + * \param width x dimension of channel stored in \param map + * \param height y dimension of channel stored in \param map + * \param radius blur radius + * \param passes blur passes + */ +static void blur( uint8_t *map, int width, int height, int radius, int passes ) +{ + uint8_t *src = mlt_pool_alloc( width * height ); + uint8_t *tmp = mlt_pool_alloc( width * height ); + + int i; + for ( i = 0; i < passes; ++i ) + { + memcpy( src, map, width * height ); + blurHorizontal( src, tmp, width, height, radius ); + blurVertical( tmp, map, width, height, radius ); + } + + mlt_pool_release(src); + mlt_pool_release(tmp); +} + +/** + * Determines which points are located in the polygon and sets their value in \param map to \param value + * \param vertices points defining the polygon + * \param count number of vertices + * \param with x range + * \param height y range + * \param value value identifying points in the polygon + * \param map array of integers of the dimension width * height. + * The map entries belonging to the points in the polygon will be set to \param set * 255 the others to !set * 255. + */ +static void fillMap( PointF *vertices, int count, int width, int height, int invert, uint8_t *map ) +{ + int nodes, nodeX[1024], pixelY, i, j, value; + + value = !invert * 255; + memset( map, invert * 255, width * height ); + + // Loop through the rows of the image + for ( pixelY = 0; pixelY < height; pixelY++ ) + { + /* + * Build a list of nodes. + * nodes are located at the borders of the polygon + * and therefore indicate a move from in to out or vice versa + */ + nodes = 0; + for ( i = 0, j = count - 1; i < count; j = i++ ) + if ( (vertices[i].y > (double)pixelY) != (vertices[j].y > (double)pixelY) ) + nodeX[nodes++] = (int)(vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x) ); + + qsort( nodeX, nodes, sizeof( int ), ncompare ); + + // Set map values for points between the node pairs to 1 + for ( i = 0; i < nodes; i += 2 ) + { + if ( nodeX[i] >= width ) + break; + + if ( nodeX[i+1] > 0 ) + { + nodeX[i] = MAX( 0, nodeX[i] ); + nodeX[i+1] = MIN( nodeX[i+1], width ); + memset( map + width * pixelY + nodeX[i], value, nodeX[i+1] - nodeX[i] ); + } + } + } +} + +/** Determines the point in the middle of the Bézier curve (t = 0.5) defined by \param p1 and \param p2 + * using De Casteljau's algorithm. + */ +static void deCasteljau( BPointF *p1, BPointF *p2, BPointF *mid ) +{ + struct PointF ab, bc, cd; + + lerpHalf( &(p1->p), &(p1->h2), &ab ); + lerpHalf( &(p1->h2), &(p2->h1), &bc ); + lerpHalf( &(p2->h1), &(p2->p), &cd ); + lerpHalf( &ab, &bc, &(mid->h1) ); // mid->h1 = abbc + lerpHalf( &bc, &cd, &(mid->h2) ); // mid->h2 = bccd + lerpHalf( &(mid->h1), &(mid->h2), &(mid->p) ); + + p1->h2 = ab; + p2->h1 = cd; +} + +/** + * Calculates points for the cubic Bézier curve defined by \param p1 and \param p2. + * Points are calculated until the squared distanced between neighbour points is smaller than 2. + * \param points Pointer to list of points. Will be allocted and filled with calculated points. + * \param count Number of calculated points in \param points + * \param size Allocated size of \param points (in elements not in bytes) + */ +static void curvePoints( BPointF p1, BPointF p2, PointF **points, int *count, int *size ) +{ + double errorSqr = SQR( p1.p.x - p2.p.x ) + SQR( p1.p.y - p2.p.y ); + + if ( *size + 1 >= *count ) + { + *size += (int)sqrt( errorSqr / 2 ); + *points = mlt_pool_realloc( *points, *size * sizeof ( struct PointF ) ); + } + + (*points)[(*count)++] = p1.p; + + if ( errorSqr <= 2 ) + return; + + BPointF mid; + deCasteljau( &p1, &p2, &mid ); + + curvePoints( p1, mid, points, count, size ); + + curvePoints( mid, p2, points, count, size ); + + (*points)[*(count)++] = p2.p; +} + +/** Do it :-). +*/ +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_properties unique = mlt_frame_pop_service( frame ); + + int mode = mlt_properties_get_int( unique, "mode" ); + + // Get the image + if ( mode == MODE_RGB ) + *format = mlt_image_rgb24; + int error = mlt_frame_get_image( frame, image, format, width, height, writable ); + + // Only process if we have no error and a valid colour space + if ( !error ) + { + BPointF *bpoints; + struct PointF *points; + int bcount, length, count, size, i, j; + bpoints = mlt_properties_get_data( unique, "points", &length ); + bcount = length / sizeof( BPointF ); + + for ( i = 0; i < bcount; i++ ) + { + // map to image dimensions + bpoints[i].h1.x *= *width; + bpoints[i].p.x *= *width; + bpoints[i].h2.x *= *width; + bpoints[i].h1.y *= *height; + bpoints[i].p.y *= *height; + bpoints[i].h2.y *= *height; + } + + count = 0; + size = 1; + points = mlt_pool_alloc( size * sizeof( struct PointF ) ); + for ( i = 0; i < bcount; i++ ) + { + j = (i + 1) % bcount; + curvePoints( bpoints[i], bpoints[j], &points, &count, &size ); + } + + if ( count ) + { + length = *width * *height; + uint8_t *map = mlt_pool_alloc( length ); + int invert = mlt_properties_get_int( unique, "invert" ); + fillMap( points, count, *width, *height, invert, map ); + + int feather = mlt_properties_get_int( unique, "feather" ); + if ( feather && mode != MODE_RGB ) + blur( map, *width, *height, feather, mlt_properties_get_int( unique, "feather_passes" ) ); + + int bpp; + size = mlt_image_format_size( *format, *width, *height, &bpp ); + uint8_t *p = *image; + uint8_t *q = *image + size; + + i = 0; + uint8_t *alpha; + + switch ( mode ) + { + case MODE_RGB: + // *format == mlt_image_rgb24 + while ( p != q ) + { + if ( !map[i++] ) + p[0] = p[1] = p[2] = 0; + p += 3; + } + break; + case MODE_LUMA: + switch ( *format ) + { + case mlt_image_rgb24: + case mlt_image_rgb24a: + case mlt_image_opengl: + while ( p != q ) + { + p[0] = p[1] = p[2] = map[i++]; + p += bpp; + } + break; + case mlt_image_yuv422: + while ( p != q ) + { + p[0] = map[i++]; + p[1] = 128; + p += 2; + } + break; + case mlt_image_yuv420p: + memcpy( p, map, length ); + memset( p + length, 128, length / 2 ); + break; + default: + break; + } + break; + case MODE_ALPHA: + switch ( *format ) + { + case mlt_image_rgb24a: + case mlt_image_opengl: + switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) + { + case ALPHA_CLEAR: + while ( p != q ) + { + p[3] = map[i++]; + p += 4; + } + break; + case ALPHA_MAX: + while ( p != q ) + { + p[3] = MAX( p[3], map[i] ); + p += 4; + i++; + } + break; + case ALPHA_MIN: + while ( p != q ) + { + p[3] = MIN( p[3], map[i] ); + p += 4; + i++; + } + break; + case ALPHA_ADD: + while ( p != q ) + { + p[3] = MIN( p[3] + map[i], 255 ); + p += 4; + i++; + } + break; + case ALPHA_SUB: + while ( p != q ) + { + p[3] = MAX( p[3] - map[i], 0 ); + p += 4; + i++; + } + break; + } + break; + default: + alpha = mlt_frame_get_alpha_mask( frame ); + switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) + { + case ALPHA_CLEAR: + memcpy( alpha, map, length ); + break; + case ALPHA_MAX: + for ( ; i < length; i++, alpha++ ) + *alpha = MAX( map[i], *alpha ); + break; + case ALPHA_MIN: + for ( ; i < length; i++, alpha++ ) + *alpha = MIN( map[i], *alpha ); + break; + case ALPHA_ADD: + for ( ; i < length; i++, alpha++ ) + *alpha = MIN( *alpha + map[i], 255 ); + break; + case ALPHA_SUB: + for ( ; i < length; i++, alpha++ ) + *alpha = MAX( *alpha - map[i], 0 ); + break; + } + break; + } + break; + } + + mlt_pool_release( map ); + } + + mlt_pool_release( points ); + } + + return error; +} + +/** Filter processing. +*/ +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + int splineIsDirty = mlt_properties_get_int( properties, "_spline_is_dirty" ); + char *modeStr = mlt_properties_get( properties, "mode" ); + cJSON *root = mlt_properties_get_data( properties, "_spline_parsed", NULL ); + + if ( splineIsDirty || root == NULL ) + { + // we need to (re-)parse + char *spline = mlt_properties_get( properties, "spline" ); + root = cJSON_Parse( spline ); + mlt_properties_set_data( properties, "_spline_parsed", root, 0, (mlt_destructor)cJSON_Delete, NULL ); + mlt_properties_set_int( properties, "_spline_is_dirty", 0 ); + } + + if ( root == NULL ) + return frame; + + BPointF *points; + int count, i; + + if ( root->type == cJSON_Array ) + { + /* + * constant + */ + count = json2BCurves( root, &points ); + } + else if ( root->type == cJSON_Object ) + { + /* + * keyframes + */ + + mlt_position time, pos1, pos2; + time = mlt_frame_get_position( frame ); + + cJSON *keyframe = root->child; + cJSON *keyframeOld = keyframe; + + if ( !keyframe ) + return frame; + + while ( atoi( keyframe->string ) < time && keyframe->next ) + { + keyframeOld = keyframe; + keyframe = keyframe->next; + } + + pos1 = atoi( keyframeOld->string ); + pos2 = atoi( keyframe->string ); + + if ( pos1 >= pos2 || time >= pos2 ) + { + // keyframes in wrong order or before first / after last keyframe + count = json2BCurves( keyframe, &points ); + } + else + { + /* + * pos1 < time < pos2 + */ + + BPointF *p1, *p2; + int c1 = json2BCurves( keyframeOld, &p1 ); + int c2 = json2BCurves( keyframe, &p2 ); + + // range 0-1 + double position = ( time - pos1 ) / (double)( pos2 - pos1 + 1 ); + + count = MIN( c1, c2 ); // additional points are ignored + points = mlt_pool_alloc( count * sizeof( BPointF ) ); + for ( i = 0; i < count; i++ ) + { + lerp( &(p1[i].h1), &(p2[i].h1), &(points[i].h1), position ); + lerp( &(p1[i].p), &(p2[i].p), &(points[i].p), position ); + lerp( &(p1[i].h2), &(p2[i].h2), &(points[i].h2), position ); + } + + mlt_pool_release( p1 ); + mlt_pool_release( p2 ); + } + } + else + { + return frame; + } + + mlt_properties unique = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); + mlt_properties_set_data( unique, "points", points, count * sizeof( BPointF ), (mlt_destructor)mlt_pool_release, NULL ); + mlt_properties_set_int( unique, "mode", stringValue( modeStr, MODESTR, 3 ) ); + mlt_properties_set_int( unique, "alpha_operation", stringValue( mlt_properties_get( properties, "alpha_operation" ), ALPHAOPERATIONSTR, 5 ) ); + mlt_properties_set_int( unique, "invert", mlt_properties_get_int( properties, "invert" ) ); + mlt_properties_set_int( unique, "feather", mlt_properties_get_int( properties, "feather" ) ); + mlt_properties_set_int( unique, "feather_passes", mlt_properties_get_int( properties, "feather_passes" ) ); + mlt_frame_push_service( frame, unique ); + mlt_frame_push_get_image( frame, filter_get_image ); + + return frame; +} + +/** Constructor for the filter. +*/ +mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + if ( filter ) + { + filter->process = filter_process; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set( properties, "mode", "alpha" ); + mlt_properties_set( properties, "alpha_operation", "clear" ); + mlt_properties_set_int( properties, "invert", 0 ); + mlt_properties_set_int( properties, "feather", 0 ); + mlt_properties_set_int( properties, "feather_passes", 1 ); + if ( arg ) + mlt_properties_set( properties, "spline", arg ); + + mlt_events_listen( properties, filter, "property-changed", (mlt_listener)rotoPropertyChanged ); + } + return filter; +} diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_rotoscoping.yml mlt-0.9.2/src/modules/plusgpl/filter_rotoscoping.yml --- mlt-0.9.0/src/modules/plusgpl/filter_rotoscoping.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_rotoscoping.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,102 @@ +schema_version: 0.1 +type: filter +identifier: rotoscoping +title: Rotoscoping +copyright: Copyright (C) 2011 Till Theato +version: 0.3 +license: GPL +language: en +url: none +creator: Till Theato +tags: + - Video +description: Keyframable vector based rotoscoping + +bugs: + - in some cases top most row in polygon is assigned to outside + +parameters: + - identifier: mode + title: Mode + type: string + description: How to visualize the area described by the spline + readonly: no + required: no + default: alpha + mutable: yes + widget: dropdown + values: + - alpha + - luma + - rgb + + - identifier: alpha_operation + title: Alpha Operation + type: string + description: | + How to proceed with the current alpha mask (only if mode = alpha). + clear = existing alpha mask is overwritten + max = maximum: existing alpha mask, mask generated by this filter + min = minimum: existing alpha mask, mask generated by this filter + add = existing alpha mask + generated mask + sub = existing alpha mask - generated mask + readonly: no + required: no + default: clear + mutable: yes + widget: dropdown + values: + - clear + - max + - min + - add + - sub + + - identifier: invert + title: Invert + type: integer + description: use area inside of spline (0) or the outside (1) + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + widget: checkbox + + - identifier: feather + title: Feather + type: integer + description: amount of feathering (radius of "average" blur applied on mask) + readonly: no + required: no + minimum: 0 + maximum: 1000 # no real limit + default: 0 + mutable: yes + widget: spinner + + - identifier: feather_passes + title: Feathering passes + type: integer + description: number of blur (feathering) passes + readonly: no + required: no + minimum: 1 + maximum: 1000 # no real limit + default: 1 + mutable: yes + widget: spinner + + - identifier: spline + title: Spline + type: string + description: > + The spline, a list of cubic Bézier curves, is described using JSON. + The most basic parts are the coordinate tuples: [x, y]; x,y will be mapped from the range 0-1 to the image dimensions. + Next layer are the Bézier points: [handle 1, point, handle 2] with handle 1, point, handle 2 being coordinate tuples + The spline is a list of Bézier points. + Optionally keyframes can be defined as a object of frame values, relative to the filter's in point, assigned to splines. + readonly: no + required: yes + mutable: yes diff -Nru mlt-0.9.0/src/modules/plusgpl/filter_telecide.c mlt-0.9.2/src/modules/plusgpl/filter_telecide.c --- mlt-0.9.0/src/modules/plusgpl/filter_telecide.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/filter_telecide.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,1061 @@ +/* + * filter_telecide.c -- Donald Graft's Inverse Telecine Filter + * Copyright (C) 2003 Donald A. Graft + * Copyright (C) 2008 Dan Dennedy + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#define MAX_CYCLE 6 +#define BLKSIZE 24 +#define BLKSIZE_TIMES2 (2 * BLKSIZE) +#define GUIDE_32 1 +#define GUIDE_22 2 +#define GUIDE_32322 3 +#define AHEAD 0 +#define BEHIND 1 +#define POST_METRICS 1 +#define POST_FULL 2 +#define POST_FULL_MAP 3 +#define POST_FULL_NOMATCH 4 +#define POST_FULL_NOMATCH_MAP 5 +#define CACHE_SIZE 100000 +#define P 0 +#define C 1 +#define N 2 +#define PBLOCK 3 +#define CBLOCK 4 +#define NO_BACK 0 +#define BACK_ON_COMBED 1 +#define ALWAYS_BACK 2 + +struct CACHE_ENTRY +{ + unsigned int frame; + unsigned int metrics[5]; + unsigned int chosen; +}; + +struct PREDICTION +{ + unsigned int metric; + unsigned int phase; + unsigned int predicted; + unsigned int predicted_metric; +}; + +struct context_s { + int is_configured; + mlt_properties image_cache; + int out; + + int tff, chroma, blend, hints, show, debug; + float dthresh, gthresh, vthresh, vthresh_saved, bthresh; + int y0, y1, nt, guide, post, back, back_saved; + int pitch, dpitch, pitchover2, pitchtimes4; + int w, h, wover2, hover2, hplus1over2, hminus2; + int xblocks, yblocks; +#ifdef WINDOWED_MATCH + unsigned int *matchc, *matchp, highest_matchc, highest_matchp; +#endif + unsigned int *sumc, *sump, highest_sumc, highest_sump; + int vmetric; + unsigned int *overrides, *overrides_p; + int film, override, inpattern, found; + int force; + + // Used by field matching. + unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp; + unsigned char *dstp, *finalp; + int chosen; + unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric; + unsigned int np, nc, npblock, ncblock, nframe; + float mismatch; + int pframe, x, y; + unsigned char *crp, *prp; + unsigned char *crpU, *prpU; + unsigned char *crpV, *prpV; + int hard; + char status[80]; + + // Metrics cache. + struct CACHE_ENTRY *cache; + + // Pattern guidance data. + int cycle; + struct PREDICTION pred[MAX_CYCLE+1]; +}; +typedef struct context_s *context; + + +static inline +void BitBlt(uint8_t* dstp, int dst_pitch, const uint8_t* srcp, + int src_pitch, int row_size, int height) +{ + uint32_t y; + for(y=0;ychosen == P) use = 'p'; + else if (cx->chosen == C) use = 'c'; + else use = 'n'; + snprintf(buf, sizeof(buf), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); + if ( cx->post ) + snprintf(buf, sizeof(buf), "%sTelecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", buf, frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); + if ( cx->guide ) + snprintf(buf, sizeof(buf), "%spattern mismatch=%0.2f%%\n", buf, cx->mismatch); + snprintf(buf, sizeof(buf), "%sTelecide: frame %d: [%s %c]%s %s\n", buf, frame, cx->found ? "forcing" : "using", use, + cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", + cx->guide ? cx->status : ""); + mlt_properties_set( properties, "meta.attr.telecide.markup", buf ); +} + +static void Debug(context cx, int frame) +{ + char use; + + if (cx->chosen == P) use = 'p'; + else if (cx->chosen == C) use = 'c'; + else use = 'n'; + fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np); + if ( cx->post ) + fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric); + if ( cx->guide ) + fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch); + fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use, + cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "", + cx->guide ? cx->status : ""); +} + +static void WriteHints(int film, int inpattern, mlt_properties frame_properties) +{ + mlt_properties_set_int( frame_properties, "telecide.progressive", film); + mlt_properties_set_int( frame_properties, "telecide.in_pattern", inpattern); +} + +static void PutChosen(context cx, int frame, unsigned int chosen) +{ + int f = frame % CACHE_SIZE; + if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame) + return; + cx->cache[f].chosen = chosen; +} + +static void CacheInsert(context cx, int frame, unsigned int p, unsigned int pblock, + unsigned int c, unsigned int cblock) +{ + int f = frame % CACHE_SIZE; + if (frame < 0 || frame > cx->out) + fprintf( stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame); + cx->cache[f].frame = frame; + cx->cache[f].metrics[P] = p; + if (f) cx->cache[f-1].metrics[N] = p; + cx->cache[f].metrics[C] = c; + cx->cache[f].metrics[PBLOCK] = pblock; + cx->cache[f].metrics[CBLOCK] = cblock; + cx->cache[f].chosen = 0xff; +} + +static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock, + unsigned int *c, unsigned int *cblock) +{ + int f; + + f = frame % CACHE_SIZE; + if (frame < 0 || frame > cx->out) + fprintf( stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame); + if (cx->cache[f].frame != frame) + { + return 0; + } + *p = cx->cache[f].metrics[P]; + *c = cx->cache[f].metrics[C]; + *pblock = cx->cache[f].metrics[PBLOCK]; + *cblock = cx->cache[f].metrics[CBLOCK]; + return 1; +} + +static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric) +{ + // Look for pattern in the actual delivered matches of the previous cycle of frames. + // If a pattern is found, use that to predict the current match. + if ( cx->guide == GUIDE_22 ) + { + if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff || + cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen == 0xff) + return 0; + switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 4) + + (cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen)) + { + case 0x11: + *predicted = C; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; + break; + case 0x22: + *predicted = N; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; + break; + default: return 0; + } + } + else if ( cx->guide == GUIDE_32 ) + { + if (cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen == 0xff || + cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen == 0xff) + return 0; + + switch ((cx->cache[(frame-cx->cycle)%CACHE_SIZE ].chosen << 16) + + (cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen << 12) + + (cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen << 8) + + (cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen << 4) + + (cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen)) + { + case 0x11122: + case 0x11221: + case 0x12211: + case 0x12221: + case 0x21122: + case 0x11222: + *predicted = C; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; + break; + case 0x22111: + case 0x21112: + case 0x22112: + case 0x22211: + *predicted = N; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; + break; + default: return 0; + } + } + else if ( cx->guide == GUIDE_32322 ) + { + if (cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen == 0xff || + cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen == 0xff || + cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen == 0xff) + return 0; + + switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE ].chosen << 20) + + (cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen << 16) + + (cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen << 12) + + (cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen << 8) + + (cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen << 4) + + (cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen)) + { + case 0x111122: + case 0x111221: + case 0x112211: + case 0x122111: + case 0x111222: + case 0x112221: + case 0x122211: + case 0x222111: + *predicted = C; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; + break; + case 0x221111: + case 0x211112: + + case 0x221112: + case 0x211122: + *predicted = N; + *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; + break; + default: return 0; + } + } +#ifdef DEBUG_PATTERN_GUIDANCE + fprintf( stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted); +#endif + return 1; +} + +static struct PREDICTION *PredictSoftYUY2(context cx, int frame ) +{ + // Use heuristics to look forward for a match. + int i, j, y, c, n, phase; + unsigned int metric; + + cx->pred[0].metric = 0xffffffff; + if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred; + + // Look at the next cycle of frames. + for (y = frame + 1; y <= frame + cx->cycle; y++) + { + // Look for a frame where the current and next match values are + // very close. Those are candidates to predict the phase, because + // that condition should occur only once per cycle. Store the candidate + // phases and predictions in a list sorted by goodness. The list will + // be used by the caller to try the phases in order. + c = cx->cache[y%CACHE_SIZE].metrics[C]; + n = cx->cache[y%CACHE_SIZE].metrics[N]; + if (c == 0) c = 1; + metric = (100 * abs (c - n)) / c; + phase = y % cx->cycle; + if (metric < 5) + { + // Place the new candidate phase in sorted order in the list. + // Find the insertion point. + i = 0; + while (metric > cx->pred[i].metric) i++; + // Find the end-of-list marker. + j = 0; + while (cx->pred[j].metric != 0xffffffff) j++; + // Shift all items below the insertion point down by one to make + // room for the insertion. + j++; + for (; j > i; j--) + { + cx->pred[j].metric = cx->pred[j-1].metric; + cx->pred[j].phase = cx->pred[j-1].phase; + cx->pred[j].predicted = cx->pred[j-1].predicted; + cx->pred[j].predicted_metric = cx->pred[j-1].predicted_metric; + } + // Insert the new candidate data. + cx->pred[j].metric = metric; + cx->pred[j].phase = phase; + if ( cx->guide == GUIDE_32 ) + { + switch ((frame % cx->cycle) - phase) + { + case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + } + } + else if ( cx->guide == GUIDE_32322 ) + { + switch ((frame % cx->cycle) - phase) + { + case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case 0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; + case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; + } + } + } +#ifdef DEBUG_PATTERN_GUIDANCE + fprintf( stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase); +#endif + } + return cx->pred; +} + +static +void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV, + unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV) +{ + int x, y, p, c, tmp1, tmp2, skip; + int vc; + unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2; + unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4; + unsigned char *a0, *a2, *b0, *b2, *b4; + unsigned int diff, index; +# define T 4 + + /* Clear the block sums. */ + for (y = 0; y < cx->yblocks; y++) + { + for (x = 0; x < cx->xblocks; x++) + { +#ifdef WINDOWED_MATCH + matchp[y*xblocks+x] = 0; + matchc[y*xblocks+x] = 0; +#endif + cx->sump[y * cx->xblocks + x] = 0; + cx->sumc[y * cx->xblocks + x] = 0; + } + } + + /* Find the best field match. Subsample the frames for speed. */ + currbot0 = fcrp + cx->pitch; + currbot2 = fcrp + 3 * cx->pitch; + currtop0 = fcrp; + currtop2 = fcrp + 2 * cx->pitch; + currtop4 = fcrp + 4 * cx->pitch; + prevbot0 = fprp + cx->pitch; + prevbot2 = fprp + 3 * cx->pitch; + prevtop0 = fprp; + prevtop2 = fprp + 2 * cx->pitch; + prevtop4 = fprp + 4 * cx->pitch; + if ( cx->tff ) + { + a0 = prevbot0; + a2 = prevbot2; + b0 = currtop0; + b2 = currtop2; + b4 = currtop4; + } + else + { + a0 = currbot0; + a2 = currbot2; + b0 = prevtop0; + b2 = prevtop2; + b4 = prevtop4; + } + p = c = 0; + + // Calculate the field match and film/video metrics. + skip = 1 + ( !cx->chroma ); + for (y = 0, index = 0; y < cx->h - 4; y+=4) + { + /* Exclusion band. Good for ignoring subtitles. */ + if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1) + { + for (x = 0; x < cx->w;) + { + index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2; + + // Test combination with current frame. + tmp1 = ((long)currbot0[x] + (long)currbot2[x]); + diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1); + if (diff > cx->nt) + { + c += diff; +#ifdef WINDOWED_MATCH + matchc[index] += diff; +#endif + } + + tmp1 = currbot0[x] + T; + tmp2 = currbot0[x] - T; + vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) || + (tmp2 > currtop0[x] && tmp2 > currtop2[x]); + if (vc) + { + cx->sumc[index]++; + } + + // Test combination with previous frame. + tmp1 = ((long)a0[x] + (long)a2[x]); + diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1); + if (diff > cx->nt) + { + p += diff; +#ifdef WINDOWED_MATCH + matchp[index] += diff; +#endif + } + + tmp1 = a0[x] + T; + tmp2 = a0[x] - T; + vc = (tmp1 < b0[x] && tmp1 < b2[x]) || + (tmp2 > b0[x] && tmp2 > b2[x]); + if (vc) + { + cx->sump[index]++; + } + + x += skip; + if (!(x&3)) x += 4; + } + } + currbot0 += cx->pitchtimes4; + currbot2 += cx->pitchtimes4; + currtop0 += cx->pitchtimes4; + currtop2 += cx->pitchtimes4; + currtop4 += cx->pitchtimes4; + a0 += cx->pitchtimes4; + a2 += cx->pitchtimes4; + b0 += cx->pitchtimes4; + b2 += cx->pitchtimes4; + b4 += cx->pitchtimes4; + } + + + if ( cx->post ) + { + cx->highest_sump = 0; + for (y = 0; y < cx->yblocks; y++) + { + for (x = 0; x < cx->xblocks; x++) + { + if (cx->sump[y * cx->xblocks + x] > cx->highest_sump) + { + cx->highest_sump = cx->sump[y * cx->xblocks + x]; + } + } + } + cx->highest_sumc = 0; + for (y = 0; y < cx->yblocks; y++) + { + for (x = 0; x < cx->xblocks; x++) + { + if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc) + { + cx->highest_sumc = cx->sumc[y * cx->xblocks + x]; + } + } + } + } +#ifdef WINDOWED_MATCH + CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc); +#else + CacheInsert( cx, frame, p, cx->highest_sump, c, cx->highest_sumc); +#endif +} + +/** Process the image. +*/ + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + // Get the filter service + mlt_filter filter = mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties frame_properties = mlt_frame_properties( frame ); + context cx = mlt_properties_get_data( properties, "context", NULL ); + mlt_service producer = mlt_service_producer( mlt_filter_service( filter ) ); + cx->out = producer? mlt_producer_get_playtime( MLT_PRODUCER( producer ) ) : 999999; + + if ( ! cx->is_configured ) + { + cx->back = mlt_properties_get_int( properties, "back" ); + cx->chroma = mlt_properties_get_int( properties, "chroma" ); + cx->guide = mlt_properties_get_int( properties, "guide" ); + cx->gthresh = mlt_properties_get_double( properties, "gthresh" ); + cx->post = mlt_properties_get_int( properties, "post" ); + cx->vthresh = mlt_properties_get_double( properties, "vthresh" ); + cx->bthresh = mlt_properties_get_double( properties, "bthresh" ); + cx->dthresh = mlt_properties_get_double( properties, "dthresh" ); + cx->blend = mlt_properties_get_int( properties, "blend" ); + cx->nt = mlt_properties_get_int( properties, "nt" ); + cx->y0 = mlt_properties_get_int( properties, "y0" ); + cx->y1 = mlt_properties_get_int( properties, "y1" ); + cx->hints = mlt_properties_get_int( properties, "hints" ); + cx->debug = mlt_properties_get_int( properties, "debug" ); + cx->show = mlt_properties_get_int( properties, "show" ); + } + + // Get the image + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + + if ( ! cx->sump ) + { + int guide = mlt_properties_get_int( properties, "guide" ); + cx->cycle = 0; + if ( guide == GUIDE_32 ) + { + // 24fps to 30 fps telecine. + cx->cycle = 5; + } + else if ( guide == GUIDE_22 ) + { + // PAL guidance (expect the current match to be continued). + cx->cycle = 2; + } + else if ( guide == GUIDE_32322 ) + { + // 25fps to 30 fps telecine. + cx->cycle = 6; + } + + cx->xblocks = (*width+BLKSIZE-1) / BLKSIZE; + cx->yblocks = (*height+BLKSIZE-1) / BLKSIZE; + cx->sump = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) ); + cx->sumc = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) ); + mlt_properties_set_data( properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL ); + mlt_properties_set_data( properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL ); + cx->tff = mlt_properties_get_int( frame_properties, "top_field_first" ); + } + + // Only process if we have no error and a valid colour space + if ( error == 0 && *format == mlt_image_yuv422 ) + { + // Put the current image into the image cache, keyed on position + size_t image_size = (*width * *height) << 1; + mlt_position pos = mlt_filter_get_position( filter, frame ); + uint8_t *image_copy = mlt_pool_alloc( image_size ); + memcpy( image_copy, *image, image_size ); + char key[20]; + sprintf( key, MLT_POSITION_FMT, pos ); + mlt_properties_set_data( cx->image_cache, key, image_copy, image_size, (mlt_destructor)mlt_pool_release, NULL ); + + // Only if we have enough frame images cached + if ( pos > 1 && pos > cx->cycle + 1 ) + { + pos -= cx->cycle + 1; + // Get the current frame image + sprintf( key, MLT_POSITION_FMT, pos ); + cx->fcrp = mlt_properties_get_data( cx->image_cache, key, NULL ); + if (!cx->fcrp) return error; + + // Get the previous frame image + cx->pframe = pos == 0 ? 0 : pos - 1; + sprintf( key, "%d", cx->pframe ); + cx->fprp = mlt_properties_get_data( cx->image_cache, key, NULL ); + if (!cx->fprp) return error; + + // Get the next frame image + cx->nframe = pos > cx->out ? cx->out : pos + 1; + sprintf( key, "%d", cx->nframe ); + cx->fnrp = mlt_properties_get_data( cx->image_cache, key, NULL ); + if (!cx->fnrp) return error; + + cx->pitch = *width << 1; + cx->pitchover2 = cx->pitch >> 1; + cx->pitchtimes4 = cx->pitch << 2; + cx->w = *width << 1; + cx->h = *height; + if ((cx->w/2) & 1) + fprintf( stderr, "%s: width must be a multiple of 2\n", __FUNCTION__ ); + if (cx->h & 1) + fprintf( stderr, "%s: height must be a multiple of 2\n", __FUNCTION__ ); + cx->wover2 = cx->w/2; + cx->hover2 = cx->h/2; + cx->hplus1over2 = (cx->h+1)/2; + cx->hminus2 = cx->h - 2; + cx->dpitch = cx->pitch; + + // Ensure that the metrics for the frames + // after the current frame are in the cache. They will be used for + // pattern guidance. + if ( cx->guide ) + { + for ( cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++ ) + { + if ( ! CacheQuery( cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) ) + { + sprintf( key, "%d", cx->y ); + cx->crp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL ); + sprintf( key, "%d", cx->y ? cx->y - 1 : 1 ); + cx->prp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL ); + CalculateMetrics( cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL ); + } + } + } + + // Check for manual overrides of the field matching. + cx->found = 0; + cx->film = 1; + cx->override = 0; + cx->inpattern = 0; + cx->vthresh = cx->vthresh; + cx->back = cx->back_saved; + + // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates. + if ( ! CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) ) + { + CalculateMetrics( cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL ); + CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ); + } + if ( ! CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ) ) + { + CalculateMetrics( cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL ); + CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ); + } + + // Determine the best candidate match. + if ( !cx->found ) + { + cx->lowest = cx->c; + cx->chosen = C; + if ( cx->back == ALWAYS_BACK && cx->p < cx->lowest ) + { + cx->lowest = cx->p; + cx->chosen = P; + } + if ( cx->np < cx->lowest ) + { + cx->lowest = cx->np; + cx->chosen = N; + } + } + if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N)) + { + cx->chosen = C; + cx->lowest = cx->c; + } + + // See if we can apply pattern guidance. + cx->mismatch = 100.0; + if ( cx->guide ) + { + cx->hard = 0; + if ( pos >= cx->cycle && PredictHardYUY2( cx, pos, &cx->predicted, &cx->predicted_metric) ) + { + cx->inpattern = 1; + cx->mismatch = 0.0; + cx->hard = 1; + if ( cx->chosen != cx->predicted ) + { + // The chosen frame doesn't match the prediction. + if ( cx->predicted_metric == 0 ) + cx->mismatch = 0.0; + else + cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest ) ) / cx->predicted_metric; + if ( cx->mismatch < cx->gthresh ) + { + // It's close enough, so use the predicted one. + if ( !cx->found ) + { + cx->chosen = cx->predicted; + cx->override = 1; + } + } + else + { + cx->hard = 0; + cx->inpattern = 0; + } + } + } + + if ( !cx->hard && cx->guide != GUIDE_22 ) + { + int i; + struct PREDICTION *pred = PredictSoftYUY2( cx, pos ); + + if ( ( pos <= cx->out - cx->cycle) && ( pred[0].metric != 0xffffffff ) ) + { + // Apply pattern guidance. + // If the predicted match metric is within defined percentage of the + // best calculated one, then override the calculated match with the + // predicted match. + i = 0; + while ( pred[i].metric != 0xffffffff ) + { + cx->predicted = pred[i].predicted; + cx->predicted_metric = pred[i].predicted_metric; +#ifdef DEBUG_PATTERN_GUIDANCE + fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted); +#endif + if ( cx->chosen != cx->predicted ) + { + // The chosen frame doesn't match the prediction. + if ( cx->predicted_metric == 0 ) + cx->mismatch = 0.0; + else + cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest )) / cx->predicted_metric; + if ( (int) cx->mismatch <= cx->gthresh ) + { + // It's close enough, so use the predicted one. + if ( !cx->found ) + { + cx->chosen = cx->predicted; + cx->override = 1; + } + cx->inpattern = 1; + break; + } + else + { + // Looks like we're not in a predictable pattern. + cx->inpattern = 0; + } + } + else + { + cx->inpattern = 1; + cx->mismatch = 0.0; + break; + } + i++; + } + } + } + } + + // Check the match for progressive versus interlaced. + if ( cx->post ) + { + if (cx->chosen == P) cx->vmetric = cx->pblock; + else if (cx->chosen == C) cx->vmetric = cx->cblock; + else if (cx->chosen == N) cx->vmetric = cx->npblock; + + if ( !cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest ) + { + // Backward match. + cx->vmetric = cx->pblock; + cx->chosen = P; + cx->inpattern = 0; + cx->mismatch = 100; + } + if ( cx->vmetric > cx->vthresh ) + { + // After field matching and pattern guidance the frame is still combed. + cx->film = 0; + if ( !cx->found && ( cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP ) ) + { + cx->chosen = C; + cx->vmetric = cx->cblock; + cx->inpattern = 0; + cx->mismatch = 100; + } + } + } + cx->vthresh = cx->vthresh_saved; + + // Setup strings for debug info. + if ( cx->inpattern && !cx->override ) strcpy( cx->status, "[in-pattern]" ); + else if ( cx->inpattern && cx->override ) strcpy( cx->status, "[in-pattern*]" ); + else strcpy( cx->status, "[out-of-pattern]" ); + + // Assemble and output the reconstructed frame according to the final match. + cx->dstp = *image; + if ( cx->chosen == N ) + { + // The best match was with the next frame. + if ( cx->tff ) + { + BitBlt( cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2 ); + BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); + } + else + { + BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); + BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); + } + } + else if ( cx->chosen == C ) + { + // The best match was with the current frame. + BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); + BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); + } + else if ( ! cx->tff ) + { + // The best match was with the previous frame. + BitBlt( cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); + BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); + } + else + { + // The best match was with the previous frame. + BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 ); + BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 ); + } + if ( cx->guide ) + PutChosen( cx, pos, cx->chosen ); + + if ( !cx->post || cx->post == POST_METRICS ) + { + if ( cx->force == '+') cx->film = 0; + else if ( cx->force == '-' ) cx->film = 1; + } + else if ((cx->force == '+') || + ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP) + && (cx->film == 0 && cx->force != '-'))) + { + unsigned char *dstpp, *dstpn; + int v1, v2; + + if ( cx->blend ) + { + // Do first and last lines. + uint8_t *final = mlt_pool_alloc( image_size ); + cx->finalp = final; + mlt_frame_set_image( frame, final, image_size, mlt_pool_release ); + dstpn = cx->dstp + cx->dpitch; + for ( cx->x = 0; cx->x < cx->w; cx->x++ ) + { + cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpn[cx->x]) >> 1); + } + cx->finalp = final + (cx->h-1)*cx->dpitch; + cx->dstp = *image + (cx->h-1)*cx->dpitch; + dstpp = cx->dstp - cx->dpitch; + for ( cx->x = 0; cx->x < cx->w; cx->x++ ) + { + cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpp[cx->x]) >> 1); + } + // Now do the rest. + cx->dstp = *image + cx->dpitch; + dstpp = cx->dstp - cx->dpitch; + dstpn = cx->dstp + cx->dpitch; + cx->finalp = final + cx->dpitch; + for ( cx->y = 1; cx->y < cx->h - 1; cx->y++ ) + { + for ( cx->x = 0; cx->x < cx->w; cx->x++ ) + { + v1 = (int) cx->dstp[cx->x] - cx->dthresh; + if ( v1 < 0 ) + v1 = 0; + v2 = (int) cx->dstp[cx->x] + cx->dthresh; + if (v2 > 235) v2 = 235; + if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) + { + if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP ) + { + if (cx->x & 1) cx->finalp[cx->x] = 128; + else cx->finalp[cx->x] = 235; + } + else + cx->finalp[cx->x] = ((int)dstpp[cx->x] + (int)dstpn[cx->x] + (int)cx->dstp[cx->x] + (int)cx->dstp[cx->x]) >> 2; + } + else cx->finalp[cx->x] = cx->dstp[cx->x]; + } + cx->finalp += cx->dpitch; + cx->dstp += cx->dpitch; + dstpp += cx->dpitch; + dstpn += cx->dpitch; + } + + + if (cx->show ) Show( cx, pos, frame_properties); + if (cx->debug) Debug(cx, pos); + if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); + goto final; + } + + // Interpolate mode. + cx->dstp = *image + cx->dpitch; + dstpp = cx->dstp - cx->dpitch; + dstpn = cx->dstp + cx->dpitch; + for ( cx->y = 1; cx->y < cx->h - 1; cx->y+=2 ) + { + for ( cx->x = 0; cx->x < cx->w; cx->x++ ) + { + v1 = (int) cx->dstp[cx->x] - cx->dthresh; + if (v1 < 0) v1 = 0; + v2 = (int) cx->dstp[cx->x] + cx->dthresh; + if (v2 > 235) v2 = 235; + if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x])) + { + if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP ) + { + if (cx->x & 1) cx->dstp[cx->x] = 128; + else cx->dstp[cx->x] = 235; + } + else + cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1; + } + } + cx->dstp += 2 * cx->dpitch; + dstpp += 2 * cx->dpitch; + dstpn += 2 * cx->dpitch; + } + } + if (cx->show ) Show( cx, pos, frame_properties); + if (cx->debug) Debug(cx, pos); + if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties); + +final: + // Flush frame at tail of period from the cache + sprintf( key, MLT_POSITION_FMT, pos - 1 ); + mlt_properties_set_data( cx->image_cache, key, NULL, 0, NULL, NULL ); + } + else + { + // Signal the first {cycle} frames as invalid + mlt_properties_set_int( frame_properties, "garbage", 1 ); + } + } + else if ( error == 0 && *format == mlt_image_yuv420p ) + { + fprintf(stderr,"%s: %d pos " MLT_POSITION_FMT "\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) ); + } + + return error; +} + +/** Process the frame object. +*/ + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + // Push the filter on to the stack + mlt_frame_push_service( frame, filter ); + + // Push the frame filter + mlt_frame_push_get_image( frame, get_image ); + + return frame; +} + +/** Constructor for the filter. +*/ + +mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new( ); + if ( filter != NULL ) + { + filter->process = process; + + // Allocate the context and set up for garbage collection + context cx = (context) mlt_pool_alloc( sizeof(struct context_s) ); + memset( cx, 0, sizeof( struct context_s ) ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_data( properties, "context", cx, sizeof(struct context_s), (mlt_destructor)mlt_pool_release, NULL ); + + // Allocate the metrics cache and set up for garbage collection + cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY )); + mlt_properties_set_data( properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor)mlt_pool_release, NULL ); + int i; + for (i = 0; i < CACHE_SIZE; i++) + { + cx->cache[i].frame = 0xffffffff; + cx->cache[i].chosen = 0xff; + } + + // Allocate the image cache and set up for garbage collection + cx->image_cache = mlt_properties_new(); + mlt_properties_set_data( properties, "image_cache", cx->image_cache, 0, (mlt_destructor)mlt_properties_close, NULL ); + + // Initialize the parameter defaults + mlt_properties_set_int( properties, "guide", 0 ); + mlt_properties_set_int( properties, "back", 0 ); + mlt_properties_set_int( properties, "chroma", 0 ); + mlt_properties_set_int( properties, "post", POST_FULL ); + mlt_properties_set_double( properties, "gthresh", 10.0 ); + mlt_properties_set_double( properties, "vthresh", 50.0 ); + mlt_properties_set_double( properties, "bthresh", 50.0 ); + mlt_properties_set_double( properties, "dthresh", 7.0 ); + mlt_properties_set_int( properties, "blend", 0 ); + mlt_properties_set_int( properties, "nt", 10 ); + mlt_properties_set_int( properties, "y0", 0 ); + mlt_properties_set_int( properties, "y1", 0 ); + mlt_properties_set_int( properties, "hints", 1 ); + } + return filter; +} + diff -Nru mlt-0.9.0/src/modules/plusgpl/image.c mlt-0.9.2/src/modules/plusgpl/image.c --- mlt-0.9.0/src/modules/plusgpl/image.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/image.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,308 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * image.c: utilities for image processing. + * + */ + +#include +#include +#include "utils.h" + + +/* + * Collection of background subtraction functions + */ + +/* checks only fake-Y value */ +/* In these function Y value is treated as R*2+G*4+B. */ + +int image_set_threshold_y(int threshold) +{ + int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */ + + return y_threshold; +} + +void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + short *q; + + p = src; + q = (short *)background; + for(i=0; i>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + *q = (short)(R + G + B); + p++; + q++; + } +} + +void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + const short *q; + unsigned char *r; + int v; + + p = src; + q = (const short *)background; + r = diff; + for(i=0; i>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + v = (R + G + B) - (int)(*q); + *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); + + p++; + q++; + r++; + } + +/* The origin of subtraction function is; + * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0; + * + * This functions is transformed to; + * (threshold > (src - dest) > -threshold) ? 0 : 0xff; + * + * (v + threshold)>>24 is 0xff when v is less than -threshold. + * (v - threshold)>>24 is 0xff when v is less than threshold. + * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when + * abs(src - dest) > threshold. + */ +} + +/* Background image is refreshed every frame */ +void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B; + const RGB32 *p; + short *q; + unsigned char *r; + int v; + + p = src; + q = (short *)background; + r = diff; + for(i=0; i>(16-1); + G = ((*p)&0xff00)>>(8-2); + B = (*p)&0xff; + v = (R + G + B) - (int)(*q); + *q = (short)(R + G + B); + *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24); + + p++; + q++; + r++; + } +} + +/* checks each RGB value */ + +/* The range of r, g, b are [0..7] */ +RGB32 image_set_threshold_RGB(int r, int g, int b) +{ + unsigned char R, G, B; + RGB32 rgb_threshold; + + R = G = B = 0xff; + R = R<>8); + b = b ^ 0xffffff; + a = a ^ b; + a = a & rgb_threshold; + *r++ = (0 - a)>>24; + } +} + +void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold) +{ + int i; + const RGB32 *p; + RGB32 *q; + unsigned a, b; + unsigned char *r; + + p = src; + q = background; + r = diff; + for(i=0; i>8); + b = b ^ 0xffffff; + a = a ^ b; + a = a & rgb_threshold; + *r++ = (0 - a)>>24; + } +} + +/* noise filter for subtracted image. */ +void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height) +{ + int x, y; + const unsigned char *src; + unsigned char *dest; + unsigned int count; + unsigned int sum1, sum2, sum3; + + src = diff; + dest = diff2 + width +1; + for(y=1; y>24; + src++; + } + dest += 2; + } +} + +/* Y value filters */ +void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B, v; + unsigned char *p = diff; + + for(i = video_area; i>0; i--) { + R = ((*src)&0xff0000)>>(16-1); + G = ((*src)&0xff00)>>(8-2); + B = (*src)&0xff; + v = y_threshold - (R + G + B); + *p = (unsigned char)(v>>24); + src++; + p++; + } +} + +void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold) +{ + int i; + int R, G, B, v; + unsigned char *p = diff; + + for(i = video_area; i>0; i--) { + R = ((*src)&0xff0000)>>(16-1); + G = ((*src)&0xff00)>>(8-2); + B = (*src)&0xff; + v = (R + G + B) - y_threshold; + *p = (unsigned char)(v>>24); + src++; + p++; + } +} + +/* tiny edge detection */ +void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold) +{ + int x, y; + const unsigned char *p; + unsigned char *q; + int r, g, b; + int ar, ag, ab; + int w; + + p = (const unsigned char *)src; + q = diff2; + w = width * sizeof(RGB32); + + for(y=0; y y_threshold) { + *q = 255; + } else { + *q = 0; + } + q++; + p += 4; + } + p += 4; + *q++ = 0; + } + memset(q, 0, width); +} + +/* horizontal flipping */ +void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height) +{ + int x, y; + + src += width - 1; + for(y=0; y.depend + +distclean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +install: all + install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" + install -d $(DESTDIR)$(mltdatadir)/plusgpl + install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/plusgpl" + +ifneq ($(wildcard .depend),) +include .depend +endif diff -Nru mlt-0.9.0/src/modules/plusgpl/utils.c mlt-0.9.2/src/modules/plusgpl/utils.c --- mlt-0.9.0/src/modules/plusgpl/utils.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/utils.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * utils.c: utilities + * + */ + +#include +#include "utils.h" + +/* + * HSI color system utilities + */ +static int itrunc(double f) +{ + int i; + + i=(int)f; + if(i<0)i=0; + if(i>255)i=255; + return i; +} + +void HSItoRGB(double H, double S, double I, int *r, int *g, int *b) +{ + double T,Rv,Gv,Bv; + + Rv=1+S*sin(H-2*M_PI/3); + Gv=1+S*sin(H); + Bv=1+S*sin(H+2*M_PI/3); + T=255.999*I/2; + *r=itrunc(Rv*T); + *g=itrunc(Gv*T); + *b=itrunc(Bv*T); +} + +/* + * fastrand - fast fake random number generator + * Warning: The low-order bits of numbers generated by fastrand() + * are bad as random numbers. For example, fastrand()%4 + * generates 1,2,3,0,1,2,3,0... + * You should use high-order bits. + */ +#ifdef __DARWIN__ +static +#endif +unsigned int fastrand_val; + +unsigned int fastrand(void) +{ + return (fastrand_val=fastrand_val*1103515245+12345); +} + +void fastsrand(unsigned int seed) +{ + fastrand_val = seed; +} diff -Nru mlt-0.9.0/src/modules/plusgpl/utils.h mlt-0.9.2/src/modules/plusgpl/utils.h --- mlt-0.9.0/src/modules/plusgpl/utils.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/plusgpl/utils.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * EffecTV - Realtime Digital Video Effector + * Copyright (C) 2001-2006 FUKUCHI Kentaro + * + * utils.h: header file for utils + * + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include + +typedef uint32_t RGB32; + +/* DEFINE's by nullset@dookie.net */ +#define RED(n) ((n>>16) & 0x000000FF) +#define GREEN(n) ((n>>8) & 0x000000FF) +#define BLUE(n) ((n>>0) & 0x000000FF) +#define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b)) +#define INTENSITY(n) ( ( (RED(n)+GREEN(n)+BLUE(n))/3)) + +/* utils.c */ +void HSItoRGB(double H, double S, double I, int *r, int *g, int *b); + +#ifndef __DARWIN__ +extern unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) +#endif +unsigned int fastrand(void); +void fastsrand(unsigned int); + +/* image.c */ +int image_set_threshold_y(int threshold); +void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold); +RGB32 image_set_threshold_RGB(int r, int g, int b); +void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area); +void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); +void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold); +void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height); +void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); +void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold); +void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold); +void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height); + +#endif /* __UTILS_H__ */ diff -Nru mlt-0.9.0/src/modules/qimage/configure mlt-0.9.2/src/modules/qimage/configure --- mlt-0.9.0/src/modules/qimage/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/configure 1970-01-01 00:00:00.000000000 +0000 @@ -1,178 +0,0 @@ -#!/bin/sh - -if [ "$help" = "1" ] -then - cat << EOF -QImage options: - - --force-qt3 - Force compile against Qt3 if Qt4 is present on the system - --qimage-libdir - Location of QT lib directory [/usr/lib/qt4 or /usr/lib/qt3] - --qimage-includedir - Location of QT include directory [/usr/include/qt4 or /usr/include/qt3] - --kde-libdir - Location of KDE lib directory [/usr/lib] - --kde-includedir - Location of KDE include directory [/usr/include/kde] - --exif-libdir - Location of libexif lib directory [/usr/lib] - --exif-includedir - Location of libexif include directory [/usr/include/libexif] - --without-kde - Don't link to KDE libraries - -EOF - -else - targetos=$(uname -s) - case $targetos in - MINGW32*) - export LIBSUF=.dll - ;; - Darwin) - export LIBSUF=.dylib - ;; - Linux|FreeBSD|NetBSD) - export LIBSUF=.so - ;; - *) - ;; - esac - - qimage_includedir= - qimage_libdir= - - if [ "$QTDIR" != "" ] - then - qimage_includedir="$QTDIR/include" - qimage_libdir="$QTDIR/lib" - fi - - export force_qt3= - export qt4_found= - export without_kde= - - for i in "$@" - do - case $i in - --qimage-libdir=* ) qimage_libdir="${i#--qimage-libdir=}" ;; - --qimage-includedir=* ) qimage_includedir="${i#--qimage-includedir=}" ;; - --kde-libdir=* ) kde_libdir="${i#--kde-libdir=}" ;; - --kde-includedir=* ) kde_includedir="${i#--kde-includedir=}" ;; - --exif-libdir=* ) exif_libdir="${i#--exif-libdir=}" ;; - --exif-includedir=* ) exif_includedir="${i#--exif-includedir=}" ;; - --force-qt3 ) force_qt3="true" ;; - --without-kde ) without_kde="true" ;; - esac - done - - echo > config.h - echo > config.mak - - pkg-config --exists 'libexif' - if [ $? -eq 0 ] - then - echo "Libexif found, enabling auto rotate" - echo "#define USE_EXIF" >> config.h - echo "USE_EXIF=1" >> config.mak - echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak - echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak - elif [ -d "$exif_libdir" -a -d "$exif_includedir" ] - then - # test if we have a libexif - if [ -f "$exif_libdir/exif-data.h" ] - then - echo "Libexif found, enabling auto rotate" - echo "#define USE_EXIF" >> config.h - echo "USE_EXIF=1" >> config.mak - echo EXIFCXXFLAGS=-I$exif_includedir >> config.mak - echo EXIFLIBS=-L$exif_libdir lexif >> config.mak - else - echo "Libexif not found, disabling exif features (auto rotate)" - fi - fi - - if [ -d "$qimage_libdir" -a -d "$qimage_includedir" ] - then - # test if we have a Qt3 or Qt4 - if [ -f "$qimage_libdir/libQtCore.so" ] || [ -d "$qimage_libdir/QtGui.framework" ] || [ -f "$qimage_libdir/libQtCore4.a" ] && [ "$force_qt3" = "" ] - then - echo "Qt version 4.x detected, will compile Qt4 qimage producer" - qt4_found=true - else - echo "Qt version 3.x detected, will compile Qt3 qimage producer" - fi - - echo "Include directory: " $qimage_includedir - - if [ "$qt4_found" != "" ] && [ "$force_qt3" = "" ] - then - echo "#define USE_QT4" >> config.h - echo "USE_QT4=1" >> config.mak - if [ -d "$qimage_libdir/QtGui.framework" ] - then - echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak - echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak - elif [ -f "$qimage_libdir/libQtCore4.a" ] - then - echo QTCXXFLAGS=-I$qimage_includedir >> config.mak - echo QTLIBS=-Wl,-enable-auto-import -L$qimage_libdir -lQtCore4 -lQtGui4 -lQtXml4 -lQtSvg4 -lQtOpenGL4 >> config.mak - else - echo QTCXXFLAGS=-I$qimage_includedir >> config.mak - echo QTLIBS=-L$qimage_libdir -lQtCore -lQtGui -lQtXml -lQtSvg -lQtOpenGL >> config.mak - fi - else - if [ "$without_kde" = "" ] && [ -d "$kde_includedir" ] - then - echo "#define USE_KDE3" >> config.h - echo "USE_KDE3=1" >> config.mak - echo "#define USE_QT3" >> config.h - echo "USE_QT3=1" >> config.mak - echo QTCXXFLAGS=-I$qimage_includedir -I$kde_includedir -DQT_THREAD_SUPPORT >> config.mak - echo QTLIBS=-L$qimage_libdir -L$kde_libdir -lqt-mt >> config.mak - else - echo "qimage: KDE environment not found or disabled by request - disabling extra image formats" - echo "#define USE_QT3" >> config.h - echo "USE_QT3=1" >> config.mak - echo QTCXXFLAGS=-I$qimage_includedir -DQT_THREAD_SUPPORT>> config.mak - echo QTLIBS=-L$qimage_libdir -lqt-mt >> config.mak - fi - fi - else - pkg-config --exists 'QtGui >= 4' - if [ $? -eq 0 ] && [ "$force_qt3" = "" ] - then - echo "Qt version 4.x detected, will compile Qt4 qimage producer" - qt4_found=true - echo "#define USE_QT4" >> config.h - echo "USE_QT4=1" >> config.mak - echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak - echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak - else - echo "qimage: QT environment not found - disabling" - touch ../disable-qimage - fi - fi - - if [ "$without_kde" = "" ] - then - kde4-config - if [ $? -eq 0 ] && [ "$qt4_found" != "" ] - then - # test if we have KDE4, required on some systems to get QImage extra formats (xcf, ...) - if [ "$kde_includedir" = "" ] - then - kde_includedir=`kde4-config --install include` - fi - if [ "$kde_libdir" = "" ] - then - kde_libdir=`kde4-config --install lib` - fi - if [ -d "$kde_includedir" ] && [ -d "$kde_libdir" ] - then - echo "KDE version 4.x detected, will enable extra image formats" - echo "#define USE_KDE4" >> config.h - echo "USE_KDE4=1" >> config.mak - echo KDECXXFLAGS=-I$kde_includedir >> config.mak - # the -L with kde4/devel is for Fedora - echo KDELIBS=-L$kde_libdir -L${kde_libdir}/kde4/devel -lkdecore >> config.mak - fi - fi - fi - - [ "$gpl3" = "true" ] && echo GPL3=1 >> config.mak - exit 0 -fi diff -Nru mlt-0.9.0/src/modules/qimage/consumer_qglsl.cpp mlt-0.9.2/src/modules/qimage/consumer_qglsl.cpp --- mlt-0.9.0/src/modules/qimage/consumer_qglsl.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/consumer_qglsl.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -/* - * consumer_qglsl.cpp - * Copyright (C) 2012 Dan Dennedy - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include - -static void onThreadStarted(mlt_properties owner, mlt_consumer consumer) -{ - mlt_service service = MLT_CONSUMER_SERVICE(consumer); - mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); - mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); - mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); - QApplication* app = qApp; - - mlt_log_debug(service, "%s\n", __FUNCTION__); -#ifdef linux - if ( getenv("DISPLAY") == 0 ) { - mlt_log_error(service, "The qglsl consumer requires a X11 environment.\nPlease either run melt from an X session or use a fake X server like xvfb:\nxvfb-run -a melt (...)\n" ); - } else -#endif - if (!app) { - int argc = 1; - char* argv[1]; - argv[0] = (char*) "MLT qglsl consumer"; - app = new QApplication(argc, argv); - const char *localename = mlt_properties_get_lcnumeric(properties); - QLocale::setDefault(QLocale(localename)); - } - QGLWidget* renderContext = new QGLWidget; - renderContext->resize(0, 0); - renderContext->show(); - mlt_events_fire(filter_properties, "init glsl", NULL); - if (!mlt_properties_get_int(filter_properties, "glsl_supported")) { - mlt_log_fatal(service, - "OpenGL Shading Language rendering is not supported on this machine.\n" ); - mlt_events_fire(properties, "consumer-fatal-error", NULL); - } - else { - mlt_properties_set_data(properties, "qglslRenderContext", renderContext, 0, NULL, NULL); - } -} - -static void onCleanup(mlt_properties owner, mlt_consumer consumer) -{ - QGLWidget* renderContext = (QGLWidget*) mlt_properties_get_data( - MLT_CONSUMER_PROPERTIES(consumer), "qglslRenderContext", NULL); - if (renderContext) - renderContext->makeCurrent(); - delete renderContext; - mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(consumer), - "qglslRenderContext", NULL, 0, NULL, NULL); -} - -extern "C" { - -mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_consumer consumer = mlt_factory_consumer(profile, "multi", arg); - if (consumer) { - mlt_filter filter = mlt_factory_filter(profile, "glsl.manager", 0); - if (filter) { - mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); - mlt_properties_set_data(properties, "glslManager", filter, 0, (mlt_destructor) mlt_filter_close, NULL); - mlt_events_register( properties, "consumer-cleanup", NULL ); - mlt_events_listen(properties, consumer, "consumer-thread-started", (mlt_listener) onThreadStarted); - mlt_events_listen(properties, consumer, "consumer-cleanup", (mlt_listener) onCleanup); - return consumer; - } - mlt_consumer_close(consumer); - } - return NULL; -} - -} diff -Nru mlt-0.9.0/src/modules/qimage/factory.c mlt-0.9.2/src/modules/qimage/factory.c --- mlt-0.9.0/src/modules/qimage/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/factory.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * factory.c -- the factory method interfaces - * Copyright (C) 2006 Visual Media - * Author: Charles Yates - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -extern mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); -extern mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); -extern mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); -extern mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ); - -static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) -{ - char file[ PATH_MAX ]; - snprintf( file, PATH_MAX, "%s/qimage/%s", mlt_environment( "MLT_DATA" ), (char*) data ); - return mlt_properties_parse_yaml( file ); -} - -MLT_REPOSITORY -{ - MLT_REGISTER( consumer_type, "qglsl", consumer_qglsl_init ); - MLT_REGISTER( producer_type, "qimage", producer_qimage_init ); - MLT_REGISTER( producer_type, "kdenlivetitle", producer_kdenlivetitle_init ); - MLT_REGISTER_METADATA( producer_type, "qimage", metadata, "producer_qimage.yml" ); - MLT_REGISTER_METADATA( producer_type, "kdenlivetitle", metadata, "producer_kdenlivetitle.yml" ); -#ifdef GPL3 - MLT_REGISTER( transition_type, "vqm", transition_vqm_init ); - MLT_REGISTER_METADATA( transition_type, "vqm", metadata, "transition_vqm.yml" ); -#endif -} diff -Nru mlt-0.9.0/src/modules/qimage/kdenlivetitle_wrapper.cpp mlt-0.9.2/src/modules/qimage/kdenlivetitle_wrapper.cpp --- mlt-0.9.0/src/modules/qimage/kdenlivetitle_wrapper.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/kdenlivetitle_wrapper.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,539 +0,0 @@ -/* - * kdenlivetitle_wrapper.cpp -- kdenlivetitle wrapper - * Copyright (c) 2009 Marco Gittler - * Copyright (c) 2009 Jean-Baptiste Mardelle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "kdenlivetitle_wrapper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#if QT_VERSION >= 0x040600 -#include -#include -#include -#endif - -static QApplication *app = NULL; -Q_DECLARE_METATYPE(QTextCursor); - -class ImageItem: public QGraphicsItem -{ -public: - ImageItem(QImage img) - { - m_img = img; - } - QImage m_img; - - -protected: - -virtual QRectF boundingRect() const -{ - return QRectF(0, 0, m_img.width(), m_img.height()); -} - -virtual void paint( QPainter *painter, - const QStyleOptionGraphicsItem * /*option*/, - QWidget* ) -{ - painter->setRenderHint(QPainter::SmoothPixmapTransform, true); - painter->drawImage(QPoint(), m_img); -} -}; - - -QRectF stringToRect( const QString & s ) -{ - - QStringList l = s.split( ',' ); - if ( l.size() < 4 ) - return QRectF(); - return QRectF( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble() ).normalized(); -} - -QColor stringToColor( const QString & s ) -{ - QStringList l = s.split( ',' ); - if ( l.size() < 4 ) - return QColor(); - return QColor( l.at( 0 ).toInt(), l.at( 1 ).toInt(), l.at( 2 ).toInt(), l.at( 3 ).toInt() ); - ; -} -QTransform stringToTransform( const QString& s ) -{ - QStringList l = s.split( ',' ); - if ( l.size() < 9 ) - return QTransform(); - return QTransform( - l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), - l.at( 3 ).toDouble(), l.at( 4 ).toDouble(), l.at( 5 ).toDouble(), - l.at( 6 ).toDouble(), l.at( 7 ).toDouble(), l.at( 8 ).toDouble() - ); -} - -static void qscene_delete( void *data ) -{ - QGraphicsScene *scene = ( QGraphicsScene * )data; - if (scene) delete scene; - scene = NULL; -} - - -void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *templateXml, const char *templateText ) -{ - scene->clear(); - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - QDomDocument doc; - QString data = QString::fromUtf8(templateXml); - QString replacementText = QString::fromUtf8(templateText); - doc.setContent(data); - QDomElement title = doc.documentElement(); - - // Check for invalid title - if ( title.isNull() || title.tagName() != "kdenlivetitle" ) return; - - // Check title locale - if ( title.hasAttribute( "LC_NUMERIC" ) ) { - QString locale = title.attribute( "LC_NUMERIC" ); - QLocale::setDefault( locale ); - } - - int originalWidth; - int originalHeight; - if ( title.hasAttribute("width") ) { - originalWidth = title.attribute("width").toInt(); - originalHeight = title.attribute("height").toInt(); - scene->setSceneRect(0, 0, originalWidth, originalHeight); - } - else { - originalWidth = scene->sceneRect().width(); - originalHeight = scene->sceneRect().height(); - } - if ( title.hasAttribute( "out" ) ) { - mlt_properties_set_position( producer_props, "_animation_out", title.attribute( "out" ).toDouble() ); - } - else { - mlt_properties_set_position( producer_props, "_animation_out", mlt_producer_get_out( producer ) ); - } - - mlt_properties_set_int( producer_props, "_original_width", originalWidth ); - mlt_properties_set_int( producer_props, "_original_height", originalHeight ); - - QDomNode node; - QDomNodeList items = title.elementsByTagName("item"); - for ( int i = 0; i < items.count(); i++ ) - { - QGraphicsItem *gitem = NULL; - node = items.item( i ); - QDomNamedNodeMap nodeAttributes = node.attributes(); - int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); - if ( zValue > -1000 ) - { - if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) - { - QDomNamedNodeMap txtProperties = node.namedItem( "content" ).attributes(); - QFont font( txtProperties.namedItem( "font" ).nodeValue() ); - QDomNode propsNode = txtProperties.namedItem( "font-bold" ); - if ( !propsNode.isNull() ) - { - // Old: Bold/Not bold. - font.setBold( propsNode.nodeValue().toInt() ); - } - else - { - // New: Font weight (QFont::) - font.setWeight( txtProperties.namedItem( "font-weight" ).nodeValue().toInt() ); - } - font.setItalic( txtProperties.namedItem( "font-italic" ).nodeValue().toInt() ); - font.setUnderline( txtProperties.namedItem( "font-underline" ).nodeValue().toInt() ); - // Older Kdenlive version did not store pixel size but point size - if ( txtProperties.namedItem( "font-pixel-size" ).isNull() ) - { - QFont f2; - f2.setPointSize( txtProperties.namedItem( "font-size" ).nodeValue().toInt() ); - font.setPixelSize( QFontInfo( f2 ).pixelSize() ); - } - else - font.setPixelSize( txtProperties.namedItem( "font-pixel-size" ).nodeValue().toInt() ); - QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); - QString text = node.namedItem( "content" ).firstChild().nodeValue(); - if ( !replacementText.isEmpty() ) - { - text = text.replace( "%s", replacementText ); - } - QGraphicsTextItem *txt = scene->addText(text, font); - if (txtProperties.namedItem("font-outline").nodeValue().toDouble()>0.0){ - QTextDocument *doc = txt->document(); - // Make sure some that the text item does not request refresh by itself - doc->blockSignals(true); - QTextCursor cursor(doc); - cursor.select(QTextCursor::Document); - QTextCharFormat format; - format.setTextOutline( - QPen(QColor( stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ) ), - txtProperties.namedItem("font-outline").nodeValue().toDouble(), - Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin) - ); - format.setForeground(QBrush(col)); - - cursor.mergeCharFormat(format); - } else { - txt->setDefaultTextColor( col ); - } - - // Effects - if (!txtProperties.namedItem( "typewriter" ).isNull()) { - // typewriter effect - mlt_properties_set_int( producer_props, "_animated", 1 ); - QStringList effetData = QStringList() << "typewriter" << text << txtProperties.namedItem( "typewriter" ).nodeValue(); - txt->setData(0, effetData); - if ( !txtProperties.namedItem( "textwidth" ).isNull() ) - txt->setData( 1, txtProperties.namedItem( "textwidth" ).nodeValue() ); - } - - if ( txtProperties.namedItem( "alignment" ).isNull() == false ) - { - txt->setTextWidth( txt->boundingRect().width() ); - QTextOption opt = txt->document()->defaultTextOption (); - opt.setAlignment(( Qt::Alignment ) txtProperties.namedItem( "alignment" ).nodeValue().toInt() ); - txt->document()->setDefaultTextOption (opt); - } - if ( !txtProperties.namedItem( "kdenlive-axis-x-inverted" ).isNull() ) - { - //txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt()); - } - if ( !txtProperties.namedItem( "kdenlive-axis-y-inverted" ).isNull() ) - { - //txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt()); - } - gitem = txt; - } - else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) - { - QString rect = node.namedItem( "content" ).attributes().namedItem( "rect" ).nodeValue(); - QString br_str = node.namedItem( "content" ).attributes().namedItem( "brushcolor" ).nodeValue(); - QString pen_str = node.namedItem( "content" ).attributes().namedItem( "pencolor" ).nodeValue(); - double penwidth = node.namedItem( "content" ).attributes().namedItem( "penwidth") .nodeValue().toDouble(); - QGraphicsRectItem *rec = scene->addRect( stringToRect( rect ), QPen( QBrush( stringToColor( pen_str ) ), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ), QBrush( stringToColor( br_str ) ) ); - gitem = rec; - } - else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) - { - const QString url = node.namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); - const QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); - QImage img; - if (base64.isEmpty()){ - img.load(url); - }else{ - img.loadFromData(QByteArray::fromBase64(base64.toAscii())); - } - ImageItem *rec = new ImageItem(img); - scene->addItem( rec ); - gitem = rec; - } - else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) - { - QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue(); - QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); - QGraphicsSvgItem *rec = NULL; - if (base64.isEmpty()){ - rec = new QGraphicsSvgItem(url); - }else{ - rec = new QGraphicsSvgItem(); - QSvgRenderer *renderer= new QSvgRenderer(QByteArray::fromBase64(base64.toAscii()), rec ); - rec->setSharedRenderer(renderer); - } - if (rec){ - scene->addItem(rec); - gitem = rec; - } - } - } - //pos and transform - if ( gitem ) - { - QPointF p( node.namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), - node.namedItem( "position" ).attributes().namedItem( "y" ).nodeValue().toDouble() ); - gitem->setPos( p ); - gitem->setTransform( stringToTransform( node.namedItem( "position" ).firstChild().firstChild().nodeValue() ) ); - int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); - gitem->setZValue( zValue ); - -#if QT_VERSION >= 0x040600 - // effects - QDomNode eff = items.item(i).namedItem("effect"); - if (!eff.isNull()) { - QDomElement e = eff.toElement(); - if (e.attribute("type") == "blur") { - QGraphicsBlurEffect *blur = new QGraphicsBlurEffect(); - blur->setBlurRadius(e.attribute("blurradius").toInt()); - gitem->setGraphicsEffect(blur); - } - else if (e.attribute("type") == "shadow") { - QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); - shadow->setBlurRadius(e.attribute("blurradius").toInt()); - shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt()); - gitem->setGraphicsEffect(shadow); - } - } -#endif - } - } - - QDomNode n = title.firstChildElement("background"); - if (!n.isNull()) { - QColor color = QColor( stringToColor( n.attributes().namedItem( "color" ).nodeValue() ) ); - if (color.alpha() > 0) { - QGraphicsRectItem *rec = scene->addRect(0, 0, scene->width(), scene->height() , QPen( Qt::NoPen ), QBrush( color ) ); - rec->setZValue(-1100); - } - - } - - QString startRect; - n = title.firstChildElement( "startviewport" ); - // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport - if (!n.isNull() && !n.toElement().hasAttribute("x")) - { - startRect = n.attributes().namedItem( "rect" ).nodeValue(); - } - n = title.firstChildElement( "endviewport" ); - // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport - if (!n.isNull() && !n.toElement().hasAttribute("x")) - { - QString rect = n.attributes().namedItem( "rect" ).nodeValue(); - if (startRect != rect) - mlt_properties_set( producer_props, "_endrect", rect.toUtf8().data() ); - } - if (!startRect.isEmpty()) { - mlt_properties_set( producer_props, "_startrect", startRect.toUtf8().data() ); - } - return; -} - - -void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int height, double position, int force_refresh ) -{ - // Obtain the producer - mlt_producer producer = &self->parent; - mlt_profile profile = mlt_service_profile ( MLT_PRODUCER_SERVICE( producer ) ) ; - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - - // Obtain properties of frame - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - - pthread_mutex_lock( &self->mutex ); - - // Check if user wants us to reload the image - if ( mlt_properties_get( producer_props, "_animated" ) != NULL || force_refresh == 1 || width != self->current_width || height != self->current_height || mlt_properties_get( producer_props, "_endrect" ) != NULL ) - { - //mlt_cache_item_close( self->image_cache ); - self->current_image = NULL; - mlt_properties_set_data( producer_props, "cached_image", NULL, 0, NULL, NULL ); - mlt_properties_set_int( producer_props, "force_reload", 0 ); - } - - if (self->current_image == NULL) { - // restore QGraphicsScene - QGraphicsScene *scene = static_cast (mlt_properties_get_data( producer_props, "qscene", NULL )); - - if ( force_refresh == 1 && scene ) - { - scene = NULL; - mlt_properties_set_data( producer_props, "qscene", NULL, 0, NULL, NULL ); - } - - if ( scene == NULL ) - { - int argc = 1; - char* argv[1]; - argv[0] = (char*) "xxx"; - - // Warning: all Qt graphic objects (QRect, ...) must be initialized AFTER - // the QApplication is created, otherwise their will be NULL - - if ( app == NULL ) { - if ( qApp ) { - app = qApp; - } - else { -#ifdef linux - if ( getenv("DISPLAY") == 0 ) - { - mlt_log_panic( MLT_PRODUCER_SERVICE( producer ), "Error, cannot render titles without an X11 environment.\nPlease either run melt from an X session or use a fake X server like xvfb:\nxvfb-run -a melt (...)\n" ); - pthread_mutex_unlock( &self->mutex ); - return; - } -#endif - app = new QApplication( argc, argv ); - const char *localename = mlt_properties_get_lcnumeric( MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) ) ); - QLocale::setDefault( QLocale( localename ) ); - } - qRegisterMetaType( "QTextCursor" ); - } - scene = new QGraphicsScene(); - scene->setItemIndexMethod( QGraphicsScene::NoIndex ); - scene->setSceneRect(0, 0, mlt_properties_get_int( properties, "width" ), mlt_properties_get_int( properties, "height" )); - if ( mlt_properties_get( producer_props, "resource" ) && mlt_properties_get( producer_props, "resource" )[0] != '\0' ) - { - // The title has a resource property, so we read all properties from the resource. - // Do not serialize the xmldata - loadFromXml( producer, scene, mlt_properties_get( producer_props, "_xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); - } - else - { - // The title has no resource, all data should be serialized - loadFromXml( producer, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); - - } - mlt_properties_set_data( producer_props, "qscene", scene, 0, ( mlt_destructor )qscene_delete, NULL ); - } - - QRectF start = stringToRect( QString( mlt_properties_get( producer_props, "_startrect" ) ) ); - QRectF end = stringToRect( QString( mlt_properties_get( producer_props, "_endrect" ) ) ); - - int originalWidth = mlt_properties_get_int( producer_props, "_original_width" ); - int originalHeight= mlt_properties_get_int( producer_props, "_original_height" ); - const QRectF source( 0, 0, width, height ); - if (start.isNull()) { - start = QRectF( 0, 0, originalWidth, originalHeight ); - } - - // Effects - QList items = scene->items(); - QGraphicsTextItem *titem = NULL; - for (int i = 0; i < items.count(); i++) { - titem = static_cast ( items.at( i ) ); - if (titem && !titem->data( 0 ).isNull()) { - QStringList params = titem->data( 0 ).toStringList(); - if (params.at( 0 ) == "typewriter" ) { - // typewriter effect has 2 param values: - // the keystroke delay and a start offset, both in frames - QStringList values = params.at( 2 ).split( ";" ); - int interval = qMax( 0, ( ( int ) position - values.at( 1 ).toInt()) / values.at( 0 ).toInt() ); - QTextCursor cursor = titem->textCursor(); - cursor.movePosition(QTextCursor::EndOfBlock); - // get the font format - QTextCharFormat format = cursor.charFormat(); - cursor.select(QTextCursor::Document); - QString txt = params.at( 1 ).left( interval ); - // If the string to insert is empty, insert a space / linebreak so that we don't loose - // formatting infos for the next iterations - int lines = params.at( 1 ).count( '\n' ); - QString empty = " "; - for (int i = 0; i < lines; i++) - empty.append( "\n " ); - cursor.insertText( txt.isEmpty() ? empty : txt, format ); - if ( !titem->data( 1 ).isNull() ) - titem->setTextWidth( titem->data( 1 ).toDouble() ); - } - } - } - - //must be extracted from kdenlive title - QImage img( width, height, QImage::Format_ARGB32 ); - img.fill( 0 ); - QPainter p1; - p1.begin( &img ); - p1.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); - //| QPainter::SmoothPixmapTransform ); - mlt_position anim_out = mlt_properties_get_position( producer_props, "_animation_out" ); - - if (end.isNull()) - { - scene->render( &p1, source, start, Qt::IgnoreAspectRatio ); - } - else if ( position > anim_out ) { - scene->render( &p1, source, end, Qt::IgnoreAspectRatio ); - } - else { - double percentage = 0; - if ( position && anim_out ) - percentage = position / anim_out; - QPointF topleft = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage; - QPointF bottomRight = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage; - const QRectF r1( topleft, bottomRight ); - scene->render( &p1, source, r1, Qt::IgnoreAspectRatio ); - if ( profile && !profile->progressive ){ - int line=0; - double percentage_next_filed = ( position + 0.5 ) / anim_out; - QPointF topleft_next_field = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage_next_filed; - QPointF bottomRight_next_field = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage_next_filed; - const QRectF r2( topleft_next_field, bottomRight_next_field ); - QImage img1( width, height, QImage::Format_ARGB32 ); - img1.fill( 0 ); - QPainter p2; - p2.begin(&img1); - p2.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); - scene->render(&p2,source,r2, Qt::IgnoreAspectRatio ); - p2.end(); - int next_field_line = ( mlt_properties_get_int( producer_props, "top_field_first" ) ? 1 : 0 ); - for (line = next_field_line ;linecurrent_image = ( uint8_t * )mlt_pool_alloc( size ); - uint8_t *dst = self->current_image; - - for ( int i = 0; i < width * height * 4; i += 4 ) - { - *dst++=qRed( *src ); - *dst++=qGreen( *src ); - *dst++=qBlue( *src ); - *dst++=qAlpha( *src ); - src++; - } - - mlt_properties_set_data( producer_props, "cached_image", self->current_image, size, mlt_pool_release, NULL ); - self->current_width = width; - self->current_height = height; - } - - pthread_mutex_unlock( &self->mutex ); - mlt_properties_set_int( properties, "width", self->current_width ); - mlt_properties_set_int( properties, "height", self->current_height ); -} - - diff -Nru mlt-0.9.0/src/modules/qimage/kdenlivetitle_wrapper.h mlt-0.9.2/src/modules/qimage/kdenlivetitle_wrapper.h --- mlt-0.9.0/src/modules/qimage/kdenlivetitle_wrapper.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/kdenlivetitle_wrapper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * kdenlivetitle_wrapper.h -- kdenlivetitle wrapper - * Copyright (c) 2009 Marco Gittler - * Copyright (c) 2009 Jean-Baptiste Mardelle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - - -struct producer_ktitle_s -{ - struct mlt_producer_s parent; - uint8_t *current_image; - int current_width; - int current_height; - pthread_mutex_t mutex; -}; - -typedef struct producer_ktitle_s *producer_ktitle; - -extern void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int, int, double, int ); - - -#ifdef __cplusplus -} -#endif - - - diff -Nru mlt-0.9.0/src/modules/qimage/Makefile mlt-0.9.2/src/modules/qimage/Makefile --- mlt-0.9.0/src/modules/qimage/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -CFLAGS += -I../.. - -LDFLAGS += -L../../framework -lmlt -lpthread -lm -L../../mlt++ -lmlt++ - -include ../../../config.mak -include config.mak - -TARGET = ../libmltqimage$(LIBSUF) - -OBJS = factory.o producer_qimage.o producer_kdenlivetitle.o -CPPOBJS = qimage_wrapper.o \ - kdenlivetitle_wrapper.o \ - consumer_qglsl.o - -ifdef GPL3 - CPPOBJS += transition_vqm.o - CFLAGS += -DGPL3 -endif - -CXXFLAGS += $(CFLAGS) $(QTCXXFLAGS) $(EXIFCXXFLAGS) $(KDECXXFLAGS) -Wno-deprecated - -LDFLAGS += $(QTLIBS) $(EXIFLIBS) $(KDELIBS) - -ifdef USE_KDE3 -LDFLAGS += -lkio -endif - -SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) - -all: $(TARGET) - -$(TARGET): $(OBJS) $(CPPOBJS) - $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) - -depend: $(SRCS) - $(CXX) -MM $(CXXFLAGS) $^ 1>.depend - -distclean: clean - rm -f .depend config.h config.mak - -clean: - rm -f $(OBJS) $(TARGET) $(CPPOBJS) - -install: all - install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" - install -d "$(DESTDIR)$(mltdatadir)/qimage" - install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/qimage" - -ifneq ($(wildcard .depend),) -include .depend -endif diff -Nru mlt-0.9.0/src/modules/qimage/producer_kdenlivetitle.c mlt-0.9.2/src/modules/qimage/producer_kdenlivetitle.c --- mlt-0.9.0/src/modules/qimage/producer_kdenlivetitle.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/producer_kdenlivetitle.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -/* - * producer_kdenlivetitle.c -- kdenlive producer - * Copyright (c) 2009 Marco Gittler - * Copyright (c) 2009 Jean-Baptiste Mardelle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "kdenlivetitle_wrapper.h" - -#include -#include -#include - - -void read_xml(mlt_properties properties) -{ - FILE *f = fopen( mlt_properties_get( properties, "resource" ), "r"); - if ( f != NULL ) - { - int size = 0; - long lSize; - - if ( fseek (f , 0 , SEEK_END) < 0 ) - goto error; - lSize = ftell (f); - if ( lSize <= 0 ) - goto error; - rewind (f); - - char *infile = (char*) mlt_pool_alloc(lSize); - if ( infile ) - { - size = fread(infile,1,lSize,f); - if ( size ) - { - infile[size] = '\0'; - mlt_properties_set(properties, "_xmldata", infile); - } - mlt_pool_release( infile ); - } -error: - fclose(f); - } -} - -static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) - -{ - /* Obtain properties of frame */ - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - - /* Obtain the producer for this frame */ - producer_ktitle this = mlt_properties_get_data( properties, "producer_kdenlivetitle", NULL ); - - /* Obtain properties of producer */ - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &this->parent ); - - *width = mlt_properties_get_int( properties, "rescale_width" ); - *height = mlt_properties_get_int( properties, "rescale_height" ); - - mlt_service_lock( MLT_PRODUCER_SERVICE( &this->parent ) ); - - /* Allocate the image */ - *format = mlt_image_rgb24a; - if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { - if ( mlt_properties_get_int( producer_props, "force_reload" ) > 1 ) read_xml( producer_props ); - mlt_properties_set_int( producer_props, "force_reload", 0 ); - drawKdenliveTitle( this, frame, *width, *height, mlt_frame_original_position( frame ), 1); - } - else drawKdenliveTitle( this, frame, *width, *height, mlt_frame_original_position( frame ), 0); - - // Get width and height (may have changed during the refresh) - *width = mlt_properties_get_int( properties, "width" ); - *height = mlt_properties_get_int( properties, "height" ); - - if ( this->current_image ) - { - // Clone the image and the alpha - int image_size = this->current_width * ( this->current_height ) * 4; - uint8_t *image_copy = mlt_pool_alloc( image_size ); - memcpy( image_copy, this->current_image, image_size ); - // Now update properties so we free the copy after - mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); - // We're going to pass the copy on - *buffer = image_copy; - - mlt_log_debug( MLT_PRODUCER_SERVICE( &this->parent ), "width:%d height:%d %s\n", *width, *height, mlt_image_format_name( *format ) ); - } - - mlt_service_unlock( MLT_PRODUCER_SERVICE( &this->parent ) ); - - return 0; -} - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) - -{ - // Get the real structure for this producer - producer_ktitle this = producer->child; - - /* Generate a frame */ - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - - if ( *frame != NULL ) - { - /* Obtain properties of frame and producer */ - mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); - - /* Obtain properties of producer */ - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - - /* Set the producer on the frame properties */ - mlt_properties_set_data( properties, "producer_kdenlivetitle", this, 0, NULL, NULL ); - - /* Update timecode on the frame we're creating */ - mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - - /* Set producer-specific frame properties */ - mlt_properties_pass_list( properties, producer_props, "progressive, aspect_ratio" ); - - /* Push the get_image method */ - mlt_frame_push_get_image( *frame, producer_get_image ); - } - - /* Calculate the next timecode */ - mlt_producer_prepare_next( producer ); - - return 0; -} - -static void producer_close( mlt_producer producer ) -{ - /* fprintf(stderr, ":::::::::::::: CLOSING TITLE\n"); */ - producer->close = NULL; - mlt_producer_close( producer ); - free( producer ); -} - - -mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) -{ - /* fprintf(stderr, ":::::::::::: CREATE TITLE\n"); */ - /* Create a new producer object */ - - producer_ktitle this = calloc( 1, sizeof( struct producer_ktitle_s ) ); - if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) - { - mlt_producer producer = &this->parent; - - /* Get the properties interface */ - mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); - /* Callback registration */ - producer->get_frame = producer_get_frame; - producer->close = ( mlt_destructor )producer_close; - mlt_properties_set( properties, "resource", filename ); - mlt_properties_set_int( properties, "progressive", 1 ); - read_xml(properties); - return producer; - } - free( this ); - return NULL; -} - diff -Nru mlt-0.9.0/src/modules/qimage/producer_kdenlivetitle.yml mlt-0.9.2/src/modules/qimage/producer_kdenlivetitle.yml --- mlt-0.9.0/src/modules/qimage/producer_kdenlivetitle.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/producer_kdenlivetitle.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -schema_version: 0.1 -type: producer -identifier: kdenlivetitle -title: Kdenlive Titler -version: 1 -copyright: Marco Gittler, Jean-Baptiste Mardelle -creator: Marco Gittler, Jean-Baptiste Mardelle -license: LGPLv2.1 -language: en -tags: - - Video diff -Nru mlt-0.9.0/src/modules/qimage/producer_qimage.c mlt-0.9.2/src/modules/qimage/producer_qimage.c --- mlt-0.9.0/src/modules/qimage/producer_qimage.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/producer_qimage.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,352 +0,0 @@ -/* - * producer_image.c -- a QT/QImage based producer for MLT - * Copyright (C) 2006 Visual Media - * Author: Charles Yates - * - * NB: This module is designed to be functionally equivalent to the - * gtk2 image loading module so it can be used as replacement. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "qimage_wrapper.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -static void load_filenames( producer_qimage self, mlt_properties producer_properties ); -static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); -static void producer_close( mlt_producer parent ); - -mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) -{ - producer_qimage self = calloc( 1, sizeof( struct producer_qimage_s ) ); - if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) - { - mlt_producer producer = &self->parent; - - // Get the properties interface - mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); - - // Initialize KDE image plugins - init_qimage(); - - // Callback registration - producer->get_frame = producer_get_frame; - producer->close = ( mlt_destructor )producer_close; - - // Set the default properties - mlt_properties_set( properties, "resource", filename ); - mlt_properties_set_int( properties, "ttl", 25 ); - mlt_properties_set_int( properties, "aspect_ratio", 1 ); - mlt_properties_set_int( properties, "progressive", 1 ); - mlt_properties_set_int( properties, "seekable", 1 ); - - // Validate the resource - if ( filename ) - load_filenames( self, properties ); - if ( self->count ) - { - mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - if ( frame ) - { - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); - mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL ); - mlt_frame_set_position( frame, mlt_producer_position( producer ) ); - refresh_qimage( self, frame ); - mlt_cache_item_close( self->qimage_cache ); - mlt_frame_close( frame ); - } - } - if ( self->current_width == 0 ) - { - producer_close( producer ); - producer = NULL; - } - return producer; - } - free( self ); - return NULL; -} - -static int load_svg( producer_qimage self, mlt_properties properties, const char *filename ) -{ - int result = 0; - - // Read xml string - if ( strstr( filename, "filenames, key, full ); - gap = 0; - } - else - { - gap ++; - } - } - if ( mlt_properties_count( self->filenames ) > 0 ) - { - mlt_properties_set_int( properties, "ttl", 1 ); - result = 1; - } - } - return result; -} - -static int load_sequence_deprecated( producer_qimage self, mlt_properties properties, const char *filename ) -{ - int result = 0; - const char *start; - - // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png - if ( ( start = strchr( filename, '%' ) ) ) - { - const char *end = ++start; - while ( isdigit( *end ) ) end++; - if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) ) - { - int n = end - start; - char *s = calloc( 1, n + 1 ); - strncpy( s, start, n ); - mlt_properties_set( properties, "begin", s ); - free( s ); - s = calloc( 1, strlen( filename ) + 2 ); - strncpy( s, filename, start - filename ); - sprintf( s + ( start - filename ), ".%d%s", n, end ); - result = load_sequence_sprintf( self, properties, s ); - free( s ); - } - } - return result; -} - -static int load_sequence_querystring( producer_qimage self, mlt_properties properties, const char *filename ) -{ - int result = 0; - - // Obtain filenames with pattern and begin value in query string - if ( strchr( filename, '%' ) && strchr( filename, '?' ) ) - { - // Split filename into pattern and query string - char *s = strdup( filename ); - char *querystring = strrchr( s, '?' ); - *querystring++ = '\0'; - if ( strstr( filename, "begin=" ) ) - mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 ); - else if ( strstr( filename, "begin:" ) ) - mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 ); - // Coerce to an int value so serialization does not have any extra query string cruft - mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) ); - result = load_sequence_sprintf( self, properties, s ); - free( s ); - } - return result; -} - -static int load_folder( producer_qimage self, mlt_properties properties, const char *filename ) -{ - int result = 0; - - // Obtain filenames within folder - if ( strstr( filename, "/.all." ) != NULL ) - { - char wildcard[ 1024 ]; - char *dir_name = strdup( filename ); - char *extension = strrchr( dir_name, '.' ); - - *( strstr( dir_name, "/.all." ) + 1 ) = '\0'; - sprintf( wildcard, "*%s", extension ); - - mlt_properties_dir_list( self->filenames, dir_name, wildcard, 1 ); - - free( dir_name ); - result = 1; - } - return result; -} - -static void load_filenames( producer_qimage self, mlt_properties properties ) -{ - char *filename = mlt_properties_get( properties, "resource" ); - self->filenames = mlt_properties_new( ); - - if (!load_svg( self, properties, filename ) && - !load_sequence_querystring( self, properties, filename ) && - !load_sequence_sprintf( self, properties, filename ) && - !load_sequence_deprecated( self, properties, filename ) && - !load_folder( self, properties, filename ) ) - { - mlt_properties_set( self->filenames, "0", filename ); - } - self->count = mlt_properties_count( self->filenames ); -} - -static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) -{ - int error = 0; - - // Obtain properties of frame and producer - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - producer_qimage self = mlt_properties_get_data( properties, "producer_qimage", NULL ); - mlt_producer producer = &self->parent; - - *width = mlt_properties_get_int( properties, "rescale_width" ); - *height = mlt_properties_get_int( properties, "rescale_height" ); - - mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) ); - - // Refresh the image - self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); - self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); - self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); - self->current_image = mlt_cache_item_data( self->image_cache, NULL ); - self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); - self->current_alpha = mlt_cache_item_data( self->alpha_cache, NULL ); - refresh_image( self, frame, *format, *width, *height ); - - // Get width and height (may have changed during the refresh) - *width = mlt_properties_get_int( properties, "width" ); - *height = mlt_properties_get_int( properties, "height" ); - *format = self->format; - - // NB: Cloning is necessary with this producer (due to processing of images ahead of use) - // The fault is not in the design of mlt, but in the implementation of the qimage producer... - if ( self->current_image ) - { - // Clone the image and the alpha - int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); - uint8_t *image_copy = mlt_pool_alloc( image_size ); - memcpy( image_copy, self->current_image, image_size ); - // Now update properties so we free the copy after - mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); - // We're going to pass the copy on - *buffer = image_copy; - mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n", - self->current_width, self->current_height, mlt_image_format_name( *format ) ); - // Clone the alpha channel - if ( self->current_alpha ) - { - image_copy = mlt_pool_alloc( self->current_width * self->current_height ); - memcpy( image_copy, self->current_alpha, self->current_width * self->current_height ); - mlt_frame_set_alpha( frame, image_copy, self->current_width * self->current_height, mlt_pool_release ); - } - } - else - { - error = 1; - } - - // Release references and locks - mlt_cache_item_close( self->qimage_cache ); - mlt_cache_item_close( self->image_cache ); - mlt_cache_item_close( self->alpha_cache ); - mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); - - return error; -} - -static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) -{ - // Get the real structure for this producer - producer_qimage self = producer->child; - - // Fetch the producers properties - mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); - - if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL ) - load_filenames( self, producer_properties ); - - // Generate a frame - *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); - - if ( *frame != NULL && self->count > 0 ) - { - // Obtain properties of frame and producer - mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); - - // Set the producer on the frame properties - mlt_properties_set_data( properties, "producer_qimage", self, 0, NULL, NULL ); - - // Update timecode on the frame we're creating - mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - - // Refresh the image - self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); - self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); - refresh_qimage( self, *frame ); - mlt_cache_item_close( self->qimage_cache ); - - // Set producer-specific frame properties - mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) ); - double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); - if ( force_ratio > 0.0 ) - mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); - else - mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) ); - - // Push the get_image method - mlt_frame_push_get_image( *frame, producer_get_image ); - } - - // Calculate the next timecode - mlt_producer_prepare_next( producer ); - - return 0; -} - -static void producer_close( mlt_producer parent ) -{ - producer_qimage self = parent->child; - parent->close = NULL; - mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); - mlt_producer_close( parent ); - mlt_properties_close( self->filenames ); - free( self ); -} diff -Nru mlt-0.9.0/src/modules/qimage/producer_qimage.yml mlt-0.9.2/src/modules/qimage/producer_qimage.yml --- mlt-0.9.0/src/modules/qimage/producer_qimage.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/producer_qimage.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -schema_version: 0.1 -type: producer -identifier: qimage -title: Qt QImage -version: 2 -copyright: Visual Media ? -creator: Charles Yates -license: GPLv2 -language: en -tags: - - Video -description: > - A still graphics to video generator using Qt QImage -notes: > - QImage has builtin scaling. It will rescale the originally rendered title to - whatever the consumer requests. Therefore, it will lose its aspect ratio if - so requested, and it is up to the consumer to request a proper width and - height that maintains the image aspect. -parameters: - - identifier: argument - title: File - type: string - description: > - The name of a graphics file loadable by Qt. - - If "%" in filename, the filename is used with sprintf to generate a - filename from a counter for multi-file/flipbook animation. The file - sequence ends when numeric discontinuity exceeds 100. - - If the file sequence does not begin within the count of 100 you - can pass the begin property like a query string parameter, for - example: anim-%04d.png?begin=1000. - - If filename contains "/.all.", suffix with an extension to load all - pictures with matching extension from a directory. - - If filename contains the string " - Reload the file instead of using its cached image. This property - automatically resets itself once it has been set 1 and processed. - minimum: 0 - maximum: 1 - mutable: yes - - - identifier: disable_exif - title: Disable auto-rotation - type: integer - minimum: 0 - maximum: 1 - widget: checkbox - - - identifier: force_aspect_ratio - title: Sample aspect ratio - type: float - description: Optionally override a (mis)detected aspect ratio - mutable: yes - diff -Nru mlt-0.9.0/src/modules/qimage/qimage_wrapper.cpp mlt-0.9.2/src/modules/qimage/qimage_wrapper.cpp --- mlt-0.9.0/src/modules/qimage/qimage_wrapper.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/qimage_wrapper.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,403 +0,0 @@ -/* - * qimage_wrapper.cpp -- a QT/QImage based producer for MLT - * Copyright (C) 2006 Visual Media - * Author: Charles Yates - * - * NB: This module is designed to be functionally equivalent to the - * gtk2 image loading module so it can be used as replacement. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "qimage_wrapper.h" - -#ifdef USE_QT3 -#include -#include - -#ifdef USE_KDE3 -#include -#include -#endif -#endif - -#ifdef USE_KDE4 -#include -#endif - -#ifdef USE_QT4 -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef USE_EXIF -#include -#endif - -#include -#include - -extern "C" { - -#include -#include - -#ifdef USE_KDE4 -static KComponentData *instance = 0L; -#elif USE_KDE3 -static KInstance *instance = 0L; -#endif - -static QApplication *app = NULL; - -static void qimage_delete( void *data ) -{ - QImage *image = ( QImage * )data; - delete image; - image = NULL; -#if defined(USE_KDE3) || defined(USE_KDE4) - if (instance) delete instance; - instance = 0L; -#endif - -} - -void init_qimage() -{ -#ifdef USE_KDE4 - if ( !instance ) { - instance = new KComponentData( "qimage_prod" ); - } -#elif defined(USE_KDE3) - if ( !instance ) { - instance = new KInstance( "qimage_prod" ); - KImageIO::registerFormats(); - } -#endif - -} - -static QImage* reorient_with_exif( producer_qimage self, int image_idx, QImage *qimage ) -{ -#ifdef USE_EXIF - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent ); - ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) ); - ExifEntry *entry; - int exif_orientation = 0; - /* get orientation and rotate image accordingly if necessary */ - if (d) { - if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) ) - exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d)); - - /* Free the EXIF data */ - exif_data_unref(d); - } - - // Remember EXIF value, might be useful for someone - mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation ); - - if ( exif_orientation > 1 ) - { - // Rotate image according to exif data - QImage processed; - QMatrix matrix; - - switch ( exif_orientation ) { - case 2: - matrix.scale( -1, 1 ); - break; - case 3: - matrix.rotate( 180 ); - break; - case 4: - matrix.scale( 1, -1 ); - break; - case 5: - matrix.rotate( 270 ); - matrix.scale( -1, 1 ); - break; - case 6: - matrix.rotate( 90 ); - break; - case 7: - matrix.rotate( 90 ); - matrix.scale( -1, 1 ); - break; - case 8: - matrix.rotate( 270 ); - break; - } - processed = qimage->transformed( matrix ); - delete qimage; - qimage = new QImage( processed ); - } -#endif - return qimage; -} - -int refresh_qimage( producer_qimage self, mlt_frame frame ) -{ - // Obtain properties of frame and producer - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_producer producer = &self->parent; - mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); - - // Check if user wants us to reload the image - if ( mlt_properties_get_int( producer_props, "force_reload" ) ) - { - self->qimage = NULL; - self->current_image = NULL; - mlt_properties_set_int( producer_props, "force_reload", 0 ); - } - - // Get the time to live for each frame - double ttl = mlt_properties_get_int( producer_props, "ttl" ); - - // Get the original position of this frame - mlt_position position = mlt_frame_original_position( frame ); - position += mlt_producer_get_in( producer ); - - // Image index - int image_idx = ( int )floor( ( double )position / ttl ) % self->count; - - // Key for the cache - char image_key[ 10 ]; - sprintf( image_key, "%d", image_idx ); - - int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); - - - if ( app == NULL ) - { - if ( qApp ) - { - app = qApp; - } - else - { -#ifdef linux - if ( getenv("DISPLAY") == 0 ) - { - mlt_log_panic( MLT_PRODUCER_SERVICE( producer ), "Error, cannot render titles without an X11 environment.\nPlease either run melt from an X session or use a fake X server like xvfb:\nxvfb-run -a melt (...)\n" ); - return -1; - } -#endif - int argc = 1; - char* argv[1]; - argv[0] = (char*) "xxx"; - app = new QApplication( argc, argv ); - const char *localename = mlt_properties_get_lcnumeric( MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) ) ); - QLocale::setDefault( QLocale( localename ) ); - } - } - - if ( image_idx != self->qimage_idx ) - self->qimage = NULL; - if ( !self->qimage || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) - { - self->current_image = NULL; - QImage *qimage = new QImage( QString::fromUtf8( mlt_properties_get_value( self->filenames, image_idx ) ) ); - self->qimage = qimage; - - if ( !qimage->isNull( ) ) - { - // Read the exif value for this file - if ( !disable_exif ) - qimage = reorient_with_exif( self, image_idx, qimage ); - - // Register qimage for destruction and reuse - mlt_cache_item_close( self->qimage_cache ); - mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); - self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); - self->qimage_idx = image_idx; - - // Store the width/height of the qimage - self->current_width = qimage->width( ); - self->current_height = qimage->height( ); - - mlt_events_block( producer_props, NULL ); - mlt_properties_set_int( producer_props, "meta.media.width", self->current_width ); - mlt_properties_set_int( producer_props, "meta.media.height", self->current_height ); - mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); - mlt_events_unblock( producer_props, NULL ); - } - else - { - delete qimage; - self->qimage = NULL; - } - } - - // Set width/height of frame - mlt_properties_set_int( properties, "width", self->current_width ); - mlt_properties_set_int( properties, "height", self->current_height ); - - return image_idx; -} - -void refresh_image( producer_qimage self, mlt_frame frame, mlt_image_format format, int width, int height ) -{ - // Obtain properties of frame and producer - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_producer producer = &self->parent; - - // Get index and qimage - int image_idx = refresh_qimage( self, frame ); - - // optimization for subsequent iterations on single pictur - if ( image_idx != self->image_idx || width != self->current_width || height != self->current_height ) - self->current_image = NULL; - - // If we have a qimage and need a new scaled image - if ( self->qimage && ( !self->current_image || ( format != mlt_image_none && format != self->format ) ) ) - { - char *interps = mlt_properties_get( properties, "rescale.interp" ); - int interp = 0; - QImage *qimage = static_cast( self->qimage ); - - // QImage has two scaling modes - we'll toggle between them here - if ( strcmp( interps, "tiles" ) == 0 - || strcmp( interps, "hyper" ) == 0 - || strcmp( interps, "bicubic" ) == 0 ) - interp = 1; - -#ifdef USE_QT4 - // Note - the original qimage is already safe and ready for destruction - if ( qimage->depth() == 1 ) - { - QImage temp = qimage->convertToFormat( QImage::Format_RGB32 ); - delete qimage; - qimage = new QImage( temp ); - self->qimage = qimage; - } - QImage scaled = interp == 0 ? qimage->scaled( QSize( width, height ) ) : - qimage->scaled( QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); - int has_alpha = scaled.hasAlphaChannel(); -#endif - -#ifdef USE_QT3 - // Note - the original qimage is already safe and ready for destruction - QImage scaled = interp == 0 ? qimage->scale( width, height, QImage::ScaleFree ) : - qimage->smoothScale( width, height, QImage::ScaleFree ); - self->has_alpha = 1; -#endif - - // Store width and height - self->current_width = width; - self->current_height = height; - - // Allocate/define image - int dst_stride = width * ( has_alpha ? 4 : 3 ); - int image_size = dst_stride * ( height + 1 ); - self->current_image = ( uint8_t * )mlt_pool_alloc( image_size ); - self->current_alpha = NULL; - self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24; - - // Copy the image - int y = self->current_height + 1; - uint8_t *dst = self->current_image; - while ( --y ) - { - QRgb *src = (QRgb*) scaled.scanLine( self->current_height - y ); - int x = self->current_width + 1; - while ( --x ) - { - *dst++ = qRed(*src); - *dst++ = qGreen(*src); - *dst++ = qBlue(*src); - if ( has_alpha ) *dst++ = qAlpha(*src); - ++src; - } - } - - // Convert image to requested format - if ( format != mlt_image_none && format != self->format ) - { - uint8_t *buffer = NULL; - - // First, set the image so it can be converted when we get it - mlt_frame_replace_image( frame, self->current_image, self->format, width, height ); - mlt_frame_set_image( frame, self->current_image, image_size, mlt_pool_release ); - self->format = format; - - // get_image will do the format conversion - mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 ); - - // cache copies of the image and alpha buffers - if ( buffer ) - { - image_size = mlt_image_format_size( format, width, height, NULL ); - self->current_image = (uint8_t*) mlt_pool_alloc( image_size ); - memcpy( self->current_image, buffer, image_size ); - } - if ( ( buffer = mlt_frame_get_alpha_mask( frame ) ) ) - { - self->current_alpha = (uint8_t*) mlt_pool_alloc( width * height ); - memcpy( self->current_alpha, buffer, width * height ); - } - } - - // Update the cache - mlt_cache_item_close( self->image_cache ); - mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.image", self->current_image, image_size, mlt_pool_release ); - self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); - self->image_idx = image_idx; - mlt_cache_item_close( self->alpha_cache ); - self->alpha_cache = NULL; - if ( self->current_alpha ) - { - mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha", self->current_alpha, width * height, mlt_pool_release ); - self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); - } - } - - // Set width/height of frame - mlt_properties_set_int( properties, "width", self->current_width ); - mlt_properties_set_int( properties, "height", self->current_height ); -} - -extern void make_tempfile( producer_qimage self, const char *xml ) -{ - // Generate a temporary file for the svg - QTemporaryFile tempFile( "mlt.XXXXXX" ); - - tempFile.setAutoRemove( false ); - if ( tempFile.open() ) - { - // Write the svg into the temp file - char *fullname = tempFile.fileName().toUtf8().data(); - - // Strip leading crap - while ( xml[0] != '<' ) - xml++; - - qint64 remaining_bytes = strlen( xml ); - while ( remaining_bytes > 0 ) - remaining_bytes -= tempFile.write( xml + strlen( xml ) - remaining_bytes, remaining_bytes ); - tempFile.close(); - - mlt_properties_set( self->filenames, "0", fullname ); - - mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( &self->parent ), "__temporary_file__", - fullname, 0, ( mlt_destructor )unlink, NULL ); - } -} - -} // extern "C" diff -Nru mlt-0.9.0/src/modules/qimage/qimage_wrapper.h mlt-0.9.2/src/modules/qimage/qimage_wrapper.h --- mlt-0.9.0/src/modules/qimage/qimage_wrapper.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/qimage_wrapper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * qimage_wrapper.h -- a QT/QImage based producer for MLT - * Copyright (C) 2006 Visual Media - * Author: Charles Yates - * - * NB: This module is designed to be functionally equivalent to the - * gtk2 image loading module so it can be used as replacement. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef MLT_QIMAGE_WRAPPER -#define MLT_QIMAGE_WRAPPER - -#include - -#include "config.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct producer_qimage_s -{ - struct mlt_producer_s parent; - mlt_properties filenames; - int count; - int image_idx; - int qimage_idx; - uint8_t *current_image; - uint8_t *current_alpha; - int current_width; - int current_height; - mlt_cache_item image_cache; - mlt_cache_item alpha_cache; - mlt_cache_item qimage_cache; - void *qimage; - mlt_image_format format; -}; - -typedef struct producer_qimage_s *producer_qimage; - -extern int refresh_qimage( producer_qimage self, mlt_frame frame ); -extern void refresh_image( producer_qimage, mlt_frame, mlt_image_format, int width, int height ); -extern void make_tempfile( producer_qimage, const char *xml ); -extern void init_qimage(); - - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru mlt-0.9.0/src/modules/qimage/transition_vqm.cpp mlt-0.9.2/src/modules/qimage/transition_vqm.cpp --- mlt-0.9.0/src/modules/qimage/transition_vqm.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/transition_vqm.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,253 +0,0 @@ -/* - * transition_vqm.c -- video quality measurement - * Copyright (c) 2012 Dan Dennedy - * Core psnr and ssim routines based on code from - * qsnr (C) 2010 E. Oriani, ema fastwebnet it - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - */ - -#include -#include -#include -#include -#include - -static QApplication *app = 0; - -static double calc_psnr( const uint8_t *a, const uint8_t *b, int size, int bpp ) -{ - double mse = 0.0; - int n = size + 1; - - while ( --n ) - { - int diff = *a - *b; - mse += diff * diff; - a += bpp; - b += bpp; - } - - return 10.0 * log10( 255.0 * 255.0 / ( mse == 0 ? 1e-10 : mse/size ) ); -} - -static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp ) -{ - int windows_x = width / window_size; - int windows_y = height / window_size; - double avg = 0.0; - - if ( !windows_x || !windows_y ) - return 0.0; - - // for each window - for ( int y = 0; y < windows_y; ++y ) - for ( int x = 0; x < windows_x; ++x ) - { - int base_offset = x * window_size + y * window_size * width; - double ref_acc = 0.0, - ref_acc_2 = 0.0, - cmp_acc = 0.0, - cmp_acc_2 = 0.0, - ref_cmp_acc = 0.0; - - // accumulate the pixel values for this window - for ( int j = 0; j < window_size; ++j ) - for ( int i = 0; i < window_size; ++i ) - { - uint8_t c_a = a[bpp * (base_offset + j * width + i)]; - uint8_t c_b = b[bpp * (base_offset + j * width + i)]; - ref_acc += c_a; - ref_acc_2 += c_a * c_a; - cmp_acc += c_b; - cmp_acc_2 += c_b * c_b; - ref_cmp_acc += c_a * c_b; - } - - // compute the SSIM for this window - // http://en.wikipedia.org/wiki/SSIM - // http://en.wikipedia.org/wiki/Variance - // http://en.wikipedia.org/wiki/Covariance - double n_samples = window_size * window_size, - ref_avg = ref_acc / n_samples, - ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg, - cmp_avg = cmp_acc / n_samples, - cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg, - ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg, - c1 = 6.5025, // (0.01*255.0)^2 - c2 = 58.5225, // (0.03*255)^2 - ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2), - ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2); - - // accumulate the SSIM - avg += ssim_num / ssim_den; - } - - // return the average SSIM - return avg / windows_x / windows_y; -} - -static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - mlt_frame b_frame = mlt_frame_pop_frame( a_frame ); - mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame ); - mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) ); - uint8_t *b_image; - int window_size = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "window_size" ); - double psnr[3], ssim[3]; - - *format = mlt_image_yuv422; - mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); - mlt_frame_get_image( a_frame, image, format, width, height, writable ); - - psnr[0] = calc_psnr( *image, b_image, *width * *height, 2 ); - psnr[1] = calc_psnr( *image + 1, b_image + 1, *width * *height / 2, 4 ); - psnr[2] = calc_psnr( *image + 3, b_image + 3, *width * *height / 2, 4 ); - ssim[0] = calc_ssim( *image, b_image, *width, *height, window_size, 2 ); - ssim[1] = calc_ssim( *image + 1, b_image + 1, *width / 2, *height, window_size, 4 ); - ssim[2] = calc_ssim( *image + 3, b_image + 3, *width / 2, *height, window_size, 4 ); - mlt_properties_set_double( properties, "meta.vqm.psnr.y", psnr[0] ); - mlt_properties_set_double( properties, "meta.vqm.psnr.cb", psnr[1] ); - mlt_properties_set_double( properties, "meta.vqm.psnr.cr", psnr[2] ); - mlt_properties_set_double( properties, "meta.vqm.ssim.y", ssim[0] ); - mlt_properties_set_double( properties, "meta.vqm.ssim.cb", ssim[1] ); - mlt_properties_set_double( properties, "meta.vqm.ssim.cr", ssim[2] ); - printf( "%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n", - mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], - ssim[0], ssim[1], ssim[2] ); - - // copy the B frame to the bottom of the A frame for comparison - window_size = mlt_image_format_size( *format, *width, *height, NULL ) / 2; - memcpy( *image + window_size, b_image + window_size, window_size ); - - if ( !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "render" ) ) - return 0; - - // get RGBA image for Qt drawing - *format = mlt_image_rgb24a; - mlt_frame_get_image( a_frame, image, format, width, height, 1 ); - - // convert mlt image to qimage - QImage img( *width, *height, QImage::Format_ARGB32 ); - int y = *height + 1; - uint8_t *src = *image; - while ( --y ) - { - QRgb *dst = (QRgb*) img.scanLine( *height - y ); - int x = *width + 1; - while ( --x ) - { - *dst++ = qRgba( src[0], src[1], src[2], 255 ); - src += 4; - } - } - - // create QApplication, if needed - if ( !app ) - { - if ( qApp ) - { - app = qApp; - } - else - { - int argc = 1; - char* argv[] = { strdup( "unknown" ) }; - - app = new QApplication( argc, argv ); - const char *localename = mlt_properties_get_lcnumeric( MLT_TRANSITION_PROPERTIES(transition) ); - QLocale::setDefault( QLocale( localename ) ); - free( argv[0] ); - } - } - - // setup Qt drawing - QPainter painter; - painter.begin( &img ); - painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); - - // draw some stuff with Qt - QPalette palette; - QFont font; - QString s; - font.setBold( true ); - font.setPointSize( 30 * *height / 1080 ); - painter.setPen( QColor("black") ); - painter.drawLine( 0, *height/2 + 1, *width, *height/2 ); - painter.setPen( QColor("white") ); - painter.drawLine( 0, *height/2 - 1, *width, *height/2 ); - painter.setFont( font ); - s.sprintf( "Frame: %05d\nPSNR: %05.2f (Y) %05.2f (Cb) %05.2f (Cr)\nSSIM: %5.3f (Y) %5.3f (Cb) %5.3f (Cr)", - mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], - ssim[0], ssim[1], ssim[2] ); - painter.setPen( QColor("black") ); - painter.drawText( 52, *height * 8 / 10 + 2, *width, *height, 0, s ); - painter.setPen( QColor("white") ); - painter.drawText( 50, *height * 8 / 10, *width, *height, 0, s ); - - // finish Qt drawing - painter.end(); - window_size = mlt_image_format_size( *format, *width, *height, NULL ); - uint8_t *dst = (uint8_t *) mlt_pool_alloc( window_size ); - mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), "image", dst, window_size, mlt_pool_release, NULL ); - *image = dst; - - // convert qimage to mlt - y = *height + 1; - while ( --y ) - { - QRgb *src = (QRgb*) img.scanLine( *height - y ); - int x = *width + 1; - while ( --x ) - { - *dst++ = qRed( *src ); - *dst++ = qGreen( *src ); - *dst++ = qBlue( *src ); - *dst++ = qAlpha( *src ); - src++; - } - } - - return 0; -} - -static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) -{ - mlt_frame_push_service( a_frame, transition ); - mlt_frame_push_frame( a_frame, b_frame ); - mlt_frame_push_get_image( a_frame, get_image ); - - return a_frame; -} - -extern "C" { - -mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) -{ - mlt_transition transition = mlt_transition_new(); - - if ( transition ) - { - mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); - - transition->process = process; - mlt_properties_set_int( properties, "_transition_type", 1 ); // video only - mlt_properties_set_int( properties, "window_size", 8 ); - printf( "frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n" ); - } - - return transition; -} - -} // extern "C" diff -Nru mlt-0.9.0/src/modules/qimage/transition_vqm.yml mlt-0.9.2/src/modules/qimage/transition_vqm.yml --- mlt-0.9.0/src/modules/qimage/transition_vqm.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/qimage/transition_vqm.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -schema_version: 0.1 -type: transition -identifier: vqm -title: Video Quality Measurement -version: 1 -copyright: Dan Dennedy -creator: Dan Dennedy -license: GPLv3 -language: en -description: > - This performs the PSNR and SSIM video quality measurements by comparing the - B frames to the reference frame A. - It outputs the numbers to stdout in space-delimited format for easy - by another tool. - The bottom half of the B frame is placed below the top half of the A frame - for visual comparison. -tags: - - Video -parameters: - - identifier: render - title: Render - description: > - Render a line between top and bottom halves and the values atop the video. - type: integer - default: 0 - minimum: 0 - maximum: 1 - widget: checkbox diff -Nru mlt-0.9.0/src/modules/qt/common.cpp mlt-0.9.2/src/modules/qt/common.cpp --- mlt-0.9.0/src/modules/qt/common.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/common.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Dan Dennedy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include +#include + +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#include +#endif + +bool createQApplicationIfNeeded(mlt_service service) +{ + if (!qApp) { +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) + XInitThreads(); + if (getenv("DISPLAY") == 0) { + mlt_log_error(service, + "The MLT Qt module requires a X11 environment.\n" + "Please either run melt from an X session or use a fake X server like xvfb:\n" + "xvfb-run -a melt (...)\n" ); + return false; + } +#endif + if (!mlt_properties_get(mlt_global_properties(), "qt_argv")) + mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT"); + static int argc = 1; + static char* argv[] = { mlt_properties_get(mlt_global_properties(), "Qt argv") }; + new QApplication(argc, argv); + const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service)); + QLocale::setDefault(QLocale(localename)); + } + return true; +} diff -Nru mlt-0.9.0/src/modules/qt/common.h mlt-0.9.2/src/modules/qt/common.h --- mlt-0.9.0/src/modules/qt/common.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/common.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 Dan Dennedy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef COMMON_H +#define COMMON_H + +#include + +bool createQApplicationIfNeeded(mlt_service service); + +#endif // COMMON_H diff -Nru mlt-0.9.0/src/modules/qt/configure mlt-0.9.2/src/modules/qt/configure --- mlt-0.9.0/src/modules/qt/configure 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/configure 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,184 @@ +#!/bin/sh + +if [ "$help" = "1" ] +then + cat << EOF +Qt options: + + --qt-libdir - Location of Qt lib directory [/usr/lib/qt4] + --qt-includedir - Location of Qt include directory [/usr/include/qt4] + --kde-libdir - Location of KDE lib directory [/usr/lib] + --kde-includedir - Location of KDE include directory [/usr/include/kde] + --exif-libdir - Location of libexif lib directory [/usr/lib] + --exif-includedir - Location of libexif include directory [/usr/include/libexif] + --without-kde - Don't link to KDE libraries + +EOF + +else + targetos=$(uname -s) + case $targetos in + MINGW32*) + export LIBSUF=.dll + ;; + Darwin) + export LIBSUF=.dylib + ;; + Linux|FreeBSD|NetBSD) + export LIBSUF=.so + ;; + *) + ;; + esac + + qt_includedir= + qt_libdir= + + if [ "$QTDIR" != "" ] + then + qt_includedir="$QTDIR/include" + qt_libdir="$QTDIR/lib" + fi + + export without_kde= + + for i in "$@" + do + case $i in + --qt-libdir=* ) qt_libdir="${i#--qt-libdir=}" ;; + --qt-includedir=* ) qt_includedir="${i#--qt-includedir=}" ;; + --kde-libdir=* ) kde_libdir="${i#--kde-libdir=}" ;; + --kde-includedir=* ) kde_includedir="${i#--kde-includedir=}" ;; + --exif-libdir=* ) exif_libdir="${i#--exif-libdir=}" ;; + --exif-includedir=* ) exif_includedir="${i#--exif-includedir=}" ;; + --without-kde ) without_kde="true" ;; + esac + done + + echo > config.mak + + pkg-config --exists 'libexif' + if [ $? -eq 0 ] + then + echo "- Libexif found, enabling auto rotate" + echo "USE_EXIF=1" >> config.mak + echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak + echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak + echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak + elif [ -d "$exif_libdir" -a -d "$exif_includedir" ] + then + # test if we have a libexif + if [ -f "$exif_libdir/exif-data.h" ] + then + echo "- Libexif found, enabling auto rotate" + echo "USE_EXIF=1" >> config.mak + echo EXIFCXXFLAGS=-I$exif_includedir >> config.mak + echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak + echo EXIFLIBS=-L$exif_libdir lexif >> config.mak + else + echo "- Libexif not found, disabling exif features (auto rotate)" + fi + fi + + if [ -d "$qt_libdir" -a -d "$qt_includedir" ] + then + # test if we have a Qt5 or Qt4 + if [ -f "$qt_libdir/libQt5Core.so" ] || [ -d "$qt_libdir/QtWidgets.framework" ] || [ -f "$qt_libdir/libQt5Core.a" ] + then + echo "- Qt version 5.x detected" + # TODO re-enable KDE support when KDE Frameworks 5 widely available + without_kde=true + elif [ -f "$qt_libdir/libQtCore.so" ] || [ -d "$qt_libdir/QtCore.framework" ] || [ -f "$qt_libdir/libQtCore4.a" ] + then + echo "- Qt version 4.x detected" + else + echo "- Qt not found: disabling" + touch ../disable-qt + exit 0 + fi + + echo "- Include directory: " $qt_includedir + + # Qt5 on Linux, BSD, or Windows + if [ -f "$qt_libdir/libQt5Core.so" ] || [ -f "$qt_libdir/libQt5Core.a" ] + then + echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtOpenGL -I$qt_includedir/QtWidgets >> config.mak + echo QTLIBS=-Wl,-rpath-link,"$qt_libdir" -L"$qt_libdir" -lQt5Core -lQt5Gui -lQt5Xml -lQt5Svg -lQt5OpenGL -lQt5Widgets >> config.mak + # Qt5 on OS X + elif [ -d "$qt_libdir/QtWidgets.framework" ] + then + echo QTCXXFLAGS=-I$qt_includedir -F$qt_libdir \ + -I$qt_includedir/QtCore -I$qt_libdir/QtCore.framework/Headers \ + -I$qt_includedir/QtGui -I$qt_libdir/QtGui.framework/Headers \ + -I$qt_includedir/QtXml -I$qt_libdir/QtXml.framework/Headers \ + -I$qt_includedir/QtSvg -I$qt_libdir/QtSvg.framework/Headers \ + -I$qt_includedir/QtOpenGL -I$qt_libdir/QtOpenGL.framework/Headers \ + -I$qt_includedir/QtWidgets -I$qt_libdir/QtWidgets.framework/Headers \ + >> config.mak + echo QTLIBS=-F"$qt_libdir" -framework QtCore -framework QtGui -framework \ + QtXml -framework QtSvg -framework QtOpenGL -framework QtWidgets >> config.mak + # Qt4 on OS X + elif [ -d "$qt_libdir/QtGui.framework" ] + then + echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak + echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak + # Qt4 on Windows + elif [ -f "$qt_libdir/libQtCore4.a" ] + then + echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtOpenGL >> config.mak + echo QTLIBS=-Wl,-enable-auto-import -L$qt_libdir -lQtCore4 -lQtGui4 -lQtXml4 -lQtSvg4 -lQtOpenGL4 >> config.mak + # Qt4 on Linux or BSD + else + echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtOpenGL >> config.mak + echo QTLIBS=-L$qt_libdir -lQtCore -lQtGui -lQtXml -lQtSvg -lQtOpenGL >> config.mak + fi + else + pkg-config --exists 'QtGui >= 4' + if [ $? -eq 0 ] + then + echo "- Qt version 4.x detected" + echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak + echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak + else + pkg-config --exists 'Qt5Gui' + if [ $? -eq 0 ] + then + echo "- Qt version 5.x detected" + echo QTCXXFLAGS=$(pkg-config --cflags Qt5Core Qt5Gui Qt5Xml Qt5Svg Qt5OpenGL Qt5Widgets) >> config.mak + echo QTLIBS=$(pkg-config --libs Qt5Core Qt5Gui Qt5Xml Qt5Svg Qt5OpenGL Qt5Widgets) >> config.mak + else + echo "- Qt not found - disabling" + touch ../disable-qt + fi + fi + fi + + if [ "$without_kde" = "" ] + then + kde4-config + if [ $? -eq 0 ] && [ "$qt4_found" != "" ] + then + # test if we have KDE4, required on some systems to get Qt extra formats (xcf, ...) + if [ "$kde_includedir" = "" ] + then + kde_includedir=`kde4-config --install include` + fi + if [ "$kde_libdir" = "" ] + then + kde_libdir=`kde4-config --install lib` + fi + if [ -d "$kde_includedir" ] && [ -d "$kde_libdir" ] + then + echo "- KDE version 4.x detected, will enable extra image formats" + echo "#define USE_KDE4" >> config.h + echo "USE_KDE4=1" >> config.mak + echo KDECXXFLAGS=-I$kde_includedir >> config.mak + # the -L with kde4/devel is for Fedora + echo KDELIBS=-L$kde_libdir -L${kde_libdir}/kde4/devel -lkdecore >> config.mak + fi + fi + fi + + [ "$gpl3" = "true" ] && echo GPL3=1 >> config.mak + exit 0 +fi diff -Nru mlt-0.9.0/src/modules/qt/consumer_qglsl.cpp mlt-0.9.2/src/modules/qt/consumer_qglsl.cpp --- mlt-0.9.0/src/modules/qt/consumer_qglsl.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/consumer_qglsl.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,221 @@ +/* + * consumer_qglsl.cpp + * Copyright (C) 2012-2014 Dan Dennedy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + +class GLWidget : public QGLWidget +{ +private: + QGLWidget *renderContext; + bool isInitialized; + QMutex mutex; + QWaitCondition condition; + +public: + GLWidget() +#ifdef Q_OS_MAC + : QGLWidget() +#else + : QGLWidget(0, 0, Qt::SplashScreen) +#endif + , renderContext(0) + , isInitialized(false) + { + resize(0, 0); + show(); + } + + ~GLWidget() + { + delete renderContext; + } + + bool createRenderContext() + { + if (!isInitialized) { + mutex.lock(); + condition.wait(&mutex); + mutex.unlock(); + } + if (!renderContext) { + renderContext = new QGLWidget(0, this, Qt::SplashScreen); + renderContext->resize(0, 0); + renderContext->makeCurrent(); + } + return renderContext->isValid(); + } + +protected: + void initializeGL() + { + condition.wakeAll(); + isInitialized = true; + } +}; + +#else // Qt 5 + +#include +#include +#include + +typedef void* ( *thread_function_t )( void* ); + +class RenderThread : public QThread +{ +public: + RenderThread(thread_function_t function, void *data) + : QThread(0) + , m_function(function) + , m_data(data) + { + m_context = new QOpenGLContext; + m_context->create(); + m_context->moveToThread(this); + m_surface = new QOffscreenSurface(); + m_surface->create(); + } + ~RenderThread() + { + m_surface->destroy(); + delete m_surface; + } + +protected: + void run() + { + Q_ASSERT(m_context->isValid()); + m_context->makeCurrent(m_surface); + m_function(m_data); + m_context->doneCurrent(); + delete m_context; + } + +private: + thread_function_t m_function; + void* m_data; + QOpenGLContext* m_context; + QOffscreenSurface* m_surface; +}; + +static void onThreadCreate(mlt_properties owner, mlt_consumer self, + RenderThread** thread, int* priority, thread_function_t function, void* data ) +{ + Q_UNUSED(owner) + Q_UNUSED(priority) + (*thread) = new RenderThread(function, data); + (*thread)->start(); +} + +static void onThreadJoin(mlt_properties owner, mlt_consumer self, RenderThread* thread) +{ + Q_UNUSED(owner) + Q_UNUSED(self) + if (thread) { + thread->quit(); + thread->wait(); + qApp->processEvents(); + delete thread; + } +} + +#endif // Qt 5 + +static void onThreadStarted(mlt_properties owner, mlt_consumer consumer) +{ + mlt_service service = MLT_CONSUMER_SERVICE(consumer); + mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); + mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); + mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); + + mlt_log_debug(service, "%s\n", __FUNCTION__); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + GLWidget *widget = (GLWidget*) mlt_properties_get_data(properties, "GLWidget", NULL); + if (widget->createRenderContext()) { +#else + { +#endif + mlt_events_fire(filter_properties, "init glsl", NULL); + if (!mlt_properties_get_int(filter_properties, "glsl_supported")) { + mlt_log_fatal(service, + "OpenGL Shading Language rendering is not supported on this machine.\n" ); + mlt_events_fire(properties, "consumer-fatal-error", NULL); + } + } +} + +static void onThreadStopped(mlt_properties owner, mlt_consumer consumer) +{ + mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); + mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); + mlt_events_fire(MLT_FILTER_PROPERTIES(filter), "close glsl", NULL); +} + +static void onCleanup(mlt_properties owner, mlt_consumer consumer) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + GLWidget* widget = (GLWidget*) mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "GLWidget", NULL); + delete widget; + mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(consumer), "GLWidget", NULL, 0, NULL, NULL); + qApp->processEvents(); +#endif +} + +extern "C" { + +mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_consumer consumer = mlt_factory_consumer(profile, "multi", arg); + if (consumer) { + mlt_filter filter = mlt_factory_filter(profile, "glsl.manager", 0); + if (filter) { + mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); + mlt_properties_set_data(properties, "glslManager", filter, 0, (mlt_destructor) mlt_filter_close, NULL); + mlt_events_register( properties, "consumer-cleanup", NULL ); + mlt_events_listen(properties, consumer, "consumer-thread-started", (mlt_listener) onThreadStarted); + mlt_events_listen(properties, consumer, "consumer-thread-stopped", (mlt_listener) onThreadStopped); + mlt_events_listen(properties, consumer, "consumer-cleanup", (mlt_listener) onCleanup); + if (!createQApplicationIfNeeded(MLT_CONSUMER_SERVICE(consumer))) { + mlt_filter_close(filter); + mlt_consumer_close(consumer); + return NULL; + } +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + mlt_properties_set_data(properties, "GLWidget", new GLWidget, 0, NULL, NULL); +#else + mlt_events_listen(properties, consumer, "consumer-thread-create", (mlt_listener) onThreadCreate); + mlt_events_listen(properties, consumer, "consumer-thread-join", (mlt_listener) onThreadJoin); +#endif + qApp->processEvents(); + return consumer; + } + mlt_consumer_close(consumer); + } + return NULL; +} + +} diff -Nru mlt-0.9.0/src/modules/qt/factory.c mlt-0.9.2/src/modules/qt/factory.c --- mlt-0.9.0/src/modules/qt/factory.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +extern mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ); + +static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) +{ + char file[ PATH_MAX ]; + snprintf( file, PATH_MAX, "%s/qt/%s", mlt_environment( "MLT_DATA" ), (char*) data ); + return mlt_properties_parse_yaml( file ); +} + +MLT_REPOSITORY +{ + MLT_REGISTER( consumer_type, "qglsl", consumer_qglsl_init ); + MLT_REGISTER( producer_type, "qimage", producer_qimage_init ); + MLT_REGISTER( producer_type, "qtext", producer_qtext_init ); + MLT_REGISTER( producer_type, "kdenlivetitle", producer_kdenlivetitle_init ); + MLT_REGISTER_METADATA( producer_type, "qimage", metadata, "producer_qimage.yml" ); + MLT_REGISTER_METADATA( producer_type, "qtext", metadata, "producer_qtext.yml" ); + MLT_REGISTER_METADATA( producer_type, "kdenlivetitle", metadata, "producer_kdenlivetitle.yml" ); +#ifdef GPL3 + MLT_REGISTER( transition_type, "vqm", transition_vqm_init ); + MLT_REGISTER_METADATA( transition_type, "vqm", metadata, "transition_vqm.yml" ); +#endif +} diff -Nru mlt-0.9.0/src/modules/qt/kdenlivetitle_wrapper.cpp mlt-0.9.2/src/modules/qt/kdenlivetitle_wrapper.cpp --- mlt-0.9.0/src/modules/qt/kdenlivetitle_wrapper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/kdenlivetitle_wrapper.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,518 @@ +/* + * kdenlivetitle_wrapper.cpp -- kdenlivetitle wrapper + * Copyright (c) 2009 Marco Gittler + * Copyright (c) 2009 Jean-Baptiste Mardelle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "kdenlivetitle_wrapper.h" +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040600 +#include +#include +#include +#endif + +Q_DECLARE_METATYPE(QTextCursor); + +class ImageItem: public QGraphicsItem +{ +public: + ImageItem(QImage img) + { + m_img = img; + } + QImage m_img; + + +protected: + +virtual QRectF boundingRect() const +{ + return QRectF(0, 0, m_img.width(), m_img.height()); +} + +virtual void paint( QPainter *painter, + const QStyleOptionGraphicsItem * /*option*/, + QWidget* ) +{ + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->drawImage(QPoint(), m_img); +} +}; + + +QRectF stringToRect( const QString & s ) +{ + + QStringList l = s.split( ',' ); + if ( l.size() < 4 ) + return QRectF(); + return QRectF( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble() ).normalized(); +} + +QColor stringToColor( const QString & s ) +{ + QStringList l = s.split( ',' ); + if ( l.size() < 4 ) + return QColor(); + return QColor( l.at( 0 ).toInt(), l.at( 1 ).toInt(), l.at( 2 ).toInt(), l.at( 3 ).toInt() ); + ; +} +QTransform stringToTransform( const QString& s ) +{ + QStringList l = s.split( ',' ); + if ( l.size() < 9 ) + return QTransform(); + return QTransform( + l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), + l.at( 3 ).toDouble(), l.at( 4 ).toDouble(), l.at( 5 ).toDouble(), + l.at( 6 ).toDouble(), l.at( 7 ).toDouble(), l.at( 8 ).toDouble() + ); +} + +static void qscene_delete( void *data ) +{ + QGraphicsScene *scene = ( QGraphicsScene * )data; + if (scene) delete scene; + scene = NULL; +} + + +void loadFromXml( mlt_producer producer, QGraphicsScene *scene, const char *templateXml, const char *templateText ) +{ + scene->clear(); + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); + QDomDocument doc; + QString data = QString::fromUtf8(templateXml); + QString replacementText = QString::fromUtf8(templateText); + doc.setContent(data); + QDomElement title = doc.documentElement(); + + // Check for invalid title + if ( title.isNull() || title.tagName() != "kdenlivetitle" ) return; + + // Check title locale + if ( title.hasAttribute( "LC_NUMERIC" ) ) { + QString locale = title.attribute( "LC_NUMERIC" ); + QLocale::setDefault( locale ); + } + + int originalWidth; + int originalHeight; + if ( title.hasAttribute("width") ) { + originalWidth = title.attribute("width").toInt(); + originalHeight = title.attribute("height").toInt(); + scene->setSceneRect(0, 0, originalWidth, originalHeight); + } + else { + originalWidth = scene->sceneRect().width(); + originalHeight = scene->sceneRect().height(); + } + if ( title.hasAttribute( "out" ) ) { + mlt_properties_set_position( producer_props, "_animation_out", title.attribute( "out" ).toDouble() ); + } + else { + mlt_properties_set_position( producer_props, "_animation_out", mlt_producer_get_out( producer ) ); + } + + mlt_properties_set_int( producer_props, "_original_width", originalWidth ); + mlt_properties_set_int( producer_props, "_original_height", originalHeight ); + + QDomNode node; + QDomNodeList items = title.elementsByTagName("item"); + for ( int i = 0; i < items.count(); i++ ) + { + QGraphicsItem *gitem = NULL; + node = items.item( i ); + QDomNamedNodeMap nodeAttributes = node.attributes(); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); + if ( zValue > -1000 ) + { + if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) + { + QDomNamedNodeMap txtProperties = node.namedItem( "content" ).attributes(); + QFont font( txtProperties.namedItem( "font" ).nodeValue() ); + QDomNode propsNode = txtProperties.namedItem( "font-bold" ); + if ( !propsNode.isNull() ) + { + // Old: Bold/Not bold. + font.setBold( propsNode.nodeValue().toInt() ); + } + else + { + // New: Font weight (QFont::) + font.setWeight( txtProperties.namedItem( "font-weight" ).nodeValue().toInt() ); + } + font.setItalic( txtProperties.namedItem( "font-italic" ).nodeValue().toInt() ); + font.setUnderline( txtProperties.namedItem( "font-underline" ).nodeValue().toInt() ); + // Older Kdenlive version did not store pixel size but point size + if ( txtProperties.namedItem( "font-pixel-size" ).isNull() ) + { + QFont f2; + f2.setPointSize( txtProperties.namedItem( "font-size" ).nodeValue().toInt() ); + font.setPixelSize( QFontInfo( f2 ).pixelSize() ); + } + else + font.setPixelSize( txtProperties.namedItem( "font-pixel-size" ).nodeValue().toInt() ); + QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); + QString text = node.namedItem( "content" ).firstChild().nodeValue(); + if ( !replacementText.isEmpty() ) + { + text = text.replace( "%s", replacementText ); + } + QGraphicsTextItem *txt = scene->addText(text, font); + if (txtProperties.namedItem("font-outline").nodeValue().toDouble()>0.0){ + QTextDocument *doc = txt->document(); + // Make sure some that the text item does not request refresh by itself + doc->blockSignals(true); + QTextCursor cursor(doc); + cursor.select(QTextCursor::Document); + QTextCharFormat format; + format.setTextOutline( + QPen(QColor( stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ) ), + txtProperties.namedItem("font-outline").nodeValue().toDouble(), + Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin) + ); + format.setForeground(QBrush(col)); + + cursor.mergeCharFormat(format); + } else { + txt->setDefaultTextColor( col ); + } + + // Effects + if (!txtProperties.namedItem( "typewriter" ).isNull()) { + // typewriter effect + mlt_properties_set_int( producer_props, "_animated", 1 ); + QStringList effetData = QStringList() << "typewriter" << text << txtProperties.namedItem( "typewriter" ).nodeValue(); + txt->setData(0, effetData); + if ( !txtProperties.namedItem( "textwidth" ).isNull() ) + txt->setData( 1, txtProperties.namedItem( "textwidth" ).nodeValue() ); + } + + if ( txtProperties.namedItem( "alignment" ).isNull() == false ) + { + txt->setTextWidth( txt->boundingRect().width() ); + QTextOption opt = txt->document()->defaultTextOption (); + opt.setAlignment(( Qt::Alignment ) txtProperties.namedItem( "alignment" ).nodeValue().toInt() ); + txt->document()->setDefaultTextOption (opt); + } + if ( !txtProperties.namedItem( "kdenlive-axis-x-inverted" ).isNull() ) + { + //txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt()); + } + if ( !txtProperties.namedItem( "kdenlive-axis-y-inverted" ).isNull() ) + { + //txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt()); + } + gitem = txt; + } + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) + { + QString rect = node.namedItem( "content" ).attributes().namedItem( "rect" ).nodeValue(); + QString br_str = node.namedItem( "content" ).attributes().namedItem( "brushcolor" ).nodeValue(); + QString pen_str = node.namedItem( "content" ).attributes().namedItem( "pencolor" ).nodeValue(); + double penwidth = node.namedItem( "content" ).attributes().namedItem( "penwidth") .nodeValue().toDouble(); + QGraphicsRectItem *rec = scene->addRect( stringToRect( rect ), QPen( QBrush( stringToColor( pen_str ) ), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ), QBrush( stringToColor( br_str ) ) ); + gitem = rec; + } + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) + { + const QString url = node.namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); + const QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); + QImage img; + if (base64.isEmpty()){ + img.load(url); + }else{ + img.loadFromData(QByteArray::fromBase64(base64.toLatin1())); + } + ImageItem *rec = new ImageItem(img); + scene->addItem( rec ); + gitem = rec; + } + else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) + { + QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue(); + QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); + QGraphicsSvgItem *rec = NULL; + if (base64.isEmpty()){ + rec = new QGraphicsSvgItem(url); + }else{ + rec = new QGraphicsSvgItem(); + QSvgRenderer *renderer= new QSvgRenderer(QByteArray::fromBase64(base64.toLatin1()), rec ); + rec->setSharedRenderer(renderer); + } + if (rec){ + scene->addItem(rec); + gitem = rec; + } + } + } + //pos and transform + if ( gitem ) + { + QPointF p( node.namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), + node.namedItem( "position" ).attributes().namedItem( "y" ).nodeValue().toDouble() ); + gitem->setPos( p ); + gitem->setTransform( stringToTransform( node.namedItem( "position" ).firstChild().firstChild().nodeValue() ) ); + int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); + gitem->setZValue( zValue ); + +#if QT_VERSION >= 0x040600 + // effects + QDomNode eff = items.item(i).namedItem("effect"); + if (!eff.isNull()) { + QDomElement e = eff.toElement(); + if (e.attribute("type") == "blur") { + QGraphicsBlurEffect *blur = new QGraphicsBlurEffect(); + blur->setBlurRadius(e.attribute("blurradius").toInt()); + gitem->setGraphicsEffect(blur); + } + else if (e.attribute("type") == "shadow") { + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); + shadow->setBlurRadius(e.attribute("blurradius").toInt()); + shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt()); + gitem->setGraphicsEffect(shadow); + } + } +#endif + } + } + + QDomNode n = title.firstChildElement("background"); + if (!n.isNull()) { + QColor color = QColor( stringToColor( n.attributes().namedItem( "color" ).nodeValue() ) ); + if (color.alpha() > 0) { + QGraphicsRectItem *rec = scene->addRect(0, 0, scene->width(), scene->height() , QPen( Qt::NoPen ), QBrush( color ) ); + rec->setZValue(-1100); + } + + } + + QString startRect; + n = title.firstChildElement( "startviewport" ); + // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport + if (!n.isNull() && !n.toElement().hasAttribute("x")) + { + startRect = n.attributes().namedItem( "rect" ).nodeValue(); + } + n = title.firstChildElement( "endviewport" ); + // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport + if (!n.isNull() && !n.toElement().hasAttribute("x")) + { + QString rect = n.attributes().namedItem( "rect" ).nodeValue(); + if (startRect != rect) + mlt_properties_set( producer_props, "_endrect", rect.toUtf8().data() ); + } + if (!startRect.isEmpty()) { + mlt_properties_set( producer_props, "_startrect", startRect.toUtf8().data() ); + } + return; +} + + +void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int width, int height, double position, int force_refresh ) +{ + // Obtain the producer + mlt_producer producer = &self->parent; + mlt_profile profile = mlt_service_profile ( MLT_PRODUCER_SERVICE( producer ) ) ; + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); + + // Obtain properties of frame + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + + pthread_mutex_lock( &self->mutex ); + + // Check if user wants us to reload the image + if ( mlt_properties_get( producer_props, "_animated" ) != NULL || force_refresh == 1 || width != self->current_width || height != self->current_height || mlt_properties_get( producer_props, "_endrect" ) != NULL ) + { + //mlt_cache_item_close( self->image_cache ); + self->current_image = NULL; + mlt_properties_set_data( producer_props, "cached_image", NULL, 0, NULL, NULL ); + mlt_properties_set_int( producer_props, "force_reload", 0 ); + } + + if (self->current_image == NULL) { + // restore QGraphicsScene + QGraphicsScene *scene = static_cast (mlt_properties_get_data( producer_props, "qscene", NULL )); + + if ( force_refresh == 1 && scene ) + { + scene = NULL; + mlt_properties_set_data( producer_props, "qscene", NULL, 0, NULL, NULL ); + } + + if ( scene == NULL ) + { + if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) { + pthread_mutex_unlock( &self->mutex ); + return; + } + if ( !QMetaType::type("QTextCursor") ) + qRegisterMetaType( "QTextCursor" ); + scene = new QGraphicsScene(); + scene->setItemIndexMethod( QGraphicsScene::NoIndex ); + scene->setSceneRect(0, 0, mlt_properties_get_int( properties, "width" ), mlt_properties_get_int( properties, "height" )); + if ( mlt_properties_get( producer_props, "resource" ) && mlt_properties_get( producer_props, "resource" )[0] != '\0' ) + { + // The title has a resource property, so we read all properties from the resource. + // Do not serialize the xmldata + loadFromXml( producer, scene, mlt_properties_get( producer_props, "_xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); + } + else + { + // The title has no resource, all data should be serialized + loadFromXml( producer, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); + + } + mlt_properties_set_data( producer_props, "qscene", scene, 0, ( mlt_destructor )qscene_delete, NULL ); + } + + QRectF start = stringToRect( QString( mlt_properties_get( producer_props, "_startrect" ) ) ); + QRectF end = stringToRect( QString( mlt_properties_get( producer_props, "_endrect" ) ) ); + + int originalWidth = mlt_properties_get_int( producer_props, "_original_width" ); + int originalHeight= mlt_properties_get_int( producer_props, "_original_height" ); + const QRectF source( 0, 0, width, height ); + if (start.isNull()) { + start = QRectF( 0, 0, originalWidth, originalHeight ); + } + + // Effects + QList items = scene->items(); + QGraphicsTextItem *titem = NULL; + for (int i = 0; i < items.count(); i++) { + titem = static_cast ( items.at( i ) ); + if (titem && !titem->data( 0 ).isNull()) { + QStringList params = titem->data( 0 ).toStringList(); + if (params.at( 0 ) == "typewriter" ) { + // typewriter effect has 2 param values: + // the keystroke delay and a start offset, both in frames + QStringList values = params.at( 2 ).split( ";" ); + int interval = qMax( 0, ( ( int ) position - values.at( 1 ).toInt()) / values.at( 0 ).toInt() ); + QTextCursor cursor = titem->textCursor(); + cursor.movePosition(QTextCursor::EndOfBlock); + // get the font format + QTextCharFormat format = cursor.charFormat(); + cursor.select(QTextCursor::Document); + QString txt = params.at( 1 ).left( interval ); + // If the string to insert is empty, insert a space / linebreak so that we don't loose + // formatting infos for the next iterations + int lines = params.at( 1 ).count( '\n' ); + QString empty = " "; + for (int i = 0; i < lines; i++) + empty.append( "\n " ); + cursor.insertText( txt.isEmpty() ? empty : txt, format ); + if ( !titem->data( 1 ).isNull() ) + titem->setTextWidth( titem->data( 1 ).toDouble() ); + } + } + } + + //must be extracted from kdenlive title + QImage img( width, height, QImage::Format_ARGB32 ); + img.fill( 0 ); + QPainter p1; + p1.begin( &img ); + p1.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + //| QPainter::SmoothPixmapTransform ); + mlt_position anim_out = mlt_properties_get_position( producer_props, "_animation_out" ); + + if (end.isNull()) + { + scene->render( &p1, source, start, Qt::IgnoreAspectRatio ); + } + else if ( position > anim_out ) { + scene->render( &p1, source, end, Qt::IgnoreAspectRatio ); + } + else { + double percentage = 0; + if ( position && anim_out ) + percentage = position / anim_out; + QPointF topleft = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage; + QPointF bottomRight = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage; + const QRectF r1( topleft, bottomRight ); + scene->render( &p1, source, r1, Qt::IgnoreAspectRatio ); + if ( profile && !profile->progressive ){ + int line=0; + double percentage_next_filed = ( position + 0.5 ) / anim_out; + QPointF topleft_next_field = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage_next_filed; + QPointF bottomRight_next_field = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage_next_filed; + const QRectF r2( topleft_next_field, bottomRight_next_field ); + QImage img1( width, height, QImage::Format_ARGB32 ); + img1.fill( 0 ); + QPainter p2; + p2.begin(&img1); + p2.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + scene->render(&p2,source,r2, Qt::IgnoreAspectRatio ); + p2.end(); + int next_field_line = ( mlt_properties_get_int( producer_props, "top_field_first" ) ? 1 : 0 ); + for (line = next_field_line ;linecurrent_image = ( uint8_t * )mlt_pool_alloc( size ); + uint8_t *dst = self->current_image; + + for ( int i = 0; i < width * height * 4; i += 4 ) + { + *dst++=qRed( *src ); + *dst++=qGreen( *src ); + *dst++=qBlue( *src ); + *dst++=qAlpha( *src ); + src++; + } + + mlt_properties_set_data( producer_props, "cached_image", self->current_image, size, mlt_pool_release, NULL ); + self->current_width = width; + self->current_height = height; + } + + pthread_mutex_unlock( &self->mutex ); + mlt_properties_set_int( properties, "width", self->current_width ); + mlt_properties_set_int( properties, "height", self->current_height ); +} + + diff -Nru mlt-0.9.0/src/modules/qt/kdenlivetitle_wrapper.h mlt-0.9.2/src/modules/qt/kdenlivetitle_wrapper.h --- mlt-0.9.0/src/modules/qt/kdenlivetitle_wrapper.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/kdenlivetitle_wrapper.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * kdenlivetitle_wrapper.h -- kdenlivetitle wrapper + * Copyright (c) 2009 Marco Gittler + * Copyright (c) 2009 Jean-Baptiste Mardelle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +struct producer_ktitle_s +{ + struct mlt_producer_s parent; + uint8_t *current_image; + int current_width; + int current_height; + pthread_mutex_t mutex; +}; + +typedef struct producer_ktitle_s *producer_ktitle; + +extern void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, int, int, double, int ); + + +#ifdef __cplusplus +} +#endif + + + diff -Nru mlt-0.9.0/src/modules/qt/Makefile mlt-0.9.2/src/modules/qt/Makefile --- mlt-0.9.0/src/modules/qt/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,55 @@ +include ../../../config.mak +include config.mak + +CFLAGS := -I../.. $(CFLAGS) + +LDFLAGS := -L../../framework -lmlt -lpthread -lm -L../../mlt++ -lmlt++ $(LDFLAGS) + +TARGET = ../libmltqt$(LIBSUF) + +OBJS = factory.o producer_qimage.o producer_kdenlivetitle.o +CPPOBJS = common.o \ + qimage_wrapper.o \ + kdenlivetitle_wrapper.o \ + consumer_qglsl.o \ + producer_qtext.o + +ifdef GPL3 + CPPOBJS += transition_vqm.o + CFLAGS += -DGPL3 +endif + +ifneq ($(targetos), Darwin) +ifneq ($(targetos), MinGW) + LDFLAGS += -lX11 +endif +endif + +CXXFLAGS := $(QTCXXFLAGS) $(CXXFLAGS) $(CFLAGS) $(EXIFCXXFLAGS) $(KDECXXFLAGS) -Wno-deprecated + +LDFLAGS += $(QTLIBS) $(EXIFLIBS) $(KDELIBS) + +SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) + +all: $(TARGET) + +$(TARGET): $(OBJS) $(CPPOBJS) + $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CXX) -MM $(CXXFLAGS) $^ 1>.depend + +distclean: clean + rm -f .depend config.h config.mak + +clean: + rm -f $(OBJS) $(TARGET) $(CPPOBJS) + +install: all + install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" + install -d "$(DESTDIR)$(mltdatadir)/qt" + install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/qt" + +ifneq ($(wildcard .depend),) +include .depend +endif diff -Nru mlt-0.9.0/src/modules/qt/producer_kdenlivetitle.c mlt-0.9.2/src/modules/qt/producer_kdenlivetitle.c --- mlt-0.9.0/src/modules/qt/producer_kdenlivetitle.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_kdenlivetitle.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,182 @@ +/* + * producer_kdenlivetitle.c -- kdenlive producer + * Copyright (c) 2009 Marco Gittler + * Copyright (c) 2009 Jean-Baptiste Mardelle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "kdenlivetitle_wrapper.h" + +#include +#include +#include + + +void read_xml(mlt_properties properties) +{ + // Convert file name string encoding. + const char *resource = mlt_properties_get( properties, "resource" ); + mlt_properties_set( properties, "_resource_utf8", resource ); + mlt_properties_from_utf8( properties, "_resource_utf8", "_resource_local8" ); + resource = mlt_properties_get( properties, "_resource_local8" ); + + FILE *f = fopen( resource, "r" ); + if ( f != NULL ) + { + int size = 0; + long lSize; + + if ( fseek (f , 0 , SEEK_END) < 0 ) + goto error; + lSize = ftell (f); + if ( lSize <= 0 ) + goto error; + rewind (f); + + char *infile = (char*) mlt_pool_alloc(lSize); + if ( infile ) + { + size = fread(infile,1,lSize,f); + if ( size ) + { + infile[size] = '\0'; + mlt_properties_set(properties, "_xmldata", infile); + } + mlt_pool_release( infile ); + } +error: + fclose(f); + } +} + +static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) + +{ + /* Obtain properties of frame */ + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + + /* Obtain the producer for this frame */ + producer_ktitle this = mlt_properties_get_data( properties, "producer_kdenlivetitle", NULL ); + + /* Obtain properties of producer */ + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &this->parent ); + + *width = mlt_properties_get_int( properties, "rescale_width" ); + *height = mlt_properties_get_int( properties, "rescale_height" ); + + mlt_service_lock( MLT_PRODUCER_SERVICE( &this->parent ) ); + + /* Allocate the image */ + *format = mlt_image_rgb24a; + if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { + if ( mlt_properties_get_int( producer_props, "force_reload" ) > 1 ) read_xml( producer_props ); + mlt_properties_set_int( producer_props, "force_reload", 0 ); + drawKdenliveTitle( this, frame, *width, *height, mlt_frame_original_position( frame ), 1); + } + else drawKdenliveTitle( this, frame, *width, *height, mlt_frame_original_position( frame ), 0); + + // Get width and height (may have changed during the refresh) + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + if ( this->current_image ) + { + // Clone the image and the alpha + int image_size = this->current_width * ( this->current_height ) * 4; + uint8_t *image_copy = mlt_pool_alloc( image_size ); + memcpy( image_copy, this->current_image, image_size ); + // Now update properties so we free the copy after + mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); + // We're going to pass the copy on + *buffer = image_copy; + + mlt_log_debug( MLT_PRODUCER_SERVICE( &this->parent ), "width:%d height:%d %s\n", *width, *height, mlt_image_format_name( *format ) ); + } + + mlt_service_unlock( MLT_PRODUCER_SERVICE( &this->parent ) ); + + return 0; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) + +{ + // Get the real structure for this producer + producer_ktitle this = producer->child; + + /* Generate a frame */ + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + + if ( *frame != NULL ) + { + /* Obtain properties of frame and producer */ + mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); + + /* Obtain properties of producer */ + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); + + /* Set the producer on the frame properties */ + mlt_properties_set_data( properties, "producer_kdenlivetitle", this, 0, NULL, NULL ); + + /* Update timecode on the frame we're creating */ + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + /* Set producer-specific frame properties */ + mlt_properties_pass_list( properties, producer_props, "progressive, aspect_ratio" ); + + /* Push the get_image method */ + mlt_frame_push_get_image( *frame, producer_get_image ); + } + + /* Calculate the next timecode */ + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer producer ) +{ + /* fprintf(stderr, ":::::::::::::: CLOSING TITLE\n"); */ + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); +} + + +mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) +{ + /* fprintf(stderr, ":::::::::::: CREATE TITLE\n"); */ + /* Create a new producer object */ + + producer_ktitle this = calloc( 1, sizeof( struct producer_ktitle_s ) ); + if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) + { + mlt_producer producer = &this->parent; + + /* Get the properties interface */ + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + /* Callback registration */ + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; + mlt_properties_set( properties, "resource", filename ); + mlt_properties_set_int( properties, "progressive", 1 ); + read_xml(properties); + return producer; + } + free( this ); + return NULL; +} + diff -Nru mlt-0.9.0/src/modules/qt/producer_kdenlivetitle.yml mlt-0.9.2/src/modules/qt/producer_kdenlivetitle.yml --- mlt-0.9.0/src/modules/qt/producer_kdenlivetitle.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_kdenlivetitle.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,11 @@ +schema_version: 0.1 +type: producer +identifier: kdenlivetitle +title: Kdenlive Titler +version: 1 +copyright: Marco Gittler, Jean-Baptiste Mardelle +creator: Marco Gittler, Jean-Baptiste Mardelle +license: LGPLv2.1 +language: en +tags: + - Video diff -Nru mlt-0.9.0/src/modules/qt/producer_qimage.c mlt-0.9.2/src/modules/qt/producer_qimage.c --- mlt-0.9.0/src/modules/qt/producer_qimage.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_qimage.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,355 @@ +/* + * producer_image.c -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * NB: This module is designed to be functionally equivalent to the + * gtk2 image loading module so it can be used as replacement. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "qimage_wrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static void load_filenames( producer_qimage self, mlt_properties producer_properties ); +static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); +static void producer_close( mlt_producer parent ); + +mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) +{ + producer_qimage self = calloc( 1, sizeof( struct producer_qimage_s ) ); + if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) + { + mlt_producer producer = &self->parent; + + // Get the properties interface + mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); + + // Initialize KDE image plugins + init_qimage(); + + // Callback registration + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; + + // Set the default properties + mlt_properties_set( properties, "resource", filename ); + mlt_properties_set_int( properties, "ttl", 25 ); + mlt_properties_set_int( properties, "aspect_ratio", 1 ); + mlt_properties_set_int( properties, "progressive", 1 ); + mlt_properties_set_int( properties, "seekable", 1 ); + + // Validate the resource + if ( filename ) + load_filenames( self, properties ); + if ( self->count ) + { + mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + if ( frame ) + { + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL ); + mlt_frame_set_position( frame, mlt_producer_position( producer ) ); + refresh_qimage( self, frame ); + mlt_cache_item_close( self->qimage_cache ); + mlt_frame_close( frame ); + } + } + if ( self->current_width == 0 ) + { + producer_close( producer ); + producer = NULL; + } + return producer; + } + free( self ); + return NULL; +} + +static int load_svg( producer_qimage self, mlt_properties properties, const char *filename ) +{ + int result = 0; + + // Read xml string + if ( strstr( filename, "filenames, key, full ); + gap = 0; + } + else + { + gap ++; + } + } + if ( mlt_properties_count( self->filenames ) > 0 ) + { + mlt_properties_set_int( properties, "ttl", 1 ); + result = 1; + } + } + return result; +} + +static int load_sequence_deprecated( producer_qimage self, mlt_properties properties, const char *filename ) +{ + int result = 0; + const char *start; + + // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png + if ( ( start = strchr( filename, '%' ) ) ) + { + const char *end = ++start; + while ( isdigit( *end ) ) end++; + if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) ) + { + int n = end - start; + char *s = calloc( 1, n + 1 ); + strncpy( s, start, n ); + mlt_properties_set( properties, "begin", s ); + free( s ); + s = calloc( 1, strlen( filename ) + 2 ); + strncpy( s, filename, start - filename ); + sprintf( s + ( start - filename ), ".%d%s", n, end ); + result = load_sequence_sprintf( self, properties, s ); + free( s ); + } + } + return result; +} + +static int load_sequence_querystring( producer_qimage self, mlt_properties properties, const char *filename ) +{ + int result = 0; + + // Obtain filenames with pattern and begin value in query string + if ( strchr( filename, '%' ) && strchr( filename, '?' ) ) + { + // Split filename into pattern and query string + char *s = strdup( filename ); + char *querystring = strrchr( s, '?' ); + *querystring++ = '\0'; + if ( strstr( filename, "begin=" ) ) + mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 ); + else if ( strstr( filename, "begin:" ) ) + mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 ); + // Coerce to an int value so serialization does not have any extra query string cruft + mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) ); + result = load_sequence_sprintf( self, properties, s ); + free( s ); + } + return result; +} + +static int load_folder( producer_qimage self, mlt_properties properties, const char *filename ) +{ + int result = 0; + + // Obtain filenames within folder + if ( strstr( filename, "/.all." ) != NULL ) + { + char wildcard[ 1024 ]; + char *dir_name = strdup( filename ); + char *extension = strrchr( dir_name, '.' ); + + *( strstr( dir_name, "/.all." ) + 1 ) = '\0'; + sprintf( wildcard, "*%s", extension ); + + mlt_properties_dir_list( self->filenames, dir_name, wildcard, 1 ); + + free( dir_name ); + result = 1; + } + return result; +} + +static void load_filenames( producer_qimage self, mlt_properties properties ) +{ + char *filename = mlt_properties_get( properties, "resource" ); + self->filenames = mlt_properties_new( ); + + if (!load_svg( self, properties, filename ) && + !load_sequence_querystring( self, properties, filename ) && + !load_sequence_sprintf( self, properties, filename ) && + !load_sequence_deprecated( self, properties, filename ) && + !load_folder( self, properties, filename ) ) + { + mlt_properties_set( self->filenames, "0", filename ); + } + self->count = mlt_properties_count( self->filenames ); +} + +static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + + // Obtain properties of frame and producer + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + producer_qimage self = mlt_properties_get_data( properties, "producer_qimage", NULL ); + mlt_producer producer = &self->parent; + + // Use the width and height suggested by the rescale filter because we can do our own scaling. + if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) + *width = mlt_properties_get_int( properties, "rescale_width" ); + if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) + *height = mlt_properties_get_int( properties, "rescale_height" ); + + mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) ); + + // Refresh the image + self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); + self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); + self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); + self->current_image = mlt_cache_item_data( self->image_cache, NULL ); + self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); + self->current_alpha = mlt_cache_item_data( self->alpha_cache, NULL ); + refresh_image( self, frame, *format, *width, *height ); + + // Get width and height (may have changed during the refresh) + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + *format = self->format; + + // NB: Cloning is necessary with this producer (due to processing of images ahead of use) + // The fault is not in the design of mlt, but in the implementation of the qimage producer... + if ( self->current_image ) + { + // Clone the image and the alpha + int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); + uint8_t *image_copy = mlt_pool_alloc( image_size ); + memcpy( image_copy, self->current_image, image_size ); + // Now update properties so we free the copy after + mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); + // We're going to pass the copy on + *buffer = image_copy; + mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n", + self->current_width, self->current_height, mlt_image_format_name( *format ) ); + // Clone the alpha channel + if ( self->current_alpha ) + { + image_copy = mlt_pool_alloc( self->current_width * self->current_height ); + memcpy( image_copy, self->current_alpha, self->current_width * self->current_height ); + mlt_frame_set_alpha( frame, image_copy, self->current_width * self->current_height, mlt_pool_release ); + } + } + else + { + error = 1; + } + + // Release references and locks + mlt_cache_item_close( self->qimage_cache ); + mlt_cache_item_close( self->image_cache ); + mlt_cache_item_close( self->alpha_cache ); + mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); + + return error; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + // Get the real structure for this producer + producer_qimage self = producer->child; + + // Fetch the producers properties + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL ) + load_filenames( self, producer_properties ); + + // Generate a frame + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + + if ( *frame != NULL && self->count > 0 ) + { + // Obtain properties of frame and producer + mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); + + // Set the producer on the frame properties + mlt_properties_set_data( properties, "producer_qimage", self, 0, NULL, NULL ); + + // Update timecode on the frame we're creating + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + // Refresh the image + self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); + self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); + refresh_qimage( self, *frame ); + mlt_cache_item_close( self->qimage_cache ); + + // Set producer-specific frame properties + mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) ); + double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); + if ( force_ratio > 0.0 ) + mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); + else + mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) ); + + // Push the get_image method + mlt_frame_push_get_image( *frame, producer_get_image ); + } + + // Calculate the next timecode + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer parent ) +{ + producer_qimage self = parent->child; + parent->close = NULL; + mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); + mlt_producer_close( parent ); + mlt_properties_close( self->filenames ); + free( self ); +} diff -Nru mlt-0.9.0/src/modules/qt/producer_qimage.yml mlt-0.9.2/src/modules/qt/producer_qimage.yml --- mlt-0.9.0/src/modules/qt/producer_qimage.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_qimage.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,108 @@ +schema_version: 0.1 +type: producer +identifier: qimage +title: Qt QImage +version: 2 +copyright: Visual Media ? +creator: Charles Yates +license: GPLv2 +language: en +tags: + - Video +description: > + A still graphics to video generator using Qt QImage +notes: > + QImage has builtin scaling. It will rescale the originally rendered title to + whatever the consumer requests. Therefore, it will lose its aspect ratio if + so requested, and it is up to the consumer to request a proper width and + height that maintains the image aspect. +parameters: + - identifier: argument + title: File + type: string + description: > + The name of a graphics file loadable by Qt. + + If "%" in filename, the filename is used with sprintf to generate a + filename from a counter for multi-file/flipbook animation. The file + sequence ends when numeric discontinuity exceeds 100. + + If the file sequence does not begin within the count of 100 you + can pass the begin property like a query string parameter, for + example: anim-%04d.png?begin=1000. + + If filename contains "/.all.", suffix with an extension to load all + pictures with matching extension from a directory. + + If filename contains the string " + Reload the file instead of using its cached image. This property + automatically resets itself once it has been set 1 and processed. + minimum: 0 + maximum: 1 + mutable: yes + + - identifier: disable_exif + title: Disable auto-rotation + type: integer + minimum: 0 + maximum: 1 + widget: checkbox + + - identifier: force_aspect_ratio + title: Sample aspect ratio + type: float + description: Optionally override a (mis)detected aspect ratio + mutable: yes + diff -Nru mlt-0.9.0/src/modules/qt/producer_qtext.cpp mlt-0.9.2/src/modules/qt/producer_qtext.cpp --- mlt-0.9.0/src/modules/qt/producer_qtext.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_qtext.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,497 @@ +/* + * producer_qtext.c -- text generating producer + * Copyright (C) 2013 Brian Matherly + * Author: Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void close_qimg( void* qimg ) +{ + delete static_cast( qimg ); +} + +static void close_qpath( void* qpath ) +{ + delete static_cast( qpath ); +} + +static void copy_qimage_to_mlt_image( QImage* qImg, uint8_t* mImg ) +{ + int height = qImg->height(); + int width = qImg->width(); + int y = 0; + + // convert qimage to mlt + y = height + 1; + while ( --y ) + { + QRgb* src = (QRgb*) qImg->scanLine( height - y ); + int x = width + 1; + while ( --x ) + { + *mImg++ = qRed( *src ); + *mImg++ = qGreen( *src ); + *mImg++ = qBlue( *src ); + *mImg++ = qAlpha( *src ); + src++; + } + } +} + +static void copy_image_to_alpha( uint8_t* image, uint8_t* alpha, int width, int height ) +{ + register int len = width * height; + // Extract the alpha mask from the RGBA image using Duff's Device + register uint8_t *s = image + 3; // start on the alpha component + register uint8_t *d = alpha; + register int n = ( len + 7 ) / 8; + + switch ( len % 8 ) + { + case 0: do { *d++ = *s; s += 4; + case 7: *d++ = *s; s += 4; + case 6: *d++ = *s; s += 4; + case 5: *d++ = *s; s += 4; + case 4: *d++ = *s; s += 4; + case 3: *d++ = *s; s += 4; + case 2: *d++ = *s; s += 4; + case 1: *d++ = *s; s += 4; + } + while ( --n > 0 ); + } +} + +/** Check if the qpath needs to be regenerated. Return true if regeneration is required. +*/ + +static bool check_qpath( mlt_properties producer_properties ) +{ + #define MAX_SIG 500 + char new_path_sig[MAX_SIG]; + + // Generate a signature that represents the current properties + snprintf( new_path_sig, MAX_SIG, "%s%s%s%s%s%s%s%s%s%s%s", + mlt_properties_get( producer_properties, "text" ), + mlt_properties_get( producer_properties, "fgcolour" ), + mlt_properties_get( producer_properties, "bgcolour" ), + mlt_properties_get( producer_properties, "olcolour" ), + mlt_properties_get( producer_properties, "outline" ), + mlt_properties_get( producer_properties, "align" ), + mlt_properties_get( producer_properties, "pad" ), + mlt_properties_get( producer_properties, "size" ), + mlt_properties_get( producer_properties, "style" ), + mlt_properties_get( producer_properties, "weight" ), + mlt_properties_get( producer_properties, "encoding" ) ); + new_path_sig[ MAX_SIG - 1 ] = '\0'; + + // Check if the properties have changed by comparing this signature with the + // last one. + char* last_path_sig = mlt_properties_get( producer_properties, "_path_sig" ); + + if( !last_path_sig || strcmp( new_path_sig, last_path_sig ) ) + { + mlt_properties_set( producer_properties, "_path_sig", new_path_sig ); + return true; + } + return false; +} + +static void generate_qpath( mlt_properties producer_properties ) +{ + QPainterPath* qPath = static_cast( mlt_properties_get_data( producer_properties, "_qpath", NULL ) ); + int outline = mlt_properties_get_int( producer_properties, "outline" ); + char* align = mlt_properties_get( producer_properties, "align" ); + char* style = mlt_properties_get( producer_properties, "style" ); + char* text = mlt_properties_get( producer_properties, "text" ); + char* encoding = mlt_properties_get( producer_properties, "encoding" ); + int pad = mlt_properties_get_int( producer_properties, "pad" ); + int offset = pad + ( outline / 2 ); + int width = 0; + int height = 0; + + // Make the path empty + *qPath = QPainterPath(); + + // Get the strings to display + QTextCodec *codec = QTextCodec::codecForName( encoding ); + QTextDecoder *decoder = codec->makeDecoder(); + QString s = decoder->toUnicode( text ); + delete decoder; + QStringList lines = s.split( "\n" ); + + // Configure the font + QFont font; + font.setPixelSize( mlt_properties_get_int( producer_properties, "size" ) ); + font.setFamily( mlt_properties_get( producer_properties, "family" ) ); + font.setWeight( ( mlt_properties_get_int( producer_properties, "weight" ) / 10 ) -1 ); + switch( style[0] ) + { + case 'i': + case 'I': + font.setStyle( QFont::StyleItalic ); + break; + } + QFontMetrics fm( font ); + + // Determine the text rectangle size + height = fm.lineSpacing() * lines.size(); + for( int i = 0; i < lines.size(); ++i ) + { + int line_width = fm.width( lines.at(i) ); + if( line_width > width ) width = line_width; + } + + // Lay out the text in the path + int x = 0; + int y = fm.ascent() + 1 + offset; + for( int i = 0; i < lines.size(); ++i ) + { + QString line = lines.at(i); + x = offset; + switch( align[0] ) + { + default: + case 'l': + case 'L': + break; + case 'c': + case 'C': + x += ( width - fm.width( line ) ) / 2; + break; + case 'r': + case 'R': + x += width - fm.width( line ); + break; + } + qPath->addText( x, y, font, line ); + y += fm.lineSpacing(); + } + + // Account for outline and pad + width += offset * 2; + height += offset * 2; + // Sanity check + if( width == 0 ) width = 1; + if( height == 0 ) height = 1; + mlt_properties_set_int( producer_properties, "meta.media.width", width ); + mlt_properties_set_int( producer_properties, "meta.media.height", height ); +} + +static bool check_qimage( mlt_properties frame_properties ) +{ + mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); + QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ), + mlt_properties_get_int( frame_properties, "rescale_height" ) ); + QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ), + mlt_properties_get_int( frame_properties, "meta.media.height" ) ); + + // Check if the last image signature is different from the path signature + // for this frame. + char* last_img_sig = mlt_properties_get( producer_properties, "_img_sig" ); + char* path_sig = mlt_properties_get( frame_properties, "_path_sig" ); + + if( !last_img_sig || strcmp( path_sig, last_img_sig ) ) + { + mlt_properties_set( producer_properties, "_img_sig", path_sig ); + return true; + } + + // Check if the last image size matches the requested image size + QSize output_size = target_size; + if( output_size.isEmpty() ) + { + output_size = native_size; + } + if( output_size != qImg->size() ) + { + return true; + } + + return false; +} + +static void generate_qimage( mlt_properties frame_properties ) +{ + mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); + QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ), + mlt_properties_get_int( frame_properties, "rescale_height" ) ); + QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ), + mlt_properties_get_int( frame_properties, "meta.media.height" ) ); + QPainterPath* qPath = static_cast( mlt_properties_get_data( frame_properties, "_qpath", NULL ) ); + mlt_color bg_color = mlt_properties_get_color( frame_properties, "_bgcolour" ); + mlt_color fg_color = mlt_properties_get_color( frame_properties, "_fgcolour" ); + mlt_color ol_color = mlt_properties_get_color( frame_properties, "_olcolour" ); + int outline = mlt_properties_get_int( frame_properties, "_outline" ); + qreal sx = 1.0; + qreal sy = 1.0; + + // Create a new image and set up scaling + if( !target_size.isEmpty() && target_size != native_size ) + { + *qImg = QImage( target_size, QImage::Format_ARGB32 ); + sx = (qreal)target_size.width() / (qreal)native_size.width(); + sy = (qreal)target_size.height() / (qreal)native_size.height(); + } + else + { + *qImg = QImage( native_size, QImage::Format_ARGB32 ); + } + qImg->fill( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ).rgba() ); + + // Draw the text + QPainter painter( qImg ); + // Scale the painter rather than the image for better looking results. + painter.scale( sx, sy ); + painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + + QPen pen; + pen.setWidth( outline ); + if( outline ) + { + pen.setColor( QColor( ol_color.r, ol_color.g, ol_color.b, ol_color.a ) ); + } + else + { + pen.setColor( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ) ); + } + painter.setPen( pen ); + QBrush brush( QColor( fg_color.r, fg_color.g, fg_color.b, fg_color.a ) ); + painter.setBrush( brush ); + painter.drawPath( *qPath ); +} + +static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable ) +{ + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + int img_size = 0; + int alpha_size = 0; + uint8_t* alpha = NULL; + QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); + + mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); + + // Regenerate the qimage if necessary + if( check_qimage( frame_properties ) == true ) + { + generate_qimage( frame_properties ); + } + + *format = mlt_image_rgb24a; + *width = qImg->width(); + *height = qImg->height(); + + // Allocate and fill the image buffer + img_size = mlt_image_format_size( *format, *width, *height, NULL ); + *buffer = static_cast( mlt_pool_alloc( img_size ) ); + copy_qimage_to_mlt_image( qImg, *buffer ); + + mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); + + // Allocate and fill the alpha buffer + alpha_size = *width * *height; + alpha = static_cast( mlt_pool_alloc( alpha_size ) ); + copy_image_to_alpha( *buffer, alpha, *width, *height ); + + // Update the frame + mlt_frame_set_image( frame, *buffer, img_size, mlt_pool_release ); + mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); + mlt_properties_set_int( frame_properties, "width", *width ); + mlt_properties_set_int( frame_properties, "height", *height ); + + return 0; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + // Generate a frame + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + + if ( *frame != NULL ) + { + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + // Regenerate the QPainterPath if necessary + if( check_qpath( producer_properties ) ) + { + generate_qpath( producer_properties ); + } + + // Give the frame a copy of the painter path + QPainterPath* prodPath = static_cast( mlt_properties_get_data( producer_properties, "_qpath", NULL ) ); + QPainterPath* framePath = new QPainterPath( *prodPath ); + mlt_properties_set_data( frame_properties, "_qpath", static_cast( framePath ), 0, close_qpath, NULL ); + + // Pass properties to the frame that will be needed to render the path + mlt_properties_set( frame_properties, "_path_sig", mlt_properties_get( producer_properties, "_path_sig" ) ); + mlt_properties_set( frame_properties, "_bgcolour", mlt_properties_get( producer_properties, "bgcolour" ) ); + mlt_properties_set( frame_properties, "_fgcolour", mlt_properties_get( producer_properties, "fgcolour" ) ); + mlt_properties_set( frame_properties, "_olcolour", mlt_properties_get( producer_properties, "olcolour" ) ); + mlt_properties_set( frame_properties, "_outline", mlt_properties_get( producer_properties, "outline" ) ); + mlt_properties_set_data( frame_properties, "_producer_qtext", static_cast( producer ), 0, NULL, NULL ); + + // Set frame properties + mlt_properties_set_int( frame_properties, "progressive", 1 ); + double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); + if ( force_ratio > 0.0 ) + mlt_properties_set_double( frame_properties, "aspect_ratio", force_ratio ); + else + mlt_properties_set_double( frame_properties, "aspect_ratio", 1.0); + + // Update time code on the frame + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + // Configure callbacks + mlt_frame_push_get_image( *frame, producer_get_image ); + } + + // Calculate the next time code + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer producer ) +{ + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); +} + +/** Initialize. +*/ +extern "C" { + +mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) +{ + // Create a new producer object + mlt_producer producer = mlt_producer_new( profile ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + // Initialize the producer + if ( producer ) + { + if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) + { + mlt_producer_close( producer ); + return NULL; + } + + mlt_properties_set( producer_properties, "text", "" ); + mlt_properties_set( producer_properties, "fgcolour", "0xffffffff" ); + mlt_properties_set( producer_properties, "bgcolour", "0x00000000" ); + mlt_properties_set( producer_properties, "olcolour", "0x00000000" ); + mlt_properties_set( producer_properties, "outline", "0" ); + mlt_properties_set( producer_properties, "align", "left" ); + mlt_properties_set( producer_properties, "pad", "0" ); + mlt_properties_set( producer_properties, "family", "Sans" ); + mlt_properties_set( producer_properties, "size", "48" ); + mlt_properties_set( producer_properties, "style", "normal" ); + mlt_properties_set( producer_properties, "weight", "400" ); + mlt_properties_set( producer_properties, "encoding", "UTF-8" ); + + // Parse the filename argument + if ( filename == NULL || + !strcmp( filename, "" ) || + strstr( filename, "" ) ) + { + } + else if( filename[ 0 ] == '+' || strstr( filename, "/+" ) ) + { + char *copy = strdup( filename + 1 ); + char *tmp = copy; + if ( strstr( tmp, "/+" ) ) + tmp = strstr( tmp, "/+" ) + 2; + if ( strrchr( tmp, '.' ) ) + ( *strrchr( tmp, '.' ) ) = '\0'; + while ( strchr( tmp, '~' ) ) + ( *strchr( tmp, '~' ) ) = '\n'; + mlt_properties_set( producer_properties, "text", tmp ); + mlt_properties_set( producer_properties, "resource", filename ); + free( copy ); + } + else + { + // Convert file name string encoding. + mlt_properties_set( producer_properties, "resource", filename ); + mlt_properties_from_utf8( producer_properties, "resource", "_resource" ); + filename = mlt_properties_get( producer_properties, "_resource" ); + + FILE *f = fopen( filename, "r" ); + if ( f != NULL ) + { + char line[81]; + char *tmp = NULL; + size_t size = 0; + line[80] = '\0'; + + while ( fgets( line, 80, f ) ) + { + size += strlen( line ) + 1; + if ( tmp ) + { + tmp = (char*)realloc( tmp, size ); + if ( tmp ) + strcat( tmp, line ); + } + else + { + tmp = strdup( line ); + } + } + fclose( f ); + + if ( tmp && tmp[ strlen( tmp ) - 1 ] == '\n' ) + tmp[ strlen( tmp ) - 1 ] = '\0'; + + if ( tmp ) + mlt_properties_set( producer_properties, "text", tmp ); + free( tmp ); + } + } + + // Create QT objects to be reused. + mlt_properties_set_data( producer_properties, "_qimg", static_cast( new QImage() ), 0, close_qimg, NULL ); + mlt_properties_set_data( producer_properties, "_qpath", static_cast( new QPainterPath() ), 0, close_qpath, NULL ); + + // Callback registration + producer->get_frame = producer_get_frame; + producer->close = ( mlt_destructor )producer_close; + } + + return producer; +} + +} diff -Nru mlt-0.9.0/src/modules/qt/producer_qtext.yml mlt-0.9.2/src/modules/qt/producer_qtext.yml --- mlt-0.9.0/src/modules/qt/producer_qtext.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/producer_qtext.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,193 @@ +schema_version: 0.1 +type: producer +identifier: qtext +title: QT Titler +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video +description: > + A title generator that uses the Qt framework to render text. +notes: > + qtext accepts a file name with at ".txt" extension. If the filename begins + with "+" the qtext producer interprets the filename as text. This is a + shortcut to embed titles in melt commands. For MLT XML, it is recommended that + you embed the title text in the "text" property value. + + qtext has builtin scaling. It will rescale the originally rendered title to + whatever the consumer requests. Therefore, it will lose its aspect ratio if + so requested, and it is up to the consumer to request a proper width and + height that maintains the image aspect. + +parameters: + - identifier: argument + title: File + type: string + description: | + A text file containing text to be rendered. + The text file contents initialize the value of the "text" parameter. + readonly: no + required: no + mutable: no + widget: fileopen + + - identifier: text + title: Text + type: string + description: | + A text string to be rendered. + readonly: no + mutable: yes + widget: textbox + + - identifier: fgcolour + title: Foreground color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + readonly: no + mutable: yes + widget: color + + - identifier: bgcolour + title: Background color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + readonly: no + mutable: yes + widget: color + + - identifier: olcolour + title: Outline color + type: string + description: > + A color value is a hexadecimal representation of RGB plus alpha channel + as 0xrrggbbaa. Colors can also be the words: white, black, red, green, + or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. + readonly: no + mutable: yes + widget: color + + - identifier: outline + title: Outline Width + type: string + description: > + The width of the outline in pixels. + readonly: no + default: 0 + minimum: 0 + maximum: 3 + mutable: yes + widget: spinner + + - identifier: align + title: Paragraph alignment + type: string + description: > + left, center, right + readonly: no + default: left + mutable: yes + widget: combo + + - identifier: pad + title: Padding + type: integer + description: > + The number of pixels to pad the background rectangle beyond edges of text. + readonly: no + default: 0 + mutable: yes + widget: spinner + + - identifier: family + title: Font family + type: string + description: > + The font typeface. + default: Sans + readonly: no + mutable: yes + widget: combo + + - identifier: size + title: Font size + type: integer + description: > + The size in pixels of the font. + default: 48 + readonly: no + mutable: yes + widget: spinner + + - identifier: style + title: Font style + type: string + description: > + The style of the font. + values: + - normal + - italic + default: normal + readonly: no + mutable: yes + widget: combo + + - identifier: weight + title: Font weight + type: integer + description: The weight of the font. + minimum: 100 + maximum: 1000 + default: 400 + readonly: no + mutable: yes + widget: spinner + + - identifier: encoding + title: Encoding + type: string + description: > + The text encoding type of the "text" parameter. + default: UTF-8 + readonly: no + mutable: yes + widget: combo + + - identifier: force_aspect_ratio + title: Sample aspect ratio + type: float + description: Optionally override a (mis)detected aspect ratio + mutable: yes + + - identifier: meta.media.width + title: Real width + type: integer + description: The original, unscaled width of the rendered image. + readonly: yes + + - identifier: meta.media.height + title: Real height + type: integer + description: The original, unscaled height of the rendered image. + readonly: yes + + - identifier: width + title: Width + type: integer + description: The last requested scaled image width. + readonly: yes + + - identifier: height + title: Height + type: integer + description: The last requested scaled image height. + readonly: yes \ No newline at end of file diff -Nru mlt-0.9.0/src/modules/qt/qimage_wrapper.cpp mlt-0.9.2/src/modules/qt/qimage_wrapper.cpp --- mlt-0.9.0/src/modules/qt/qimage_wrapper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/qimage_wrapper.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,344 @@ +/* + * qimage_wrapper.cpp -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * NB: This module is designed to be functionally equivalent to the + * gtk2 image loading module so it can be used as replacement. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "qimage_wrapper.h" +#include "common.h" + +#ifdef USE_KDE4 +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef USE_EXIF +#include +#endif + +#include +#include + +extern "C" { + +#include +#include + +#ifdef USE_KDE4 +static KComponentData *instance = 0L; +#endif + +static void qimage_delete( void *data ) +{ + QImage *image = ( QImage * )data; + delete image; + image = NULL; +#if defined(USE_KDE4) + if (instance) delete instance; + instance = 0L; +#endif + +} + +void init_qimage() +{ +#ifdef USE_KDE4 + if ( !instance ) { + instance = new KComponentData( "qimage_prod" ); + } +#endif + +} + +static QImage* reorient_with_exif( producer_qimage self, int image_idx, QImage *qimage ) +{ +#ifdef USE_EXIF + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent ); + ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) ); + ExifEntry *entry; + int exif_orientation = 0; + /* get orientation and rotate image accordingly if necessary */ + if (d) { + if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) ) + exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d)); + + /* Free the EXIF data */ + exif_data_unref(d); + } + + // Remember EXIF value, might be useful for someone + mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation ); + + if ( exif_orientation > 1 ) + { + // Rotate image according to exif data + QImage processed; + QMatrix matrix; + + switch ( exif_orientation ) { + case 2: + matrix.scale( -1, 1 ); + break; + case 3: + matrix.rotate( 180 ); + break; + case 4: + matrix.scale( 1, -1 ); + break; + case 5: + matrix.rotate( 270 ); + matrix.scale( -1, 1 ); + break; + case 6: + matrix.rotate( 90 ); + break; + case 7: + matrix.rotate( 90 ); + matrix.scale( -1, 1 ); + break; + case 8: + matrix.rotate( 270 ); + break; + } + processed = qimage->transformed( matrix ); + delete qimage; + qimage = new QImage( processed ); + } +#endif + return qimage; +} + +int refresh_qimage( producer_qimage self, mlt_frame frame ) +{ + // Obtain properties of frame and producer + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_producer producer = &self->parent; + mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); + + // Check if user wants us to reload the image + if ( mlt_properties_get_int( producer_props, "force_reload" ) ) + { + self->qimage = NULL; + self->current_image = NULL; + mlt_properties_set_int( producer_props, "force_reload", 0 ); + } + + // Get the time to live for each frame + double ttl = mlt_properties_get_int( producer_props, "ttl" ); + + // Get the original position of this frame + mlt_position position = mlt_frame_original_position( frame ); + position += mlt_producer_get_in( producer ); + + // Image index + int image_idx = ( int )floor( ( double )position / ttl ) % self->count; + + // Key for the cache + char image_key[ 10 ]; + sprintf( image_key, "%d", image_idx ); + + int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); + + if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) + return -1; + + if ( image_idx != self->qimage_idx ) + self->qimage = NULL; + if ( !self->qimage || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) + { + self->current_image = NULL; + QImage *qimage = new QImage( QString::fromUtf8( mlt_properties_get_value( self->filenames, image_idx ) ) ); + self->qimage = qimage; + + if ( !qimage->isNull( ) ) + { + // Read the exif value for this file + if ( !disable_exif ) + qimage = reorient_with_exif( self, image_idx, qimage ); + + // Register qimage for destruction and reuse + mlt_cache_item_close( self->qimage_cache ); + mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); + self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); + self->qimage_idx = image_idx; + + // Store the width/height of the qimage + self->current_width = qimage->width( ); + self->current_height = qimage->height( ); + + mlt_events_block( producer_props, NULL ); + mlt_properties_set_int( producer_props, "meta.media.width", self->current_width ); + mlt_properties_set_int( producer_props, "meta.media.height", self->current_height ); + mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); + mlt_events_unblock( producer_props, NULL ); + } + else + { + delete qimage; + self->qimage = NULL; + } + } + + // Set width/height of frame + mlt_properties_set_int( properties, "width", self->current_width ); + mlt_properties_set_int( properties, "height", self->current_height ); + + return image_idx; +} + +void refresh_image( producer_qimage self, mlt_frame frame, mlt_image_format format, int width, int height ) +{ + // Obtain properties of frame and producer + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_producer producer = &self->parent; + + // Get index and qimage + int image_idx = refresh_qimage( self, frame ); + + // optimization for subsequent iterations on single pictur + if ( image_idx != self->image_idx || width != self->current_width || height != self->current_height ) + self->current_image = NULL; + + // If we have a qimage and need a new scaled image + if ( self->qimage && ( !self->current_image || ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) ) ) + { + QString interps = mlt_properties_get( properties, "rescale.interp" ); + bool interp = ( interps != "nearest" ) && ( interps != "none" ); + QImage *qimage = static_cast( self->qimage ); + + // Note - the original qimage is already safe and ready for destruction + if ( qimage->depth() == 1 ) + { + QImage temp = qimage->convertToFormat( QImage::Format_RGB32 ); + delete qimage; + qimage = new QImage( temp ); + self->qimage = qimage; + } + QImage scaled = interp? qimage->scaled( QSize( width, height ) ) : + qimage->scaled( QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); + int has_alpha = scaled.hasAlphaChannel(); + + // Store width and height + self->current_width = width; + self->current_height = height; + + // Allocate/define image + int dst_stride = width * ( has_alpha ? 4 : 3 ); + int image_size = dst_stride * ( height + 1 ); + self->current_image = ( uint8_t * )mlt_pool_alloc( image_size ); + self->current_alpha = NULL; + self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24; + + // Copy the image + int y = self->current_height + 1; + uint8_t *dst = self->current_image; + while ( --y ) + { + QRgb *src = (QRgb*) scaled.scanLine( self->current_height - y ); + int x = self->current_width + 1; + while ( --x ) + { + *dst++ = qRed(*src); + *dst++ = qGreen(*src); + *dst++ = qBlue(*src); + if ( has_alpha ) *dst++ = qAlpha(*src); + ++src; + } + } + + // Convert image to requested format + if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) + { + uint8_t *buffer = NULL; + + // First, set the image so it can be converted when we get it + mlt_frame_replace_image( frame, self->current_image, self->format, width, height ); + mlt_frame_set_image( frame, self->current_image, image_size, mlt_pool_release ); + self->format = format; + + // get_image will do the format conversion + mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 ); + + // cache copies of the image and alpha buffers + if ( buffer ) + { + image_size = mlt_image_format_size( format, width, height, NULL ); + self->current_image = (uint8_t*) mlt_pool_alloc( image_size ); + memcpy( self->current_image, buffer, image_size ); + } + if ( ( buffer = mlt_frame_get_alpha_mask( frame ) ) ) + { + self->current_alpha = (uint8_t*) mlt_pool_alloc( width * height ); + memcpy( self->current_alpha, buffer, width * height ); + } + } + + // Update the cache + mlt_cache_item_close( self->image_cache ); + mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.image", self->current_image, image_size, mlt_pool_release ); + self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); + self->image_idx = image_idx; + mlt_cache_item_close( self->alpha_cache ); + self->alpha_cache = NULL; + if ( self->current_alpha ) + { + mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha", self->current_alpha, width * height, mlt_pool_release ); + self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); + } + } + + // Set width/height of frame + mlt_properties_set_int( properties, "width", self->current_width ); + mlt_properties_set_int( properties, "height", self->current_height ); +} + +extern void make_tempfile( producer_qimage self, const char *xml ) +{ + // Generate a temporary file for the svg + QTemporaryFile tempFile( "mlt.XXXXXX" ); + + tempFile.setAutoRemove( false ); + if ( tempFile.open() ) + { + // Write the svg into the temp file + char *fullname = tempFile.fileName().toUtf8().data(); + + // Strip leading crap + while ( xml[0] != '<' ) + xml++; + + qint64 remaining_bytes = strlen( xml ); + while ( remaining_bytes > 0 ) + remaining_bytes -= tempFile.write( xml + strlen( xml ) - remaining_bytes, remaining_bytes ); + tempFile.close(); + + mlt_properties_set( self->filenames, "0", fullname ); + + mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( &self->parent ), "__temporary_file__", + fullname, 0, ( mlt_destructor )unlink, NULL ); + } +} + +} // extern "C" diff -Nru mlt-0.9.0/src/modules/qt/qimage_wrapper.h mlt-0.9.2/src/modules/qt/qimage_wrapper.h --- mlt-0.9.0/src/modules/qt/qimage_wrapper.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/qimage_wrapper.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * qimage_wrapper.h -- a QT/QImage based producer for MLT + * Copyright (C) 2006 Visual Media + * Author: Charles Yates + * + * NB: This module is designed to be functionally equivalent to the + * gtk2 image loading module so it can be used as replacement. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MLT_QIMAGE_WRAPPER +#define MLT_QIMAGE_WRAPPER + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct producer_qimage_s +{ + struct mlt_producer_s parent; + mlt_properties filenames; + int count; + int image_idx; + int qimage_idx; + uint8_t *current_image; + uint8_t *current_alpha; + int current_width; + int current_height; + mlt_cache_item image_cache; + mlt_cache_item alpha_cache; + mlt_cache_item qimage_cache; + void *qimage; + mlt_image_format format; +}; + +typedef struct producer_qimage_s *producer_qimage; + +extern int refresh_qimage( producer_qimage self, mlt_frame frame ); +extern void refresh_image( producer_qimage, mlt_frame, mlt_image_format, int width, int height ); +extern void make_tempfile( producer_qimage, const char *xml ); +extern void init_qimage(); + + +#ifdef __cplusplus +} +#endif + +#endif diff -Nru mlt-0.9.0/src/modules/qt/transition_vqm.cpp mlt-0.9.2/src/modules/qt/transition_vqm.cpp --- mlt-0.9.0/src/modules/qt/transition_vqm.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/transition_vqm.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,243 @@ +/* + * transition_vqm.c -- video quality measurement + * Copyright (c) 2012 Dan Dennedy + * Core psnr and ssim routines based on code from + * qsnr (C) 2010 E. Oriani, ema fastwebnet it + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static double calc_psnr( const uint8_t *a, const uint8_t *b, int size, int bpp ) +{ + double mse = 0.0; + int n = size + 1; + + while ( --n ) + { + int diff = *a - *b; + mse += diff * diff; + a += bpp; + b += bpp; + } + + return 10.0 * log10( 255.0 * 255.0 / ( mse == 0 ? 1e-10 : mse/size ) ); +} + +static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp ) +{ + int windows_x = width / window_size; + int windows_y = height / window_size; + double avg = 0.0; + + if ( !windows_x || !windows_y ) + return 0.0; + + // for each window + for ( int y = 0; y < windows_y; ++y ) + for ( int x = 0; x < windows_x; ++x ) + { + int base_offset = x * window_size + y * window_size * width; + double ref_acc = 0.0, + ref_acc_2 = 0.0, + cmp_acc = 0.0, + cmp_acc_2 = 0.0, + ref_cmp_acc = 0.0; + + // accumulate the pixel values for this window + for ( int j = 0; j < window_size; ++j ) + for ( int i = 0; i < window_size; ++i ) + { + uint8_t c_a = a[bpp * (base_offset + j * width + i)]; + uint8_t c_b = b[bpp * (base_offset + j * width + i)]; + ref_acc += c_a; + ref_acc_2 += c_a * c_a; + cmp_acc += c_b; + cmp_acc_2 += c_b * c_b; + ref_cmp_acc += c_a * c_b; + } + + // compute the SSIM for this window + // http://en.wikipedia.org/wiki/SSIM + // http://en.wikipedia.org/wiki/Variance + // http://en.wikipedia.org/wiki/Covariance + double n_samples = window_size * window_size, + ref_avg = ref_acc / n_samples, + ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg, + cmp_avg = cmp_acc / n_samples, + cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg, + ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg, + c1 = 6.5025, // (0.01*255.0)^2 + c2 = 58.5225, // (0.03*255)^2 + ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2), + ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2); + + // accumulate the SSIM + avg += ssim_num / ssim_den; + } + + // return the average SSIM + return avg / windows_x / windows_y; +} + +static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_frame b_frame = mlt_frame_pop_frame( a_frame ); + mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame ); + mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) ); + uint8_t *b_image; + int window_size = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "window_size" ); + double psnr[3], ssim[3]; + + *format = mlt_image_yuv422; + mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + mlt_frame_get_image( a_frame, image, format, width, height, writable ); + + psnr[0] = calc_psnr( *image, b_image, *width * *height, 2 ); + psnr[1] = calc_psnr( *image + 1, b_image + 1, *width * *height / 2, 4 ); + psnr[2] = calc_psnr( *image + 3, b_image + 3, *width * *height / 2, 4 ); + ssim[0] = calc_ssim( *image, b_image, *width, *height, window_size, 2 ); + ssim[1] = calc_ssim( *image + 1, b_image + 1, *width / 2, *height, window_size, 4 ); + ssim[2] = calc_ssim( *image + 3, b_image + 3, *width / 2, *height, window_size, 4 ); + mlt_properties_set_double( properties, "meta.vqm.psnr.y", psnr[0] ); + mlt_properties_set_double( properties, "meta.vqm.psnr.cb", psnr[1] ); + mlt_properties_set_double( properties, "meta.vqm.psnr.cr", psnr[2] ); + mlt_properties_set_double( properties, "meta.vqm.ssim.y", ssim[0] ); + mlt_properties_set_double( properties, "meta.vqm.ssim.cb", ssim[1] ); + mlt_properties_set_double( properties, "meta.vqm.ssim.cr", ssim[2] ); + printf( "%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n", + mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], + ssim[0], ssim[1], ssim[2] ); + + // copy the B frame to the bottom of the A frame for comparison + window_size = mlt_image_format_size( *format, *width, *height, NULL ) / 2; + memcpy( *image + window_size, b_image + window_size, window_size ); + + if ( !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "render" ) ) + return 0; + + // get RGBA image for Qt drawing + *format = mlt_image_rgb24a; + mlt_frame_get_image( a_frame, image, format, width, height, 1 ); + + // convert mlt image to qimage + QImage img( *width, *height, QImage::Format_ARGB32 ); + int y = *height + 1; + uint8_t *src = *image; + while ( --y ) + { + QRgb *dst = (QRgb*) img.scanLine( *height - y ); + int x = *width + 1; + while ( --x ) + { + *dst++ = qRgba( src[0], src[1], src[2], 255 ); + src += 4; + } + } + + // setup Qt drawing + QPainter painter; + painter.begin( &img ); + painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); + + // draw some stuff with Qt + QPalette palette; + QFont font; + QString s; + font.setBold( true ); + font.setPointSize( 30 * *height / 1080 ); + painter.setPen( QColor("black") ); + painter.drawLine( 0, *height/2 + 1, *width, *height/2 ); + painter.setPen( QColor("white") ); + painter.drawLine( 0, *height/2 - 1, *width, *height/2 ); + painter.setFont( font ); + s.sprintf( "Frame: %05d\nPSNR: %05.2f (Y) %05.2f (Cb) %05.2f (Cr)\nSSIM: %5.3f (Y) %5.3f (Cb) %5.3f (Cr)", + mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], + ssim[0], ssim[1], ssim[2] ); + painter.setPen( QColor("black") ); + painter.drawText( 52, *height * 8 / 10 + 2, *width, *height, 0, s ); + painter.setPen( QColor("white") ); + painter.drawText( 50, *height * 8 / 10, *width, *height, 0, s ); + + // finish Qt drawing + painter.end(); + window_size = mlt_image_format_size( *format, *width, *height, NULL ); + uint8_t *dst = (uint8_t *) mlt_pool_alloc( window_size ); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), "image", dst, window_size, mlt_pool_release, NULL ); + *image = dst; + + // convert qimage to mlt + y = *height + 1; + while ( --y ) + { + QRgb *src = (QRgb*) img.scanLine( *height - y ); + int x = *width + 1; + while ( --x ) + { + *dst++ = qRed( *src ); + *dst++ = qGreen( *src ); + *dst++ = qBlue( *src ); + *dst++ = qAlpha( *src ); + src++; + } + } + + return 0; +} + +static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + mlt_frame_push_service( a_frame, transition ); + mlt_frame_push_frame( a_frame, b_frame ); + mlt_frame_push_get_image( a_frame, get_image ); + + return a_frame; +} + +extern "C" { + +mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) +{ + mlt_transition transition = mlt_transition_new(); + + if ( transition ) + { + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) ) + { + mlt_transition_close( transition ); + return NULL; + } + transition->process = process; + mlt_properties_set_int( properties, "_transition_type", 1 ); // video only + mlt_properties_set_int( properties, "window_size", 8 ); + printf( "frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n" ); + } + + return transition; +} + +} // extern "C" diff -Nru mlt-0.9.0/src/modules/qt/transition_vqm.yml mlt-0.9.2/src/modules/qt/transition_vqm.yml --- mlt-0.9.0/src/modules/qt/transition_vqm.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/qt/transition_vqm.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,28 @@ +schema_version: 0.1 +type: transition +identifier: vqm +title: Video Quality Measurement +version: 1 +copyright: Dan Dennedy +creator: Dan Dennedy +license: GPLv3 +language: en +description: > + This performs the PSNR and SSIM video quality measurements by comparing the + B frames to the reference frame A. + It outputs the numbers to stdout in space-delimited format for easy + by another tool. + The bottom half of the B frame is placed below the top half of the A frame + for visual comparison. +tags: + - Video +parameters: + - identifier: render + title: Render + description: > + Render a line between top and bottom halves and the values atop the video. + type: integer + default: 0 + minimum: 0 + maximum: 1 + widget: checkbox diff -Nru mlt-0.9.0/src/modules/resample/Makefile mlt-0.9.2/src/modules/resample/Makefile --- mlt-0.9.0/src/modules/resample/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/resample/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -9,9 +9,9 @@ OBJS = factory.o \ filter_resample.o -CFLAGS += `pkg-config --cflags samplerate` +CFLAGS += $(shell pkg-config --cflags samplerate) -LDFLAGS += `pkg-config --libs samplerate` +LDFLAGS += $(shell pkg-config --libs samplerate) SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.0/src/modules/rotoscoping/cJSON.c mlt-0.9.2/src/modules/rotoscoping/cJSON.c --- mlt-0.9.0/src/modules/rotoscoping/cJSON.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/cJSON.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,497 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -// cJSON -// JSON parser in C. - -#include -#include -#include -#include -#include -#include -#include -#include "cJSON.h" - -static int cJSON_strcasecmp(const char *s1,const char *s2) -{ - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; -} - -void cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; - return; - } - - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; -} - -// Internal constructor. -static cJSON *cJSON_New_Item() -{ - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; -} - -// Delete a cJSON structure. -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } -} - -// Parse the input text to generate a number, and populate the result into item. -static const char *parse_number(cJSON *item,const char *num) -{ - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; - - // Could use sscanf for this? - if (*num=='-') sign=-1,num++; // Has sign? - if (*num=='0') num++; // is zero - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); // Number? - if (*num=='.') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} // Fractional part? - if (*num=='e' || *num=='E') // Exponent? - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; // With sign? - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); // Number? - } - - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); // number = +/- number.fraction * 10^+/- exponent - - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; -} - -// Render the number nicely from the given item into a string. -static char *print_number(cJSON *item) -{ - char *str; - double d=item->valuedouble; - if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - str=(char*)cJSON_malloc(21); // 2^64+1 can be represented in 21 chars. - if (str) sprintf(str,"%d",item->valueint); - } - else - { - str=(char*)cJSON_malloc(64); // This is a nice tradeoff. - if (str) - { - if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; -} - -// Parse the input text into an unescaped cstring, and populate item. -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) -{ - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; - if (*str!='\"') return 0; // not a string! - - while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes. - - out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly. - if (!out) return 0; - - ptr=str+1;ptr2=out; - while (*ptr!='\"' && (unsigned char)*ptr>31) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. - sscanf(ptr+1,"%4x",&uc); // get the unicode char. - len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; - - switch (len) { - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } - ptr2+=len;ptr+=4; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; -} - -// Render the cstring provided to an escaped version that can be printed. -static char *print_string_ptr(const char *str) -{ - const char *ptr;char *ptr2,*out;int len=0; - - if (!str) return cJSON_strdup(""); - ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} - - out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: ptr2--; break; // eviscerate with prejudice. - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; -} -// Invote print_string_ptr (which is useful) on an item. -static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} - -// Predeclare these prototypes. -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt); - -// Utility to jump whitespace and cr/lf -static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;} - -// Parse an object - create a new root, and populate. -cJSON *cJSON_Parse(const char *value) -{ - cJSON *c=cJSON_New_Item(); - if (!c) return 0; /* memory fail */ - - if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} - return c; -} - -// Render a cJSON item/entity/structure to text. -char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} - -// Parser core - when encountering text, process appropriately. -static const char *parse_value(cJSON *item,const char *value) -{ - if (!value) return 0; // Fail on null. - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } - - return 0; // failure. -} - -// Render a value to text. -static char *print_value(cJSON *item,int depth,int fmt) -{ - char *out=0; - if (!item) return 0; - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item);break; - case cJSON_String: out=print_string(item);break; - case cJSON_Array: out=print_array(item,depth,fmt);break; - case cJSON_Object: out=print_object(item,depth,fmt);break; - } - return out; -} - -// Build an array from input text. -static const char *parse_array(cJSON *item,const char *value) -{ - cJSON *child; - if (*value!='[') return 0; // not an array! - - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; // empty array. - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; // memory fail - value=skip(parse_value(child,skip(value))); // skip any spacing, get the value. - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; // memory fail - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; // memory fail - } - - if (*value==']') return value+1; // end of array - return 0; // malformed. -} - -// Render an array to text -static char *print_array(cJSON *item,int depth,int fmt) -{ - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; - - // How many entries in the array? - while (child) numentries++,child=child->next; - // Allocate an array to hold the values for each - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - // Retrieve all the results: - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } - - // If we didn't fail, try to malloc the output string - if (!fail) out=cJSON_malloc(len); - // If that fails, we fail. - if (!out) fail=1; - - // Handle failure. - if (fail) - { - for (i=0;itype=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; // empty array. - - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') return 0; // fail! - value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; // memory fail - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') return 0; // fail! - value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. - if (!value) return 0; - } - - if (*value=='}') return value+1; // end of array - return 0; // malformed. -} - -// Render an object to text. -static char *print_object(cJSON *item,int depth,int fmt) -{ - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - // Count the number of entries. - while (child) numentries++,child=child->next; - // Allocate space for the names and the objects - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); - - // Collect all the results into our arrays: - child=item->child;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string); - entries[i++]=ret=print_value(child,depth,fmt); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } - - // Try to allocate the output string - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; - - // Handle failure - if (fail) - { - for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} - -// Utility for array list handling. -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} -// Utility for handling references. -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} - -// Add item to array/object. -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; - if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} - -// Replace array/object items with new ones. -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} - -// Create basic types: -cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} - -// Create Arrays: -cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} diff -Nru mlt-0.9.0/src/modules/rotoscoping/cJSON.h mlt-0.9.2/src/modules/rotoscoping/cJSON.h --- mlt-0.9.0/src/modules/rotoscoping/cJSON.h 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/cJSON.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" -{ -#endif - -// cJSON Types: -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - -#define cJSON_IsReference 256 - -// The cJSON structure: -typedef struct cJSON { - struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem - struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. - - int type; // The type of the item, as above. - - char *valuestring; // The item's string, if type==cJSON_String - int valueint; // The item's number, if type==cJSON_Number - double valuedouble; // The item's number, if type==cJSON_Number - - char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object. -} cJSON; - -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); -} cJSON_Hooks; - -// Supply malloc, realloc and free functions to cJSON -extern void cJSON_InitHooks(cJSON_Hooks* hooks); - - -// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. -extern cJSON *cJSON_Parse(const char *value); -// Render a cJSON entity to text for transfer/storage. Free the char* when finished. -extern char *cJSON_Print(cJSON *item); -// Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. -extern char *cJSON_PrintUnformatted(cJSON *item); -// Delete a cJSON entity and all subentities. -extern void cJSON_Delete(cJSON *c); - -// Returns the number of items in an array (or object). -extern int cJSON_GetArraySize(cJSON *array); -// Retrieve item number "item" from array "array". Returns NULL if unsuccessful. -extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); -// Get item "string" from object. Case insensitive. -extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); - -// These calls create a cJSON item of the appropriate type. -extern cJSON *cJSON_CreateNull(); -extern cJSON *cJSON_CreateTrue(); -extern cJSON *cJSON_CreateFalse(); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(); -extern cJSON *cJSON_CreateObject(); - -// These utilities create an Array of count items. -extern cJSON *cJSON_CreateIntArray(int *numbers,int count); -extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); - -// Append item to the specified array/object. -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -// Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); - -// Remove/Detatch items from Arrays/Objects. -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - -// Update array items. -extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); - -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -#ifdef __cplusplus -} -#endif - -#endif diff -Nru mlt-0.9.0/src/modules/rotoscoping/factory.c mlt-0.9.2/src/modules/rotoscoping/factory.c --- mlt-0.9.0/src/modules/rotoscoping/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/factory.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* - * factory.c -- the factory method interfaces - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited - * Author: Charles Yates - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -extern mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); - -static mlt_properties rotoscoping_metadata( mlt_service_type type, const char *id, void *data ) -{ - char file[ PATH_MAX ]; - snprintf( file, PATH_MAX, "%s/rotoscoping/filter_%s.yml", mlt_environment( "MLT_DATA" ), id ); - return mlt_properties_parse_yaml( file ); -} - -MLT_REPOSITORY -{ - MLT_REGISTER( filter_type, "rotoscoping", filter_rotoscoping_init ); - - MLT_REGISTER_METADATA( filter_type, "rotoscoping", rotoscoping_metadata, NULL ); -} diff -Nru mlt-0.9.0/src/modules/rotoscoping/filter_rotoscoping.c mlt-0.9.2/src/modules/rotoscoping/filter_rotoscoping.c --- mlt-0.9.0/src/modules/rotoscoping/filter_rotoscoping.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/filter_rotoscoping.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,630 +0,0 @@ -/* - * rotoscoping.c -- keyframable vector based rotoscoping - * Copyright (C) 2011 Till Theato - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "cJSON.h" - -#include -#include -#include -#include - -#define MAX( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) ) -#define MIN( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) -#define SQR( x ) ( x ) * ( x ) - -/** x, y tuple with double precision */ -typedef struct PointF -{ - double x; - double y; -} PointF; - -typedef struct BPointF -{ - struct PointF h1; - struct PointF p; - struct PointF h2; -} BPointF; - -enum MODES { MODE_RGB, MODE_ALPHA, MODE_LUMA }; -const char *MODESTR[3] = { "rgb", "alpha", "luma" }; - -enum ALPHAOPERATIONS { ALPHA_CLEAR, ALPHA_MAX, ALPHA_MIN, ALPHA_ADD, ALPHA_SUB }; -const char *ALPHAOPERATIONSTR[5] = { "clear", "max", "min", "add", "sub" }; - - -/** Returns the index of \param string in \param stringList. - * Useful for assigning string parameters to enums. */ -static int stringValue( const char *string, const char **stringList, int max ) -{ - int i; - for ( i = 0; i < max; i++ ) - if ( strcmp( stringList[i], string ) == 0 ) - return i; - return 0; -} - -/** Sets "spline_is_dirty" to 1 if property "spline" was changed. - * We then know when to parse the json stored in "spline" */ -static void rotoPropertyChanged( mlt_service owner, mlt_filter this, char *name ) -{ - if ( !strcmp( name, "spline" ) ) - mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_spline_is_dirty", 1 ); -} - -/** Linear interp */ -static inline void lerp( const PointF *a, const PointF *b, PointF *result, double t ) -{ - result->x = a->x + ( b->x - a->x ) * t; - result->y = a->y + ( b->y - a->y ) * t; -} - -/** Linear interp. with t = 0.5 - * Speed gain? */ -static inline void lerpHalf( const PointF *a, const PointF *b, PointF *result ) -{ - result->x = ( a->x + b->x ) * .5; - result->y = ( a->y + b->y ) * .5; -} - -/** Helper for using qsort with an array of integers. */ -int ncompare( const void *a, const void *b ) -{ - return *(const int*)a - *(const int*)b; -} - -/** Turns a json array with two children into a point (x, y tuple). */ -static void jsonGetPoint( cJSON *json, PointF *point ) -{ - if ( cJSON_GetArraySize( json ) == 2 ) - { - point->x = json->child->valuedouble; - point->y = json->child->next->valuedouble; - } -} - -/** - * Turns the array of json elements into an array of Bézier points. - * \param array cJSON array. values have to be Bézier points: handle 1, point , handl2 - * ( [ [ [h1x, h1y], [px, py], [h2x, h2y] ], ... ] ) - * \param points pointer to array of points. Will be allocated and filled with the points in \param array - * \return number of points - */ -static int json2BCurves( cJSON *array, BPointF **points ) -{ - int count = cJSON_GetArraySize( array ); - cJSON *child = array->child; - *points = mlt_pool_alloc( count * sizeof( BPointF ) ); - - int i = 0; - do - { - if ( child && cJSON_GetArraySize( child ) == 3 ) - { - jsonGetPoint( child->child , &(*points)[i].h1 ); - jsonGetPoint( child->child->next, &(*points)[i].p ); - jsonGetPoint( child->child->next->next, &(*points)[i].h2 ); - i++; - } - } while ( child && ( child = child->next ) ); - - if ( i < count ) - *points = mlt_pool_realloc( *points, i * sizeof( BPointF ) ); - - return i; -} - -/** Blurs \param src horizontally. \See funtion blur. */ -static void blurHorizontal( uint8_t *src, uint8_t *dst, int width, int height, int radius) -{ - int x, y, kx, yOff, total, amount, amountInit; - amountInit = radius * 2 + 1; - for (y = 0; y < height; ++y) - { - total = 0; - yOff = y * width; - // Process entire window for first pixel - int size = MIN(radius + 1, width); - for ( kx = 0; kx < size; ++kx ) - total += src[yOff + kx]; - dst[yOff] = total / ( radius + 1 ); - // Subsequent pixels just update window total - for ( x = 1; x < width; ++x ) - { - amount = amountInit; - // Subtract pixel leaving window - if ( x - radius - 1 >= 0 ) - total -= src[yOff + x - radius - 1]; - else - amount -= radius - x; - // Add pixel entering window - if ( x + radius < width ) - total += src[yOff + x + radius]; - else - amount -= radius - width + x; - dst[yOff + x] = total / amount; - } - } -} - -/** Blurs \param src vertically. \See funtion blur. */ -static void blurVertical( uint8_t *src, uint8_t *dst, int width, int height, int radius) -{ - int x, y, ky, total, amount, amountInit; - amountInit = radius * 2 + 1; - for (x = 0; x < width; ++x) - { - total = 0; - int size = MIN(radius + 1, height); - for ( ky = 0; ky < size; ++ky ) - total += src[x + ky * width]; - dst[x] = total / ( radius + 1 ); - for ( y = 1; y < height; ++y ) - { - amount = amountInit; - if ( y - radius - 1 >= 0 ) - total -= src[( y - radius - 1 ) * width + x]; - else - amount -= radius - y; - if ( y + radius < height ) - total += src[( y + radius ) * width + x]; - else - amount -= radius - height + y; - dst[y * width + x] = total / amount; - } - } -} - -/** - * Blurs the \param map using a simple "average" blur. - * \param map Will be blured; 1bpp - * \param width x dimension of channel stored in \param map - * \param height y dimension of channel stored in \param map - * \param radius blur radius - * \param passes blur passes - */ -static void blur( uint8_t *map, int width, int height, int radius, int passes ) -{ - uint8_t *src = mlt_pool_alloc( width * height ); - uint8_t *tmp = mlt_pool_alloc( width * height ); - - int i; - for ( i = 0; i < passes; ++i ) - { - memcpy( src, map, width * height ); - blurHorizontal( src, tmp, width, height, radius ); - blurVertical( tmp, map, width, height, radius ); - } - - mlt_pool_release(src); - mlt_pool_release(tmp); -} - -/** - * Determines which points are located in the polygon and sets their value in \param map to \param value - * \param vertices points defining the polygon - * \param count number of vertices - * \param with x range - * \param height y range - * \param value value identifying points in the polygon - * \param map array of integers of the dimension width * height. - * The map entries belonging to the points in the polygon will be set to \param set * 255 the others to !set * 255. - */ -static void fillMap( PointF *vertices, int count, int width, int height, int invert, uint8_t *map ) -{ - int nodes, nodeX[1024], pixelY, i, j, value; - - value = !invert * 255; - memset( map, invert * 255, width * height ); - - // Loop through the rows of the image - for ( pixelY = 0; pixelY < height; pixelY++ ) - { - /* - * Build a list of nodes. - * nodes are located at the borders of the polygon - * and therefore indicate a move from in to out or vice versa - */ - nodes = 0; - for ( i = 0, j = count - 1; i < count; j = i++ ) - if ( (vertices[i].y > (double)pixelY) != (vertices[j].y > (double)pixelY) ) - nodeX[nodes++] = (int)(vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x) ); - - qsort( nodeX, nodes, sizeof( int ), ncompare ); - - // Set map values for points between the node pairs to 1 - for ( i = 0; i < nodes; i += 2 ) - { - if ( nodeX[i] >= width ) - break; - - if ( nodeX[i+1] > 0 ) - { - nodeX[i] = MAX( 0, nodeX[i] ); - nodeX[i+1] = MIN( nodeX[i+1], width ); - memset( map + width * pixelY + nodeX[i], value, nodeX[i+1] - nodeX[i] ); - } - } - } -} - -/** Determines the point in the middle of the Bézier curve (t = 0.5) defined by \param p1 and \param p2 - * using De Casteljau's algorithm. - */ -static void deCasteljau( BPointF *p1, BPointF *p2, BPointF *mid ) -{ - struct PointF ab, bc, cd; - - lerpHalf( &(p1->p), &(p1->h2), &ab ); - lerpHalf( &(p1->h2), &(p2->h1), &bc ); - lerpHalf( &(p2->h1), &(p2->p), &cd ); - lerpHalf( &ab, &bc, &(mid->h1) ); // mid->h1 = abbc - lerpHalf( &bc, &cd, &(mid->h2) ); // mid->h2 = bccd - lerpHalf( &(mid->h1), &(mid->h2), &(mid->p) ); - - p1->h2 = ab; - p2->h1 = cd; -} - -/** - * Calculates points for the cubic Bézier curve defined by \param p1 and \param p2. - * Points are calculated until the squared distanced between neighbour points is smaller than 2. - * \param points Pointer to list of points. Will be allocted and filled with calculated points. - * \param count Number of calculated points in \param points - * \param size Allocated size of \param points (in elements not in bytes) - */ -static void curvePoints( BPointF p1, BPointF p2, PointF **points, int *count, int *size ) -{ - double errorSqr = SQR( p1.p.x - p2.p.x ) + SQR( p1.p.y - p2.p.y ); - - if ( *size + 1 >= *count ) - { - *size += (int)sqrt( errorSqr / 2 ); - *points = mlt_pool_realloc( *points, *size * sizeof ( struct PointF ) ); - } - - (*points)[(*count)++] = p1.p; - - if ( errorSqr <= 2 ) - return; - - BPointF mid; - deCasteljau( &p1, &p2, &mid ); - - curvePoints( p1, mid, points, count, size ); - - curvePoints( mid, p2, points, count, size ); - - (*points)[*(count)++] = p2.p; -} - -/** Do it :-). -*/ -static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) -{ - mlt_properties unique = mlt_frame_pop_service( frame ); - - int mode = mlt_properties_get_int( unique, "mode" ); - - // Get the image - if ( mode == MODE_RGB ) - *format = mlt_image_rgb24; - int error = mlt_frame_get_image( frame, image, format, width, height, writable ); - - // Only process if we have no error and a valid colour space - if ( !error ) - { - BPointF *bpoints; - struct PointF *points; - int bcount, length, count, size, i, j; - bpoints = mlt_properties_get_data( unique, "points", &length ); - bcount = length / sizeof( BPointF ); - - for ( i = 0; i < bcount; i++ ) - { - // map to image dimensions - bpoints[i].h1.x *= *width; - bpoints[i].p.x *= *width; - bpoints[i].h2.x *= *width; - bpoints[i].h1.y *= *height; - bpoints[i].p.y *= *height; - bpoints[i].h2.y *= *height; - } - - count = 0; - size = 1; - points = mlt_pool_alloc( size * sizeof( struct PointF ) ); - for ( i = 0; i < bcount; i++ ) - { - j = (i + 1) % bcount; - curvePoints( bpoints[i], bpoints[j], &points, &count, &size ); - } - - if ( count ) - { - length = *width * *height; - uint8_t *map = mlt_pool_alloc( length ); - int invert = mlt_properties_get_int( unique, "invert" ); - fillMap( points, count, *width, *height, invert, map ); - - int feather = mlt_properties_get_int( unique, "feather" ); - if ( feather && mode != MODE_RGB ) - blur( map, *width, *height, feather, mlt_properties_get_int( unique, "feather_passes" ) ); - - int bpp; - size = mlt_image_format_size( *format, *width, *height, &bpp ); - uint8_t *p = *image; - uint8_t *q = *image + size; - - i = 0; - uint8_t *alpha; - - switch ( mode ) - { - case MODE_RGB: - // *format == mlt_image_rgb24 - while ( p != q ) - { - if ( !map[i++] ) - p[0] = p[1] = p[2] = 0; - p += 3; - } - break; - case MODE_LUMA: - switch ( *format ) - { - case mlt_image_rgb24: - case mlt_image_rgb24a: - case mlt_image_opengl: - while ( p != q ) - { - p[0] = p[1] = p[2] = map[i++]; - p += bpp; - } - break; - case mlt_image_yuv422: - while ( p != q ) - { - p[0] = map[i++]; - p[1] = 128; - p += 2; - } - break; - case mlt_image_yuv420p: - memcpy( p, map, length ); - memset( p + length, 128, length / 2 ); - break; - default: - break; - } - break; - case MODE_ALPHA: - switch ( *format ) - { - case mlt_image_rgb24a: - case mlt_image_opengl: - switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) - { - case ALPHA_CLEAR: - while ( p != q ) - { - p[3] = map[i++]; - p += 4; - } - break; - case ALPHA_MAX: - while ( p != q ) - { - p[3] = MAX( p[3], map[i] ); - p += 4; - i++; - } - break; - case ALPHA_MIN: - while ( p != q ) - { - p[3] = MIN( p[3], map[i] ); - p += 4; - i++; - } - break; - case ALPHA_ADD: - while ( p != q ) - { - p[3] = MIN( p[3] + map[i], 255 ); - p += 4; - i++; - } - break; - case ALPHA_SUB: - while ( p != q ) - { - p[3] = MAX( p[3] - map[i], 0 ); - p += 4; - i++; - } - break; - } - break; - default: - alpha = mlt_frame_get_alpha_mask( frame ); - switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) - { - case ALPHA_CLEAR: - memcpy( alpha, map, length ); - break; - case ALPHA_MAX: - for ( ; i < length; i++, alpha++ ) - *alpha = MAX( map[i], *alpha ); - break; - case ALPHA_MIN: - for ( ; i < length; i++, alpha++ ) - *alpha = MIN( map[i], *alpha ); - break; - case ALPHA_ADD: - for ( ; i < length; i++, alpha++ ) - *alpha = MIN( *alpha + map[i], 255 ); - break; - case ALPHA_SUB: - for ( ; i < length; i++, alpha++ ) - *alpha = MAX( *alpha - map[i], 0 ); - break; - } - break; - } - break; - } - - mlt_pool_release( map ); - } - - mlt_pool_release( points ); - } - - return error; -} - -/** Filter processing. -*/ -static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) -{ - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - int splineIsDirty = mlt_properties_get_int( properties, "_spline_is_dirty" ); - char *modeStr = mlt_properties_get( properties, "mode" ); - cJSON *root = mlt_properties_get_data( properties, "_spline_parsed", NULL ); - - if ( splineIsDirty || root == NULL ) - { - // we need to (re-)parse - char *spline = mlt_properties_get( properties, "spline" ); - root = cJSON_Parse( spline ); - mlt_properties_set_data( properties, "_spline_parsed", root, 0, (mlt_destructor)cJSON_Delete, NULL ); - mlt_properties_set_int( properties, "_spline_is_dirty", 0 ); - } - - if ( root == NULL ) - return frame; - - BPointF *points; - int count, i; - - if ( root->type == cJSON_Array ) - { - /* - * constant - */ - count = json2BCurves( root, &points ); - } - else if ( root->type == cJSON_Object ) - { - /* - * keyframes - */ - - mlt_position time, pos1, pos2; - time = mlt_frame_get_position( frame ); - - cJSON *keyframe = root->child; - cJSON *keyframeOld = keyframe; - - if ( !keyframe ) - return frame; - - while ( atoi( keyframe->string ) < time && keyframe->next ) - { - keyframeOld = keyframe; - keyframe = keyframe->next; - } - - pos1 = atoi( keyframeOld->string ); - pos2 = atoi( keyframe->string ); - - if ( pos1 >= pos2 || time >= pos2 ) - { - // keyframes in wrong order or before first / after last keyframe - count = json2BCurves( keyframe, &points ); - } - else - { - /* - * pos1 < time < pos2 - */ - - BPointF *p1, *p2; - int c1 = json2BCurves( keyframeOld, &p1 ); - int c2 = json2BCurves( keyframe, &p2 ); - - // range 0-1 - double position = ( time - pos1 ) / (double)( pos2 - pos1 + 1 ); - - count = MIN( c1, c2 ); // additional points are ignored - points = mlt_pool_alloc( count * sizeof( BPointF ) ); - for ( i = 0; i < count; i++ ) - { - lerp( &(p1[i].h1), &(p2[i].h1), &(points[i].h1), position ); - lerp( &(p1[i].p), &(p2[i].p), &(points[i].p), position ); - lerp( &(p1[i].h2), &(p2[i].h2), &(points[i].h2), position ); - } - - mlt_pool_release( p1 ); - mlt_pool_release( p2 ); - } - } - else - { - return frame; - } - - mlt_properties unique = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); - mlt_properties_set_data( unique, "points", points, count * sizeof( BPointF ), (mlt_destructor)mlt_pool_release, NULL ); - mlt_properties_set_int( unique, "mode", stringValue( modeStr, MODESTR, 3 ) ); - mlt_properties_set_int( unique, "alpha_operation", stringValue( mlt_properties_get( properties, "alpha_operation" ), ALPHAOPERATIONSTR, 5 ) ); - mlt_properties_set_int( unique, "invert", mlt_properties_get_int( properties, "invert" ) ); - mlt_properties_set_int( unique, "feather", mlt_properties_get_int( properties, "feather" ) ); - mlt_properties_set_int( unique, "feather_passes", mlt_properties_get_int( properties, "feather_passes" ) ); - mlt_frame_push_service( frame, unique ); - mlt_frame_push_get_image( frame, filter_get_image ); - - return frame; -} - -/** Constructor for the filter. -*/ -mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) -{ - mlt_filter filter = mlt_filter_new( ); - if ( filter ) - { - filter->process = filter_process; - mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); - mlt_properties_set( properties, "mode", "alpha" ); - mlt_properties_set( properties, "alpha_operation", "clear" ); - mlt_properties_set_int( properties, "invert", 0 ); - mlt_properties_set_int( properties, "feather", 0 ); - mlt_properties_set_int( properties, "feather_passes", 1 ); - if ( arg ) - mlt_properties_set( properties, "spline", arg ); - - mlt_events_listen( properties, filter, "property-changed", (mlt_listener)rotoPropertyChanged ); - } - return filter; -} diff -Nru mlt-0.9.0/src/modules/rotoscoping/filter_rotoscoping.yml mlt-0.9.2/src/modules/rotoscoping/filter_rotoscoping.yml --- mlt-0.9.0/src/modules/rotoscoping/filter_rotoscoping.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/filter_rotoscoping.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ -schema_version: 0.1 -type: filter -identifier: rotoscoping -title: Rotoscoping -copyright: Copyright (C) 2011 Till Theato -version: 0.3 -license: GPL -language: en -url: none -creator: Till Theato -tags: - - Video -description: Keyframable vector based rotoscoping - -bugs: - - in some cases top most row in polygon is assigned to outside - -parameters: - - identifier: mode - title: Mode - type: string - description: How to visualize the area described by the spline - readonly: no - required: no - default: alpha - mutable: yes - widget: dropdown - values: - - alpha - - luma - - rgb - - - identifier: alpha_operation - title: Alpha Operation - type: string - description: | - How to proceed with the current alpha mask (only if mode = alpha). - clear = existing alpha mask is overwritten - max = maximum: existing alpha mask, mask generated by this filter - min = minimum: existing alpha mask, mask generated by this filter - add = existing alpha mask + generated mask - sub = existing alpha mask - generated mask - readonly: no - required: no - default: clear - mutable: yes - widget: dropdown - values: - - clear - - max - - min - - add - - sub - - - identifier: invert - title: Invert - type: integer - description: use area inside of spline (0) or the outside (1) - readonly: no - required: no - minimum: 0 - maximum: 1 - default: 0 - mutable: yes - widget: checkbox - - - identifier: feather - title: Feather - type: integer - description: amount of feathering (radius of "average" blur applied on mask) - readonly: no - required: no - minimum: 0 - maximum: 1000 # no real limit - default: 0 - mutable: yes - widget: spinner - - - identifier: feather_passes - title: Feathering passes - type: integer - description: number of blur (feathering) passes - readonly: no - required: no - minimum: 1 - maximum: 1000 # no real limit - default: 1 - mutable: yes - widget: spinner - - - identifier: spline - title: Spline - type: string - description: > - The spline, a list of cubic Bézier curves, is described using JSON. - The most basic parts are the coordinate tuples: [x, y]; x,y will be mapped from the range 0-1 to the image dimensions. - Next layer are the Bézier points: [handle 1, point, handle 2] with handle 1, point, handle 2 being coordinate tuples - The spline is a list of Bézier points. - Optionally keyframes can be defined as a object of frame values, relative to the filter's in point, assigned to splines. - readonly: no - required: yes - mutable: yes diff -Nru mlt-0.9.0/src/modules/rotoscoping/Makefile mlt-0.9.2/src/modules/rotoscoping/Makefile --- mlt-0.9.0/src/modules/rotoscoping/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rotoscoping/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -CFLAGS += -I../.. - -LDFLAGS += -L../../framework -lmlt -lm - -include ../../../config.mak - -TARGET = ../libmltrotoscoping$(LIBSUF) - -OBJS = factory.o \ - filter_rotoscoping.o \ - cJSON.o - - -LDFLAGS += -lm - -SRCS := $(OBJS:.o=.c) - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) - -depend: $(SRCS) - $(CC) -MM $(CFLAGS) $^ 1>.depend - -distclean: clean - rm -f .depend - -clean: - rm -f $(OBJS) $(TARGET) - -install: all - install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" - install -d $(DESTDIR)$(mltdatadir)/rotoscoping - install -m 644 filter_rotoscoping.yml "$(DESTDIR)$(mltdatadir)/rotoscoping" - -ifneq ($(wildcard .depend),) -include .depend -endif diff -Nru mlt-0.9.0/src/modules/rtaudio/Makefile mlt-0.9.2/src/modules/rtaudio/Makefile --- mlt-0.9.0/src/modules/rtaudio/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/rtaudio/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -24,8 +24,8 @@ #CXXFLAGS +=-D__WINDOWS_ASIO__ else ifeq ($(targetos), Linux) CXXFLAGS += -D__LINUX_ALSA__ -CXXFLAGS += `pkg-config --cflags alsa` -LDFLAGS += `pkg-config --libs alsa` +CXXFLAGS += $(shell pkg-config --cflags alsa) +LDFLAGS += $(shell pkg-config --libs alsa) else ifeq ($(targetos), NetBSD) CXXFLAGS += -D__LINUX_OSS__ LDFLAGS += -lossaudio diff -Nru mlt-0.9.0/src/modules/sdl/consumer_sdl.c mlt-0.9.2/src/modules/sdl/consumer_sdl.c --- mlt-0.9.0/src/modules/sdl/consumer_sdl.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/sdl/consumer_sdl.c 2014-06-29 20:23:17.000000000 +0000 @@ -256,9 +256,7 @@ if ( mlt_properties_get_int( self->properties, "fullscreen" ) ) { const SDL_VideoInfo *vi; - pthread_mutex_lock( &mlt_sdl_mutex ); vi = SDL_GetVideoInfo(); - pthread_mutex_unlock( &mlt_sdl_mutex ); self->window_width = vi->current_w; self->window_height = vi->current_h; self->sdl_flags |= SDL_FULLSCREEN; diff -Nru mlt-0.9.0/src/modules/sdl/Makefile mlt-0.9.2/src/modules/sdl/Makefile --- mlt-0.9.0/src/modules/sdl/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/sdl/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -21,9 +21,9 @@ LDFLAGS += -lX11 endif -CFLAGS += `sdl-config --cflags` +CFLAGS += $(shell sdl-config --cflags) -LDFLAGS += `sdl-config --libs` +LDFLAGS += $(shell sdl-config --libs) ifeq ($(WITH_SDL_IMAGE),1) OBJS += producer_sdl_image.o diff -Nru mlt-0.9.0/src/modules/sox/configure mlt-0.9.2/src/modules/sox/configure --- mlt-0.9.0/src/modules/sox/configure 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/sox/configure 2014-06-29 20:23:17.000000000 +0000 @@ -39,46 +39,10 @@ echo "CFLAGS += $(libst-config --cflags) -I../.." > config.mak echo "LDFLAGS += -lst $(libst-config --libs) $libsndfile $libsamplerate" >> config.mak else - sox --version 2> /dev/null | grep 'v14.' > /dev/null - disable_sox=$? - if [ $disable_sox -eq 0 ] - then - LIBDIR=lib - bits=$(uname -m) - case $bits in - x86_64) - [ -d /usr/lib/lib64 ] && export LIBDIR=lib64 || export LIBDIR=lib - ;; - *) - export LIBDIR=lib - ;; - esac - - sox=$(which sox) - # chop sox - soxdir=$(dirname $sox) - # chop bin - soxdir=$(dirname $soxdir) - - # determine if we need libsamplerate - $LDD "$sox" | grep libsamplerate > /dev/null - [ $? -eq 0 ] && libsamplerate="-lsamplerate" - - # determine if we need libsfx - $LDD $(which sox) | grep libsfx > /dev/null - [ $? -eq 0 ] && libsfx="-lsfx" - - echo "CFLAGS += -DSOX14 -I$soxdir/include" > config.mak - echo "LDFLAGS += -L$soxdir/$LIBDIR -lsox $libsfx $libsamplerate" >> config.mak - fi + echo "- sox not found: disabling" + touch ../disable-sox fi fi - if [ "$disable_sox" != "0" ] - then - echo "- sox not found: disabling" - touch ../disable-sox - fi - exit 0 fi diff -Nru mlt-0.9.0/src/modules/sox/filter_sox.c mlt-0.9.2/src/modules/sox/filter_sox.c --- mlt-0.9.0/src/modules/sox/filter_sox.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/sox/filter_sox.c 2014-06-29 20:23:17.000000000 +0000 @@ -57,6 +57,7 @@ #define BUFFER_LEN 8192 #define AMPLITUDE_NORM 0.2511886431509580 /* -12dBFS */ #define AMPLITUDE_MIN 0.00001 +#define DBFSTOAMP(x) pow(10,(x)/20.0) /** Compute the mean of a set of doubles skipping unset values flagged as -1 */ @@ -311,7 +312,17 @@ if ( use_peak ) normalised_gain = ST_SSIZE_MIN / -peak; else - normalised_gain = AMPLITUDE_NORM / rms; + { + double gain = DBFSTOAMP(-12); // default -12 dBFS + char *p = mlt_properties_get( filter_properties, "analysis_level" ); + if (p) + { + gain = mlt_properties_get_double( filter_properties, "analysis_level" ); + if ( strstr( p, "dB" ) ) + gain = DBFSTOAMP( gain ); + } + normalised_gain = gain / rms; + } // Set properties for serialization snprintf( effect, sizeof(effect), "vol %f", normalised_gain ); diff -Nru mlt-0.9.0/src/modules/sox/filter_sox.yml mlt-0.9.2/src/modules/sox/filter_sox.yml --- mlt-0.9.0/src/modules/sox/filter_sox.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/sox/filter_sox.yml 2014-06-29 20:23:17.000000000 +0000 @@ -16,7 +16,8 @@ - Some effects have a temporal side-effect that do not work well. parameters: - - identifier: argument + - identifier: effect + argument: yes title: Effect name and options type: string format: effect [options] @@ -26,6 +27,17 @@ The results are put into the level, peak, and gain properties as well as this effect property as the parameter to the vol effect. + - identifier: analysis_level + title: Normalization level + type: string + default: -12dBFS + description: > + Normalize the volume to the specified amplitude. + The normalization may be indicated as a floating point value of the + relative volume with 1.0 being maximum. + The normalization may also be indicated as a numeric value with the + suffix "dB" to set the amplitude in decibels. + - identifier: level title: Signal power level (RMS) type: float diff -Nru mlt-0.9.0/src/modules/videostab/filter_videostab2.c mlt-0.9.2/src/modules/videostab/filter_videostab2.c --- mlt-0.9.0/src/modules/videostab/filter_videostab2.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/videostab/filter_videostab2.c 2014-06-29 20:23:17.000000000 +0000 @@ -134,6 +134,13 @@ // Service locks are for concurrency control mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + // Handle signal from app to re-init data + if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "refresh" ) ) + { + mlt_properties_set( MLT_FILTER_PROPERTIES(filter) , "refresh", NULL ); + data->initialized = 0; + } + if ( !vectors) { if ( !data->initialized ) { @@ -172,7 +179,7 @@ // Load analysis results from property data->initialized = 2; - int interp = 2; + int interp = 2; // default to bilinear float scale_zoom=1.0; if ( *width != mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" ) ) scale_zoom = (float) *width / (float) mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" ); @@ -180,12 +187,6 @@ interp = 0; else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) interp = 1; - else if ( strcmp( interps, "bilinear" ) == 0 ) - interp = 2; - else if ( strcmp( interps, "bicubic" ) == 0 ) - interp = 3; - else if ( strcmp( interps, "bicublin" ) == 0 ) - interp = 4; data->trans->interpoltype = interp; data->trans->smoothing = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "smoothing" ); diff -Nru mlt-0.9.0/src/modules/videostab/filter_videostab2.yml mlt-0.9.2/src/modules/videostab/filter_videostab2.yml --- mlt-0.9.0/src/modules/videostab/filter_videostab2.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/videostab/filter_videostab2.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,17 +1,19 @@ schema_version: 0.1 type: filter identifier: videostab2 -title: Videostab2 +title: Videostab2 (*deprecated*) copyright: Copyright (C) 2011 Marco Gittler creator: Marco Gittler version: 0.1 license: GPL language: en -url: ttp://public.hronopik.de/vid.stab/features.php?lang=en +url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > + This filter is deprecated and will eventually be removed; use the vidstab + filter instead. This filter requires two passes. The first pass performs analysis and stores the result in the vectors property. The second pass applies the vectors to the image. @@ -19,17 +21,18 @@ first pass. For the second pass, use output.mlt as the input. parameters: - - identifier: vectors + - identifier: vectors (transform) title: Vectors type: geometry description: > A set of X/Y coordinates by which to adjust the image. When this is not supplied, the filter computes the vectors and stores them in this property when the last frame has been processed. + - identifier: shakiness title: Shakiness type: integer - description: How shaky is the Video + description: How shaky is the video (analysis) readonly: no required: no minimum: 1 @@ -37,10 +40,11 @@ default: 4 mutable: yes widget: spinner + - identifier: accuracy title: Accuracy type: integer - description: Accuracy of Shakiness detection + description: Accuracy of shakiness detection (analysis) readonly: no required: no minimum: 1 @@ -48,10 +52,11 @@ default: 4 mutable: yes widget: spinner + - identifier: stepsize title: Stepsize type: integer - description: Stepsize of Detection process minimum around + description: Step size of search process (analysis) readonly: no required: no minimum: 0 @@ -59,10 +64,11 @@ default: 6 mutable: yes widget: spinner + - identifier: algo - title: Algo + title: Algorithm type: integer - description: 0 = Bruteforce, 1 = small measurement fields + description: 0 = brute force (translation only), 1 = small measurement fields (analysis) readonly: no required: no minimum: 0 @@ -70,10 +76,11 @@ default: 1 mutable: yes widget: spinner + - identifier: mincontrast - title: MinContrast + title: Minimum Contrast type: float - description: Below this Contrast Field is discarded + description: Below this contrast, a field is discarded (analysis) readonly: no required: no minimum: 0 @@ -81,10 +88,11 @@ default: 0.3 mutable: yes widget: spinner + - identifier: show title: Show type: integer - description: 0 = draw nothing, 1,2 show fields and transforms + description: 0 = draw nothing, 1 or 2 = show fields and transforms (analysis) readonly: no required: no minimum: 0 @@ -92,10 +100,11 @@ default: 0 mutable: yes widget: spinner + - identifier: smoothing title: Smoothing type: integer - description: number of frames for lowpass filtering ( ( num * 2 + 1) frames ) + description: number of frames for lowpass filtering (2N + 1 frames) (transform) readonly: no required: no minimum: 0 @@ -103,10 +112,12 @@ default: 10 mutable: yes widget: spinner + - identifier: maxshift title: Maxshift type: integer - description: max number of pixels to shift + description: maximum translation, -1 = no limit (transform) + unit: pixels readonly: no required: no minimum: -1 @@ -114,10 +125,12 @@ default: -1 mutable: yes widget: spinner + - identifier: maxangle title: Maxangle type: float - description: max angle to rotate (in rad) + description: max angle to rotate, -1 = no limit (transform) + unit: radians readonly: no required: no minimum: -1 @@ -125,10 +138,11 @@ default: -1 mutable: yes widget: spinner + - identifier: crop title: Crop type: integer - description: 0 = keep border, 1 = black background + description: 0 = keep border, 1 = black background (transform) readonly: no required: no minimum: 0 @@ -136,10 +150,11 @@ default: 0 mutable: yes widget: spinner + - identifier: invert title: Invert type: integer - description: Invert transform + description: Invert transforms (transform) readonly: no required: no minimum: 0 @@ -147,10 +162,11 @@ default: 0 mutable: yes widget: spinner + - identifier: relative title: Relative Transform type: integer - description: 0 = absolute transform, 1= relative + description: 0 = absolute, 1 = relative (transform) readonly: no required: no minimum: 0 @@ -158,10 +174,12 @@ default: 1 mutable: yes widget: spinner + - identifier: zoom title: Zoom type: integer - description: additional zoom during transform + description: additional zoom amount (transform) + unit: percent readonly: no required: no minimum: -500 @@ -169,10 +187,11 @@ default: 0 mutable: yes widget: spinner + - identifier: optzoom title: Optimal Zoom type: integer - description: use optimal zoom (calulated from transforms) + description: automatically determine optimal zoom (transform) readonly: no required: no minimum: 0 @@ -180,10 +199,11 @@ default: 1 mutable: yes widget: spinner + - identifier: sharpen title: Sharpen Image type: float - description: sharpen transformed image + description: amount of sharpening (transform) readonly: no required: no minimum: 0 @@ -192,3 +212,9 @@ mutable: yes widget: spinner + - identifier: refresh + description: > + Applications should set this when it updates a transform parameter. + type: integer + minimum: 0 + maximum: 1 diff -Nru mlt-0.9.0/src/modules/videostab/filter_videostab.yml mlt-0.9.2/src/modules/videostab/filter_videostab.yml --- mlt-0.9.0/src/modules/videostab/filter_videostab.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/videostab/filter_videostab.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,7 +1,7 @@ schema_version: 0.1 type: filter identifier: videostab -title: Videostab +title: Videostab (*deprecated*) copyright: Copyright (C) 2011 Marco Gittler creator: Marco Gittler < contributor: @@ -15,6 +15,8 @@ - Video description: Stabilize Video (for wiggly video) notes: > + This filter is deprecated and will eventually be removed; use the vidstab + filter instead. This filter requires two passes. The first pass performs analysis and stores the result in the vectors property. The second pass applies the vectors to the image. diff -Nru mlt-0.9.0/src/modules/videostab/transform_image.c mlt-0.9.2/src/modules/videostab/transform_image.c --- mlt-0.9.0/src/modules/videostab/transform_image.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/videostab/transform_image.c 2014-06-29 20:23:17.000000000 +0000 @@ -74,7 +74,7 @@ |-1, 3,-3, 1 | |a3| */ static short bicub_kernel(float t, short a0, short a1, short a2, short a3){ - return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2; + return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2; } /** interpolateBiCub: bi-cubic interpolation function using 4x4 pixel, see interpolate */ @@ -266,7 +266,7 @@ + zcos_a * y_d1 + c_s_y -t.y; for (z = 0; z < 3; z++) { // iterate over colors unsigned char* dest = &D_2[(x + y * td->width_dest)*3+z]; - interpolate(dest, myfloor(x_s), myfloor(y_s), D_1, + interpolate(dest, x_s, y_s, D_1, td->width_src, td->height_src, td->crop ? 16 : *dest,3,z); } diff -Nru mlt-0.9.0/src/modules/vid.stab/common.c mlt-0.9.2/src/modules/vid.stab/common.c --- mlt-0.9.0/src/modules/vid.stab/common.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/common.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,274 @@ +/* + * common.c + * Copyright (C) 2014 Brian Matherly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include "common.h" + +mlt_image_format validate_format( mlt_image_format format ) +{ + switch( format ) + { + case mlt_image_rgb24a: + case mlt_image_rgb24: + return mlt_image_rgb24; + case mlt_image_yuv420p: + return mlt_image_yuv420p; + default: + case mlt_image_none: + case mlt_image_yuv422: + case mlt_image_opengl: + case mlt_image_glsl: + case mlt_image_glsl_texture: + return mlt_image_yuv422; + } +} + +/** Convert an MLT image to one that can be used by VS. + * Use free_vsimage() when done with the resulting image. + */ + +VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t* mlt_img, uint8_t** vs_img ) +{ + switch( mlt_format ) + { + case mlt_image_rgb24: + // Convert packed RGB24 to planar YUV444 + // Note: vid.stab 0.98 does not seem to support PF_RGB24 + { + *vs_img = mlt_pool_alloc( width * height * 3 ); + int y, u, v, r, g, b; + int total = width * height + 1; + uint8_t* yp = *vs_img; + uint8_t* up = yp + ( width * height ); + uint8_t* vp = up + ( width * height ); + + while( --total ) + { + r = *mlt_img++; + g = *mlt_img++; + b = *mlt_img++; + RGB2YUV_601_SCALED(r, g, b, y, u, v); + *yp++ = y; + *up++ = u; + *vp++ = v; + } + + return PF_YUV444P; + } + case mlt_image_yuv420p: + // This format maps with no conversion + { + *vs_img = mlt_img; + return PF_YUV420P; + } + case mlt_image_yuv422: + // Convert packed YUV422 to planar YUV444 + // Note: vid.stab 0.98 seems to suffer chroma bleeding + // when using PF_YUV422P - which is why PF_YUV444P is used. + { + *vs_img = mlt_pool_alloc( width * height * 3 ); + uint8_t* yp = *vs_img; + uint8_t* up = yp + ( width * height ); + uint8_t* vp = up + ( width * height ); + int i, j, n = width / 2 + 1; + + for ( i = 0; i < height; i++ ) + { + j = n; + while ( --j ) + { + *yp++ = mlt_img[0]; + *up++ = mlt_img[1]; + *vp++ = mlt_img[3]; + *yp++ = mlt_img[2]; + *up++ = mlt_img[1]; + *vp++ = mlt_img[3]; + mlt_img += 4; + } + if ( width % 2 ) + { + *yp++ = mlt_img[0]; + *up++ = mlt_img[1]; + *vp++ = (mlt_img - 4)[3]; + mlt_img += 2; + } + } + + return PF_YUV444P; + } + default: + return PF_NONE; + } +} + +/** Convert a VS image back to the MLT image it originally came from in mltimage_to_vsimage(). + */ + +void vsimage_to_mltimage( uint8_t* vs_img, uint8_t* mlt_img, mlt_image_format mlt_format, int width, int height ) +{ + switch( mlt_format ) + { + case mlt_image_rgb24: + // Convert packet YUV444 to packed RGB24. + { + int y, u, v, r, g, b; + int total = width * height + 1; + uint8_t* yp = vs_img; + uint8_t* up = yp + ( width * height ); + uint8_t* vp = up + ( width * height ); + + while( --total ) + { + y = *yp++; + u = *up++; + v = *vp++; + YUV2RGB_601_SCALED( y, u, v, r, g, b ); + *mlt_img++ = r; + *mlt_img++ = g; + *mlt_img++ = b; + } + } + break; + case mlt_image_yuv420p: + // This format was never converted + break; + case mlt_image_yuv422: + // Convert planar YUV444 to packed YUV422 + { + uint8_t* yp = vs_img; + uint8_t* up = yp + ( width * height ); + uint8_t* vp = up + ( width * height ); + int i, j, n = width / 2 + 1; + + for ( i = 0; i < height; i++ ) + { + j = n; + while ( --j ) + { + *mlt_img++ = yp[0]; + *mlt_img++ = ( up[0] + up[1] ) >> 1; + *mlt_img++ = yp[1]; + *mlt_img++ = ( vp[0] + vp[1] ) >> 1; + yp += 2; + up += 2; + vp += 2; + } + if ( width % 2 ) + { + *mlt_img++ = yp[0]; + *mlt_img++ = up[0]; + yp += 1; + up += 1; + vp += 1; + } + } + } + break; + default: + break; + } +} + +/** Free an image allocated by mltimage_to_vsimage(). + */ + +void free_vsimage( uint8_t* vs_img, VSPixelFormat format ) +{ + if( format != PF_YUV420P ) + { + mlt_pool_release( vs_img ); + } +} + +/** Compare two VSMotionDetectConfig structures. + * Return 1 if they are different. 0 if they are the same. + */ + +int compare_motion_config( VSMotionDetectConfig* a, VSMotionDetectConfig* b ) +{ + if( a->shakiness != b->shakiness || + a->accuracy != b->accuracy || + a->stepSize != b->stepSize || + // Skip: Deprecated + // a->algo != b->algo || + a->virtualTripod != b->virtualTripod || + a->show != b->show || + // Skip: inconsequential? + // a->modName != b->modName || + a->contrastThreshold != b->contrastThreshold ) + { + return 1; + } + return 0; +} + +/** Compare two VSTransformConfig structures. + * Return 1 if they are different. 0 if they are the same. + */ + +int compare_transform_config( VSTransformConfig* a, VSTransformConfig* b ) +{ + if( a->relative != b->relative || + a->smoothing != b->smoothing || + a->crop != b->crop || + a->invert != b->invert || + a->zoom != b->zoom || + a->optZoom != b->optZoom || + a->zoomSpeed != b->zoomSpeed || + a->interpolType != b->interpolType || + a->maxShift != b->maxShift || + a->maxAngle != b->maxAngle || + // Skip: inconsequential? + // a->modName != b->modName || + // Skip: unused? + // a->verbose != b->verbose || + a->simpleMotionCalculation != b->simpleMotionCalculation || + // Skip: unused? + // a->storeTransforms != b->storeTransforms || + a->smoothZoom != b->smoothZoom || + a->camPathAlgo != b->camPathAlgo ) + { + return 1; + } + return 0; +} + +static int vs_log_wrapper( int type, const char *tag, const char *format, ... ) +{ + va_list vl; + + if ( type > mlt_log_get_level() ) + return VS_OK; + + va_start( vl, format ); + fprintf( stderr, "[%s] ", tag ); + vfprintf( stderr, format, vl ); + va_end( vl ); + + return VS_OK; +} + +void init_vslog() +{ + VS_ERROR_TYPE = MLT_LOG_ERROR; + VS_WARN_TYPE = MLT_LOG_WARNING; + VS_INFO_TYPE = MLT_LOG_INFO; + VS_MSG_TYPE = MLT_LOG_VERBOSE; + vs_log = vs_log_wrapper; +} diff -Nru mlt-0.9.0/src/modules/vid.stab/common.h mlt-0.9.2/src/modules/vid.stab/common.h --- mlt-0.9.0/src/modules/vid.stab/common.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/common.h 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * common.h + * Copyright (C) 2013 Jakub Ksiezniak + * Copyright (C) 2014 Brian Matherly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef VIDSTAB_COMMON_H_ +#define VIDSTAB_COMMON_H_ + +#include +#include + +mlt_image_format validate_format( mlt_image_format format ); +VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t* mlt_img, uint8_t** vs_img ); +void vsimage_to_mltimage( uint8_t* vs_img, uint8_t* mlt_img, mlt_image_format mlt_format, int width, int height ); +void free_vsimage( uint8_t* vs_img, VSPixelFormat format ); + +int compare_motion_config( VSMotionDetectConfig* a, VSMotionDetectConfig* b ); +int compare_transform_config( VSTransformConfig* a, VSTransformConfig* b ); + +void init_vslog(); + +#endif /* VIDSTAB_COMMON_H_ */ diff -Nru mlt-0.9.0/src/modules/vid.stab/configure mlt-0.9.2/src/modules/vid.stab/configure --- mlt-0.9.0/src/modules/vid.stab/configure 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/configure 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,33 @@ +#!/bin/sh + +if [ "$help" != "1" ] +then + if ! $(pkg-config vidstab) + then + echo "- vid.stab not found: disabling" + touch ../disable-vid.stab + exit 0 + fi + + minver="0.98" + modver=$(pkg-config --modversion vidstab) + pkg-config --exists "vidstab >= $minver" + if [ $? -ne 0 ] + then + echo "- vid.stab $modver found, but $minver or newer is required: disabling" + touch ../disable-vid.stab + exit 0 + fi + + echo > config.mak + case $targetos in + Darwin) + ;; + MinGW) + ;; + *) + ;; + esac + exit 0 +fi + diff -Nru mlt-0.9.0/src/modules/vid.stab/factory.c mlt-0.9.2/src/modules/vid.stab/factory.c --- mlt-0.9.0/src/modules/vid.stab/factory.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +extern mlt_filter filter_deshake_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); + +static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) +{ + char file[ PATH_MAX ]; + snprintf( file, PATH_MAX, "%s/vid.stab/filter_%s.yml", mlt_environment( "MLT_DATA" ), id ); + return mlt_properties_parse_yaml( file ); +} + +MLT_REPOSITORY +{ + MLT_REGISTER( filter_type, "deshake", filter_deshake_init ); + MLT_REGISTER( filter_type, "vidstab", filter_vidstab_init ); + + MLT_REGISTER_METADATA( filter_type, "deshake", metadata, "filter_deshake.yml" ); + MLT_REGISTER_METADATA( filter_type, "vidstab", metadata, "filter_vidstab.yml" ); +} diff -Nru mlt-0.9.0/src/modules/vid.stab/filter_deshake.cpp mlt-0.9.2/src/modules/vid.stab/filter_deshake.cpp --- mlt-0.9.0/src/modules/vid.stab/filter_deshake.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/filter_deshake.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,250 @@ +/* + * filter_deshake.cpp + * Copyright (C) 2013 Marco Gittler + * Copyright (C) 2013 Jakub Ksiezniak + * Copyright (C) 2014 Brian Matherly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +extern "C" +{ +#include +#include "common.h" +} + +#include +#include +#include + +typedef struct _deshake_data +{ + bool initialized; + VSMotionDetect md; + VSTransformData td; + VSSlidingAvgTrans avg; + VSMotionDetectConfig mconf; + VSTransformConfig tconf; + mlt_position lastFrame; +} DeshakeData; + +static void get_config( VSTransformConfig* tconf, VSMotionDetectConfig* mconf, mlt_filter filter, mlt_frame frame ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + const char* filterName = mlt_properties_get( properties, "mlt_service" ); + + memset( mconf, 0, sizeof(VSMotionDetectConfig) ); + *mconf = vsMotionDetectGetDefaultConfig( filterName ); + mconf->shakiness = mlt_properties_get_int( properties, "shakiness" ); + mconf->accuracy = mlt_properties_get_int(properties, "accuracy"); + mconf->stepSize = mlt_properties_get_int(properties, "stepsize"); + mconf->contrastThreshold = mlt_properties_get_double(properties, "mincontrast"); + + memset( tconf, 0, sizeof(VSTransformConfig) ); + *tconf = vsTransformGetDefaultConfig( filterName ); + tconf->smoothing = mlt_properties_get_int(properties, "smoothing"); + tconf->maxShift = mlt_properties_get_int(properties, "maxshift"); + tconf->maxAngle = mlt_properties_get_double(properties, "maxangle"); + tconf->crop = (VSBorderType) mlt_properties_get_int(properties, "crop"); + tconf->zoom = mlt_properties_get_int(properties, "zoom"); + tconf->optZoom = mlt_properties_get_int(properties, "optzoom"); + tconf->zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); + tconf->relative = 1; + + // by default a bicubic interpolation is selected + const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" ); + tconf->interpolType = VS_BiCubic; + if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 ) + tconf->interpolType = VS_Zero; + else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) + tconf->interpolType = VS_Linear; + else if ( strcmp( interps, "bilinear" ) == 0 ) + tconf->interpolType = VS_BiLinear; +} + +static int check_config( mlt_filter filter, mlt_frame frame ) +{ + DeshakeData *data = static_cast( filter->child ); + VSTransformConfig new_tconf; + VSMotionDetectConfig new_mconf; + + get_config( &new_tconf, &new_mconf, filter, frame ); + + if( compare_transform_config( &data->tconf, &new_tconf ) || + compare_motion_config( &data->mconf, &new_mconf ) ) + { + return 1; + } + + return 0; +} + +static void init_deshake( DeshakeData *data, mlt_filter filter, mlt_frame frame, + VSPixelFormat vs_format, int *width, int *height ) +{ + VSFrameInfo fiIn, fiOut; + + vsFrameInfoInit( &fiIn, *width, *height, vs_format ); + vsFrameInfoInit( &fiOut, *width, *height, vs_format ); + get_config( &data->tconf, &data->mconf, filter, frame ); + vsMotionDetectInit( &data->md, &data->mconf, &fiIn ); + vsTransformDataInit(&data->td, &data->tconf, &fiIn, &fiOut); + + data->avg.initialized = 0; +} + +static void clear_deshake(DeshakeData *data) +{ + if (data->initialized) + { + vsMotionDetectionCleanup(&data->md); + vsTransformDataCleanup(&data->td); + } +} + +static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, + int *width, int *height, int writable) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); + uint8_t* vs_image = NULL; + VSPixelFormat vs_format = PF_NONE; + + // VS only works on progressive frames + mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); + + *format = validate_format( *format ); + DeshakeData *data = static_cast(filter->child); + + int error = mlt_frame_get_image(frame, image, format, width, height, 1); + + // Convert the received image to a format vid.stab can handle + if ( !error ) + { + vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image ); + } + + if ( vs_image ) + { + // Service locks are for concurrency control + mlt_service_lock(MLT_FILTER_SERVICE(filter)); + + // clear deshake data, when seeking or dropping frames + mlt_position pos = mlt_filter_get_position( filter, frame ); + if( pos != data->lastFrame + 1 || + check_config( filter, frame) == 1 ) + { + clear_deshake( data ); + data->initialized = false; + } + data->lastFrame = pos; + + if ( !data->initialized ) + { + init_deshake( data, filter, frame, vs_format, width, height ); + data->initialized = true; + } + + VSMotionDetect* md = &data->md; + VSTransformData* td = &data->td; + LocalMotions localmotions; + VSTransform motion; + VSFrame vsFrame; + + vsFrameFillFromBuffer(&vsFrame, vs_image, &md->fi); + vsMotionDetection(md, &localmotions, &vsFrame); + + const char* filterName = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "mlt_service" ); + motion = vsSimpleMotionsToTransform(md->fi, filterName, &localmotions); + vs_vector_del(&localmotions); + + vsTransformPrepare(td, &vsFrame, &vsFrame); + + VSTransform t = vsLowPassTransforms(td, &data->avg, &motion); +// mlt_log_warning(filter, "Trans: det: %f %f %f \n\t\t act: %f %f %f %f", +// motion.x, motion.y, motion.alpha, +// t.x, t.y, t.alpha, t.zoom); + vsDoTransform(td, t); + vsTransformFinish(td); + + vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); + + mlt_service_unlock(MLT_FILTER_SERVICE(filter)); + + free_vsimage( vs_image, vs_format ); + } + + return error; +} + +static mlt_frame process_filter(mlt_filter filter, mlt_frame frame) +{ + mlt_frame_push_service(frame, filter); + mlt_frame_push_get_image(frame, get_image); + return frame; +} + +static void close_filter(mlt_filter filter) +{ + DeshakeData *data = static_cast(filter->child); + if (data) + { + clear_deshake(data); + delete data; + filter->child = NULL; + } +} + +extern "C" +{ + +mlt_filter filter_deshake_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + + DeshakeData *data = new DeshakeData; + memset(data, 0, sizeof(DeshakeData)); + + if ((filter = mlt_filter_new())) + { + filter->process = process_filter; + filter->close = close_filter; + filter->child = data; + + mlt_properties properties = MLT_FILTER_PROPERTIES(filter); + //properties for stabilize + mlt_properties_set(properties, "shakiness", "4"); + mlt_properties_set(properties, "accuracy", "4"); + mlt_properties_set(properties, "stepsize", "6"); + mlt_properties_set(properties, "mincontrast", "0.3"); + + //properties for transform + mlt_properties_set(properties, "smoothing", "15"); + mlt_properties_set(properties, "maxshift", "-1"); + mlt_properties_set(properties, "maxangle", "-1"); + mlt_properties_set(properties, "crop", "0"); + mlt_properties_set(properties, "zoom", "0"); + mlt_properties_set(properties, "optzoom", "1"); + mlt_properties_set(properties, "zoomspeed", "0.25"); + + init_vslog(); + + return filter; + } + + delete data; + return NULL; +} + +} diff -Nru mlt-0.9.0/src/modules/vid.stab/filter_deshake.yml mlt-0.9.2/src/modules/vid.stab/filter_deshake.yml --- mlt-0.9.0/src/modules/vid.stab/filter_deshake.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/filter_deshake.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,154 @@ +schema_version: 0.1 +type: filter +identifier: vid.stab.deshake +title: Vid.Stab Deshake +copyright: Jakub Ksiezniak +creator: Georg Martius +version: 1 +license: GPLv2 +language: en +url: http://public.hronopik.de/vid.stab/ +tags: + - Video +description: Stabilize Video (for wiggly/rolling video) +notes: > + Deshakes a video clip by extracting relative transformations + of subsequent frames and transforms the high-frequency away. + This is a single pass version of the vidstab filter. + +parameters: + - identifier: shakiness + title: Shakiness + type: integer + description: How shaky the video is. + readonly: no + required: no + minimum: 1 + maximum: 10 + default: 4 + mutable: yes + widget: spinner + + - identifier: accuracy + title: Accuracy + type: integer + description: The accuracy of shakiness detection. + readonly: no + required: no + minimum: 1 + maximum: 15 + default: 4 + mutable: yes + widget: spinner + + - identifier: stepsize + title: Stepsize + type: integer + description: The step size of the search process. + readonly: no + required: no + minimum: 0 + maximum: 100 + default: 6 + mutable: yes + widget: spinner + + - identifier: mincontrast + title: Minimum Contrast + type: float + description: Below this contrast, a field is discarded. + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0.3 + mutable: yes + widget: spinner + + - identifier: smoothing + title: Smoothing + type: integer + description: Number of frames for lowpass filtering (2N + 1 frames) + readonly: no + required: no + minimum: 0 + maximum: 100 + default: 15 + mutable: yes + widget: spinner + + - identifier: maxshift + title: Maxshift + type: integer + description: Maximum number of pixels to transform the image. -1 = no limit + unit: pixels + readonly: no + required: no + minimum: -1 + maximum: 1000 + default: -1 + mutable: yes + widget: spinner + + - identifier: maxangle + title: Maxangle + type: float + description: Maximum angle to rotate, -1 = no limit + unit: radians + readonly: no + required: no + minimum: -1 + maximum: 3.142 + default: -1 + mutable: yes + widget: spinner + + - identifier: crop + title: Crop + type: integer + description: 0 = keep border, 1 = black background + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + widget: spinner + + - identifier: zoom + title: Zoom + type: integer + description: Additional zoom amount + unit: percent + readonly: no + required: no + minimum: -500 + maximum: 500 + default: 0 + mutable: yes + widget: spinner + + - identifier: optzoom + title: Optimal Zoom + type: integer + description: Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom + readonly: no + required: no + minimum: 0 + maximum: 2 + default: 1 + mutable: yes + widget: spinner + + - identifier: zoomspeed + title: Optimal Zoom Speed + type: float + description: Zoom per frame (used when optzoom = 2) + unit: percent + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0.25 + mutable: yes + widget: spinner diff -Nru mlt-0.9.0/src/modules/vid.stab/filter_vidstab.cpp mlt-0.9.2/src/modules/vid.stab/filter_vidstab.cpp --- mlt-0.9.0/src/modules/vid.stab/filter_vidstab.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/filter_vidstab.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,458 @@ +/* + * filter_vidstab.cpp + * Copyright (C) 2013 Marco Gittler + * Copyright (C) 2013 Jakub Ksiezniak + * Copyright (C) 2014 Brian Matherly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +extern "C" +{ +#include +#include +#include +#include "common.h" +} + +#include +#include +#include + +typedef struct +{ + VSMotionDetect md; + FILE* results; + mlt_position last_position; +} vs_analyze; + +typedef struct +{ + VSTransformData td; + VSTransformConfig conf; + VSTransformations trans; +} vs_apply; + +typedef struct +{ + vs_analyze* analyze_data; + vs_apply* apply_data; +} vs_data; + +static void get_transform_config( VSTransformConfig* conf, mlt_filter filter, mlt_frame frame ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + const char* filterName = mlt_properties_get( properties, "mlt_service" ); + + *conf = vsTransformGetDefaultConfig( filterName ); + conf->smoothing = mlt_properties_get_int( properties, "smoothing" ); + conf->maxShift = mlt_properties_get_int( properties, "maxshift" ); + conf->maxAngle = mlt_properties_get_double( properties, "maxangle" ); + conf->crop = (VSBorderType)mlt_properties_get_int( properties, "crop" ); + conf->zoom = mlt_properties_get_int( properties, "zoom" ); + conf->optZoom = mlt_properties_get_int( properties, "optzoom" ); + conf->zoomSpeed = mlt_properties_get_double( properties, "zoomspeed" ); + conf->relative = mlt_properties_get_int( properties, "relative" ); + conf->invert = mlt_properties_get_int( properties, "invert" ); + if ( mlt_properties_get_int( properties, "tripod" ) != 0 ) + { + // Virtual tripod mode: relative=False, smoothing=0 + conf->relative = 0; + conf->smoothing = 0; + } + + // by default a bicubic interpolation is selected + const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" ); + conf->interpolType = VS_BiCubic; + if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 ) + conf->interpolType = VS_Zero; + else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) + conf->interpolType = VS_Linear; + else if ( strcmp( interps, "bilinear" ) == 0 ) + conf->interpolType = VS_BiLinear; +} + +static int check_apply_config( mlt_filter filter, mlt_frame frame ) +{ + vs_apply* apply_data = ((vs_data*)filter->child)->apply_data; + + if( apply_data ) + { + VSTransformConfig new_conf; + memset( &new_conf, 0, sizeof(VSTransformConfig) ); + get_transform_config( &new_conf, filter, frame ); + return compare_transform_config( &apply_data->conf, &new_conf ); + } + + return 0; +} + +static void destory_apply_data( vs_apply* apply_data ) +{ + if ( apply_data ) + { + vsTransformDataCleanup( &apply_data->td ); + vsTransformationsCleanup( &apply_data->trans ); + free( apply_data ); + } +} + +static void init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + vs_data* data = (vs_data*)filter->child; + vs_apply* apply_data = (vs_apply*)calloc( 1, sizeof(vs_apply) ); + char* filename = mlt_properties_get( properties, "results" ); + memset( apply_data, 0, sizeof( vs_apply ) ); + + mlt_log_info( MLT_FILTER_SERVICE(filter), "Load results from %s\n", filename ); + + // Initialize the VSTransformConfig + get_transform_config( &apply_data->conf, filter, frame ); + + // Initialize VSTransformData + VSFrameInfo fi_src, fi_dst; + vsFrameInfoInit( &fi_src, width, height, vs_format ); + vsFrameInfoInit( &fi_dst, width, height, vs_format ); + vsTransformDataInit( &apply_data->td, &apply_data->conf, &fi_src, &fi_dst ); + + // Initialize VSTransformations + vsTransformationsInit( &apply_data->trans ); + + // Convert file name string encoding. + mlt_properties_from_utf8( properties, "results", "_results" ); + filename = mlt_properties_get( properties, "_results" ); + + // Load the motions from the analyze step and convert them to VSTransformations + FILE* f = fopen( filename, "r" ); + VSManyLocalMotions mlms; + + if( vsReadLocalMotionsFile( f, &mlms ) == VS_OK ) + { + int i = 0; + mlt_log_info( MLT_FILTER_SERVICE(filter), "Successfully loaded %d motions\n", vs_vector_size( &mlms ) ); + vsLocalmotions2Transforms( &apply_data->td, &mlms, &apply_data->trans ); + vsPreprocessTransforms( &apply_data->td, &apply_data->trans ); + + // Free the MultipleLocalMotions + for( i = 0; i < vs_vector_size( &mlms ); i++ ) + { + LocalMotions* lms = (LocalMotions*)vs_vector_get( &mlms, i ); + if( lms ) + { + vs_vector_del( lms ); + } + } + vs_vector_del( &mlms ); + + data->apply_data = apply_data; + } + else + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Can not read results file: %s\n", filename ); + destory_apply_data( apply_data ); + data->apply_data = NULL; + } + + if( f ) + { + fclose( f ); + } +} + +void destory_analyze_data( vs_analyze* analyze_data ) +{ + if ( analyze_data ) + { + vsMotionDetectionCleanup( &analyze_data->md ); + if( analyze_data->results ) + { + fclose( analyze_data->results ); + analyze_data->results = NULL; + } + free( analyze_data ); + } +} + +static void init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + vs_data* data = (vs_data*)filter->child; + vs_analyze* analyze_data = (vs_analyze*)calloc( 1, sizeof(vs_analyze) ); + memset( analyze_data, 0, sizeof(vs_analyze) ); + + // Initialize a VSMotionDetectConfig + const char* filterName = mlt_properties_get( properties, "mlt_service" ); + VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig( filterName ); + conf.shakiness = mlt_properties_get_int( properties, "shakiness" ); + conf.accuracy = mlt_properties_get_int( properties, "accuracy" ); + conf.stepSize = mlt_properties_get_int( properties, "stepsize" ); + conf.contrastThreshold = mlt_properties_get_double( properties, "mincontrast" ); + conf.show = mlt_properties_get_int( properties, "show" ); + conf.virtualTripod = mlt_properties_get_int( properties, "tripod" ); + + // Initialize a VSFrameInfo + VSFrameInfo fi; + vsFrameInfoInit( &fi, width, height, vs_format ); + + // Initialize the saved VSMotionDetect + vsMotionDetectInit( &analyze_data->md, &conf, &fi ); + + // Initialize the file to save results to + char* filename = mlt_properties_get( properties, "filename" ); + analyze_data->results = fopen( filename, "w" ); + if ( vsPrepareFile( &analyze_data->md, analyze_data->results ) != VS_OK ) + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Can not write to results file: %s\n", filename ); + destory_analyze_data( analyze_data ); + data->analyze_data = NULL; + } + else + { + data->analyze_data = analyze_data; + } +} + +static int apply_results( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height ) +{ + int error = 0; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + vs_data* data = (vs_data*)filter->child; + + if ( check_apply_config( filter, frame ) || + mlt_properties_get_int( properties, "reload" ) ) + { + mlt_properties_set_int( properties, "reload", 0 ); + destory_apply_data( data->apply_data ); + data->apply_data = NULL; + } + + // Init transform data if necessary (first time) + if ( !data->apply_data ) + { + init_apply_data( filter, frame, vs_format, width, height ); + } + + if( data->apply_data ) + { + // Apply transformations to this image + VSTransformData* td = &data->apply_data->td; + VSTransformations* trans = &data->apply_data->trans; + VSFrame vsFrame; + vsFrameFillFromBuffer( &vsFrame, vs_image, vsTransformGetSrcFrameInfo( td ) ); + trans->current = mlt_filter_get_position( filter, frame ); + vsTransformPrepare( td, &vsFrame, &vsFrame ); + VSTransform t = vsGetNextTransform( td, trans ); + vsDoTransform( td, t ); + vsTransformFinish( td ); + } + + return error; +} + +static void analyze_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height ) +{ + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + vs_data* data = (vs_data*)filter->child; + mlt_position pos = mlt_filter_get_position( filter, frame ); + + // If any frames are skipped, analysis data will be incomplete. + if( data->analyze_data && pos != data->analyze_data->last_position + 1 ) + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Bad frame sequence\n" ); + destory_analyze_data( data->analyze_data ); + data->analyze_data = NULL; + } + + if ( !data->analyze_data && pos == 0 ) + { + // Analysis must start on the first frame + init_analyze_data( filter, frame, vs_format, width, height ); + } + + if( data->analyze_data ) + { + // Initialize the VSFrame to be analyzed. + VSMotionDetect* md = &data->analyze_data->md; + LocalMotions localmotions; + VSFrame vsFrame; + vsFrameFillFromBuffer( &vsFrame, vs_image, &md->fi ); + + // Detect and save motions. + if( vsMotionDetection( md, &localmotions, &vsFrame ) == VS_OK ) + { + vsWriteToFile( md, data->analyze_data->results, &localmotions); + vs_vector_del( &localmotions ); + } + else + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Motion detection failed\n" ); + destory_analyze_data( data->analyze_data ); + data->analyze_data = NULL; + } + + // Publish the motions if this is the last frame. + if ( pos + 1 == mlt_filter_get_length2( filter, frame ) ) + { + mlt_log_info( MLT_FILTER_SERVICE(filter), "Analysis complete\n" ); + destory_analyze_data( data->analyze_data ); + data->analyze_data = NULL; + mlt_properties_set( properties, "results", mlt_properties_get( properties, "filename" ) ); +#ifdef WIN32 + mlt_properties_set_int( properties, "_ignore_results", 1 ); +#endif + } + else + { + data->analyze_data->last_position = pos; + } + } +} + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + uint8_t* vs_image = NULL; + VSPixelFormat vs_format = PF_NONE; + + // VS only works on progressive frames + mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); + + *format = validate_format( *format ); + + int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + + // Convert the received image to a format vid.stab can handle + if ( !error ) + { + vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image ); + } + + if( vs_image ) + { + mlt_service_lock( MLT_FILTER_SERVICE(filter) ); + + char* results = mlt_properties_get( properties, "results" ); +#ifdef WIN32 + if( !mlt_properties_get_int( properties, "_ignore_results" ) ) +#endif + if( results && strcmp( results, "" ) ) + { + apply_results( filter, frame, vs_image, vs_format, *width, *height ); + vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); + } + else + { + analyze_image( filter, frame, vs_image, vs_format, *width, *height ); + if( mlt_properties_get_int( properties, "show" ) == 1 ) + { + vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); + } + } + + mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); + + free_vsimage( vs_image, vs_format ); + } + + return error; +} + +static mlt_frame process_filter( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +static void filter_close( mlt_filter filter ) +{ + vs_data* data = (vs_data*)filter->child; + if ( data ) + { + if ( data->analyze_data ) destory_analyze_data( data->analyze_data ); + if ( data->apply_data ) destory_apply_data( data->apply_data ); + free( data ); + } + filter->close = NULL; + filter->child = NULL; + filter->parent.close = NULL; + mlt_service_close( &filter->parent ); +} + +extern "C" +{ + +mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new(); + vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) ); + + if ( filter && data ) + { + data->analyze_data = NULL; + data->apply_data = NULL; + + filter->close = filter_close; + filter->child = data; + filter->process = process_filter; + + mlt_properties properties = MLT_FILTER_PROPERTIES(filter); + + //properties for analyze + mlt_properties_set( properties, "filename", "vidstab.trf" ); + mlt_properties_set( properties, "shakiness", "4" ); + mlt_properties_set( properties, "accuracy", "4" ); + mlt_properties_set( properties, "stepsize", "6" ); + mlt_properties_set( properties, "algo", "1" ); + mlt_properties_set( properties, "mincontrast", "0.3" ); + mlt_properties_set( properties, "show", "0" ); + mlt_properties_set( properties, "tripod", "0" ); + + // properties for apply + mlt_properties_set( properties, "smoothing", "15" ); + mlt_properties_set( properties, "maxshift", "-1" ); + mlt_properties_set( properties, "maxangle", "-1" ); + mlt_properties_set( properties, "crop", "0" ); + mlt_properties_set( properties, "invert", "0" ); + mlt_properties_set( properties, "relative", "1" ); + mlt_properties_set( properties, "zoom", "0" ); + mlt_properties_set( properties, "optzoom", "1" ); + mlt_properties_set( properties, "zoomspeed", "0.25" ); + mlt_properties_set( properties, "reload", "0" ); + + mlt_properties_set( properties, "vid.stab.version", LIBVIDSTAB_VERSION ); + + init_vslog(); + } + else + { + if( filter ) + { + mlt_filter_close( filter ); + } + + if( data ) + { + free( data ); + } + + filter = NULL; + } + return filter; +} + +} diff -Nru mlt-0.9.0/src/modules/vid.stab/filter_vidstab.yml mlt-0.9.2/src/modules/vid.stab/filter_vidstab.yml --- mlt-0.9.0/src/modules/vid.stab/filter_vidstab.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/filter_vidstab.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,270 @@ +schema_version: 0.1 +type: filter +identifier: vidstab +title: Vid.Stab Detect and Transform +copyright: Jakub Ksiezniak +creator: Marco Gittler +version: 1 +license: GPL +language: en +url: http://public.hronopik.de/vid.stab/ +tags: + - Video +description: Stabilize Video (for wiggly/rolling video) +notes: > + This filter requires two passes. The first pass performs analysis and stores + the result in a file. Upon successful completion of the analysis, the + "results" property is updated with the name of the file storing the results. + The second pass applies the results to the image. + + To use with melt, use 'melt ... -consumer xml:output.mlt all=1' for the + first pass. For the second pass, use output.mlt as the input. + +parameters: + - identifier: results + title: Analysis Results + type: string + description: > + Set after analysis. Used during application. + A set of image motion information that describes the analyzed clip. + When results are not supplied, the filter computes the results and stores + them in a file. This property is updated with the name of that file when + the last frame has been processed. + mutable: no + + - identifier: filename + title: Target File Name + type: string + description: > + Used during analysis. + The name of the file to store the analysis results in. + required: no + readonly: no + default: vidstab.trf + widget: fileopen + + - identifier: shakiness + title: Shakiness + type: integer + description: > + Used during analysis. + How shaky the video is. + readonly: no + required: no + minimum: 1 + maximum: 10 + default: 4 + mutable: no + widget: spinner + + - identifier: accuracy + title: Accuracy + type: integer + description: > + Used during analysis. + The accuracy of shakiness detection. + readonly: no + required: no + minimum: 1 + maximum: 15 + default: 4 + mutable: no + widget: spinner + + - identifier: stepsize + title: Stepsize + type: integer + description: > + Used during analysis. + The step size of the search process. + readonly: no + required: no + minimum: 0 + maximum: 100 + default: 6 + mutable: no + widget: spinner + + - identifier: mincontrast + title: Minimum Contrast + type: float + description: > + Used during analysis. + Below this contrast, a field is discarded. + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0.3 + mutable: no + widget: spinner + + - identifier: show + title: Show + type: integer + description: > + Used during analysis. + 0 = draw nothing + 1 or 2 = show fields and transforms + readonly: no + required: no + minimum: 0 + maximum: 2 + default: 0 + mutable: no + widget: spinner + + - identifier: tripod + title: Tripod + type: integer + description: > + Used during analysis and application. + if 0, tripod mode is disabled. + if > 0, specifies the frame to be used as a reference frame for tripod mode + During application, relative and smoothing properties are both ignored if tripod mode is in use. + readonly: no + required: no + minimum: 0 + maximum: 100000 + default: 0 + mutable: no + widget: spinner + + - identifier: smoothing + title: Smoothing + type: integer + description: > + Used during application. + Number of frames for lowpass filtering (2N + 1 frames) + readonly: no + required: no + minimum: 0 + maximum: 100 + default: 15 + mutable: yes + widget: spinner + + - identifier: maxshift + title: Maxshift + type: integer + description: > + Used during application. + Maximum number of pixels to transform the image. -1 = no limit + unit: pixels + readonly: no + required: no + minimum: -1 + maximum: 1000 + default: -1 + mutable: yes + widget: spinner + + - identifier: maxangle + title: Maxangle + type: float + description: > + Used during application. + Maximum angle to rotate, -1 = no limit + unit: radians + readonly: no + required: no + minimum: -1 + maximum: 3.142 + default: -1 + mutable: yes + widget: spinner + + - identifier: crop + title: Crop + type: integer + description: > + Used during application. + 0 = keep border, 1 = black background + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + widget: spinner + + - identifier: invert + title: Invert + type: integer + description: > + Used during application. + Invert transforms + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + widget: spinner + + - identifier: relative + title: Relative + type: integer + description: > + Used during application. + Consider transforms as absolute (0) or relative (1) + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 1 + mutable: yes + widget: spinner + + - identifier: zoom + title: Zoom + type: integer + description: > + Used during application. + Additional zoom amount + unit: percent + readonly: no + required: no + minimum: -500 + maximum: 500 + default: 0 + mutable: yes + widget: spinner + + - identifier: optzoom + title: Optimal Zoom + type: integer + description: > + Used during application. + Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom + readonly: no + required: no + minimum: 0 + maximum: 2 + default: 1 + mutable: yes + widget: spinner + + - identifier: zoomspeed + title: Optimal Zoom Speed + type: float + description: > + Used during application. + Zoom per frame (used when optzoom = 2) + unit: percent + readonly: no + required: no + minimum: 0 + maximum: 1 + default: 0.25 + mutable: yes + widget: spinner + + - identifier: reload + title: Reload Results + description: > + The application should set this to 1 when it updates the results property to indicate that the results should be reloaded. + type: integer + minimum: 0 + maximum: 1 + mutable: yes diff -Nru mlt-0.9.0/src/modules/vid.stab/Makefile mlt-0.9.2/src/modules/vid.stab/Makefile --- mlt-0.9.0/src/modules/vid.stab/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/vid.stab/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,45 @@ +CFLAGS += -I../.. + +LDFLAGS += -L../../framework -lmlt -lm + +include ../../../config.mak + +TARGET = ../libmltvidstab$(LIBSUF) + +OBJS = factory.o \ + common.o + +CPPOBJS = filter_deshake.o +CPPOBJS += filter_vidstab.o + +CXXFLAGS += -Wno-deprecated $(CFLAGS) +CXXFLAGS += $(shell pkg-config --cflags vidstab) + +LDFLAGS += -L../../mlt++ -lmlt++ +LDFLAGS += $(shell pkg-config --libs vidstab) + + +SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) + +all: $(TARGET) + +$(TARGET): $(OBJS) $(CPPOBJS) + $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CXX) -MM $(CXXFLAGS) $^ 1>.depend + +distclean: clean + rm -f .depend config.h config.mak + +clean: + rm -f $(OBJS) $(TARGET) $(CPPOBJS) + +install: all + install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" + install -d "$(DESTDIR)$(mltdatadir)/vid.stab" + install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/vid.stab" + +ifneq ($(wildcard .depend),) +include .depend +endif diff -Nru mlt-0.9.0/src/modules/vmfx/producer_pgm.c mlt-0.9.2/src/modules/vmfx/producer_pgm.c --- mlt-0.9.0/src/modules/vmfx/producer_pgm.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/vmfx/producer_pgm.c 2014-06-29 20:23:17.000000000 +0000 @@ -35,6 +35,12 @@ int height = 0; int maxval = 0; + // Convert file name string encoding. + mlt_properties tmp_properties = mlt_properties_new(); + mlt_properties_set( tmp_properties, "utf8", resource ); + mlt_properties_from_utf8( tmp_properties, "utf8", "local8" ); + resource = mlt_properties_get( tmp_properties, "local8" ); + if ( read_pgm( resource, &image, &width, &height, &maxval ) == 0 ) { this = calloc( 1, sizeof( struct mlt_producer_s ) ); @@ -55,6 +61,7 @@ this = NULL; } } + mlt_properties_close( tmp_properties ); return this; } @@ -66,7 +73,7 @@ { uint8_t *input = NULL; int error = 0; - FILE *f = fopen( name, "r" ); + FILE *f = fopen( name, "rb" ); char data[ 512 ]; // Initialise diff -Nru mlt-0.9.0/src/modules/vorbis/Makefile mlt-0.9.2/src/modules/vorbis/Makefile --- mlt-0.9.0/src/modules/vorbis/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/vorbis/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -9,11 +9,11 @@ OBJS = factory.o \ producer_vorbis.o -CFLAGS += `pkg-config --cflags vorbis` -CFLAGS += `pkg-config --cflags vorbisfile` +CFLAGS += $(shell pkg-config --cflags vorbis) +CFLAGS += $(shell pkg-config --cflags vorbisfile) -LDFLAGS += `pkg-config --libs vorbis` -LDFLAGS += `pkg-config --libs vorbisfile` +LDFLAGS += $(shell pkg-config --libs vorbis) +LDFLAGS += $(shell pkg-config --libs vorbisfile) SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.0/src/modules/vorbis/producer_vorbis.c mlt-0.9.2/src/modules/vorbis/producer_vorbis.c --- mlt-0.9.0/src/modules/vorbis/producer_vorbis.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/vorbis/producer_vorbis.c 2014-06-29 20:23:17.000000000 +0000 @@ -124,7 +124,7 @@ static int producer_open( mlt_producer this, mlt_profile profile, char *file ) { // FILE pointer for file - FILE *input = fopen( file, "r" ); + FILE *input = fopen( file, "rb" ); // Error code to return int error = input == NULL; diff -Nru mlt-0.9.0/src/modules/vorbis/producer_vorbis.yml mlt-0.9.2/src/modules/vorbis/producer_vorbis.yml --- mlt-0.9.0/src/modules/vorbis/producer_vorbis.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/vorbis/producer_vorbis.yml 2014-06-29 20:23:17.000000000 +0000 @@ -1,7 +1,7 @@ schema_version: 0.1 type: producer identifier: vorbis -title: Ogg Vorbis +title: Ogg Vorbis (*deprecated*) version: 1 copyright: Visual Media FX ? creator: Charles Yates diff -Nru mlt-0.9.0/src/modules/xml/consumer_xml.c mlt-0.9.2/src/modules/xml/consumer_xml.c --- mlt-0.9.0/src/modules/xml/consumer_xml.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/consumer_xml.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * consumer_xml.c -- a libxml2 serialiser of mlt service networks - * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Dan Dennedy * * This library is free software; you can redistribute it and/or @@ -392,6 +392,7 @@ xmlNewProp( track, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); xmlNewProp( track, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store ); + serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." ); serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track ); @@ -446,6 +447,7 @@ // Store application specific properties serialise_store_properties( context, properties, child, context->store ); + serialise_store_properties( context, properties, child, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, properties, child, "meta." ); @@ -485,6 +487,7 @@ if ( mlt_producer_is_cut( info.cut ) ) { serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store ); + serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." ); serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry ); @@ -527,11 +530,14 @@ xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) ); if ( mlt_properties_get( properties, "global_feed" ) ) xmlNewProp( child, _x("global_feed"), _x(mlt_properties_get( properties, "global_feed" )) ); - xmlNewProp( child, _x("in"), _x(mlt_properties_get_time( properties, "in", context->time_format )) ); - xmlNewProp( child, _x("out"), _x(mlt_properties_get_time( properties, "out", context->time_format )) ); + if ( mlt_properties_get_position( properties, "in" ) >= 0 ) + xmlNewProp( child, _x("in"), _x(mlt_properties_get_time( properties, "in", context->time_format )) ); + if ( mlt_properties_get_position( properties, "out" ) >= 0 ) + xmlNewProp( child, _x("out"), _x(mlt_properties_get_time( properties, "out", context->time_format )) ); // Store application specific properties serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store ); + serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." ); @@ -665,6 +671,8 @@ else { serialise_producer( context, service, node ); + if ( mlt_properties_get( properties, "xml" ) != NULL ) + break; } } @@ -687,6 +695,24 @@ } } +static void serialise_other( mlt_properties properties, struct serialise_context_s *context, xmlNodePtr root ) +{ + int i; + for ( i = 0; i < mlt_properties_count( properties ); i++ ) + { + const char* name = mlt_properties_get_name( properties, i ); + if ( strlen(name) > 10 && !strncmp( name, "xml_retain", 10 ) ) + { + mlt_service service = mlt_properties_get_data_at( properties, i, NULL ); + if ( service ) + { + mlt_properties_set_int( MLT_SERVICE_PROPERTIES( service ), "xml_retain", 1 ); + serialise_service( context, service, root ); + } + } + } +} + xmlDocPtr xml_make_doc( mlt_consumer consumer, mlt_service service ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); @@ -768,11 +794,13 @@ // In pass one, we serialise the end producers and playlists, // adding them to a map keyed by address. + serialise_other( MLT_SERVICE_PROPERTIES( service ), context, root ); serialise_service( context, service, root ); // In pass two, we serialise the tractor and reference the // producers and playlists context->pass++; + serialise_other( MLT_SERVICE_PROPERTIES( service ), context, root ); serialise_service( context, service, root ); // Cleanup resource @@ -814,6 +842,12 @@ free( cwd ); } +#if !defined(__GLIBC__) && !defined(__DARWIN__) + // Get the current locale + char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); + setlocale( LC_NUMERIC, "C" ); +#endif + // Make the document doc = xml_make_doc( this, service ); @@ -836,9 +870,19 @@ } else { + // Convert file name string encoding. + mlt_properties_from_utf8( properties, "resource", "_resource" ); + resource = mlt_properties_get( properties, "_resource" ); + xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 ); } +#if !defined(__GLIBC__) && !defined(__DARWIN__) + // Restore the current locale + setlocale( LC_NUMERIC, orig_localename ); + free( orig_localename ); +#endif + // Close the document xmlFreeDoc( doc ); } @@ -918,6 +962,9 @@ // Frame and size mlt_frame frame = NULL; + int video_off = mlt_properties_get_int( properties, "video_off" ); + int audio_off = mlt_properties_get_int( properties, "audio_off" ); + // Loop while running while( !terminated && mlt_properties_get_int( properties, "running" ) ) { @@ -939,8 +986,10 @@ mlt_audio_format aformat = mlt_audio_s16; uint8_t *buffer; - mlt_frame_get_image( frame, &buffer, &iformat, &width, &height, 0 ); - mlt_frame_get_audio( frame, (void**) &buffer, &aformat, &frequency, &channels, &samples ); + if ( !video_off ) + mlt_frame_get_image( frame, &buffer, &iformat, &width, &height, 0 ); + if ( !audio_off ) + mlt_frame_get_audio( frame, (void**) &buffer, &aformat, &frequency, &channels, &samples ); // Close the frame mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); diff -Nru mlt-0.9.0/src/modules/xml/consumer_xml.yml mlt-0.9.2/src/modules/xml/consumer_xml.yml --- mlt-0.9.0/src/modules/xml/consumer_xml.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/consumer_xml.yml 2014-06-29 20:23:17.000000000 +0000 @@ -13,13 +13,25 @@ description: > Serialise the service network to XML. See docs/mlt-xml.txt for more information. + +notes: > + If you set a data property beginning with (and longer than) "xml_retain" on + the service connected to this consumer where the data is a mlt_service + pointer, then the pointed at service will also be serialized before the + connected service. This can be useful, for example, to save a playlist as + a media bin along with a multitrack. You can serialize more than one of these + additional services by setting more than property, each with a unique key + beginning with "xml_retain". + bugs: - Untested arbitrary nesting of multitracks and playlists. - > Property "id" is generated as service type followed by number if no property named "id" exists, but it fails to guarantee uniqueness. + parameters: - - identifier: argument + - identifier: resource + argument: yes title: File type: string description: > @@ -83,3 +95,10 @@ - clock # or CLOCK default: frames widget: dropdown + + - identifier: store + title: Include property prefix + type: string + description: > + To save additional properties that MLT does not know about, supply an + application-specific property name prefix that you are using. diff -Nru mlt-0.9.0/src/modules/xml/factory.c mlt-0.9.2/src/modules/xml/factory.c --- mlt-0.9.0/src/modules/xml/factory.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/factory.c 2014-06-29 20:23:17.000000000 +0000 @@ -37,8 +37,10 @@ MLT_REGISTER( consumer_type, "xml", consumer_xml_init ); MLT_REGISTER( producer_type, "xml", producer_xml_init ); MLT_REGISTER( producer_type, "xml-string", producer_xml_init ); + MLT_REGISTER( producer_type, "xml-nogl", producer_xml_init ); MLT_REGISTER_METADATA( consumer_type, "xml", metadata, "consumer_xml.yml" ); MLT_REGISTER_METADATA( producer_type, "xml", metadata, "producer_xml.yml" ); MLT_REGISTER_METADATA( producer_type, "xml-string", metadata, "producer_xml-string.yml" ); + MLT_REGISTER_METADATA( producer_type, "xml-nogl", metadata, "producer_xml-nogl.yml" ); } diff -Nru mlt-0.9.0/src/modules/xml/Makefile mlt-0.9.2/src/modules/xml/Makefile --- mlt-0.9.0/src/modules/xml/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -10,9 +10,9 @@ consumer_xml.o \ producer_xml.o -CFLAGS += `pkg-config libxml-2.0 --cflags` +CFLAGS += $(shell pkg-config libxml-2.0 --cflags) -LDFLAGS += `pkg-config libxml-2.0 --libs` +LDFLAGS += $(shell pkg-config libxml-2.0 --libs) SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.0/src/modules/xml/producer_xml.c mlt-0.9.2/src/modules/xml/producer_xml.c --- mlt-0.9.0/src/modules/xml/producer_xml.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/producer_xml.c 2014-06-29 20:23:17.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_xml.c -- a libxml2 parser of mlt service networks - * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * Copyright (C) 2003-2014 Ushodaya Enterprises Limited * Author: Dan Dennedy * * This library is free software; you can redistribute it and/or @@ -590,7 +590,8 @@ // Track this producer track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric ); - context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); + if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) ) + context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); // Propagate the properties qualify_property( context, properties, "resource" ); @@ -931,6 +932,7 @@ qualify_property( context, properties, "luma.resource" ); qualify_property( context, properties, "composite.luma" ); qualify_property( context, properties, "producer.resource" ); + qualify_property( context, properties, "filename" ); mlt_properties_inherit( filter_props, properties ); // Attach all filters from service onto filter @@ -1551,22 +1553,63 @@ } // Quick workaround to avoid unecessary libxml2 warnings -static int file_exists( char *file ) +static int file_exists( char *name ) { - char *name = strdup( file ); int exists = 0; - if ( name != NULL && strchr( name, '?' ) ) - *( strchr( name, '?' ) ) = '\0'; if ( name != NULL ) { FILE *f = fopen( name, "r" ); exists = f != NULL; if ( exists ) fclose( f ); } - free( name ); return exists; } +// This function will add remaing services in the context service stack marked +// with a "xml_retain" property to a property named "xml_retain" on the returned +// service. The property is a mlt_properties data property. + +static void retain_services( struct deserialise_context_s *context, mlt_service service ) +{ + mlt_properties retain_list = mlt_properties_new(); + enum service_type type; + mlt_service retain_service = context_pop_service( context, &type ); + + while ( retain_service ) + { + mlt_properties retain_properties = MLT_SERVICE_PROPERTIES( retain_service ); + + if ( mlt_properties_get_int( retain_properties, "xml_retain" ) ) + { + // Remove the retained service from the destructors list. + int i; + for ( i = mlt_properties_count( context->destructors ) - 1; i >= 1; i -- ) + { + const char *name = mlt_properties_get_name( context->destructors, i ); + if ( mlt_properties_get_data_at( context->destructors, i, NULL ) == retain_service ) + { + mlt_properties_set_data( context->destructors, name, retain_service, 0, NULL, NULL ); + break; + } + } + const char *name = mlt_properties_get( retain_properties, "id" ); + if ( name ) + mlt_properties_set_data( retain_list, name, retain_service, 0, + (mlt_destructor) mlt_service_close, NULL ); + } + retain_service = context_pop_service( context, &type ); + } + if ( mlt_properties_count( retain_list ) > 0 ) + { + mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0, + (mlt_destructor) mlt_properties_close, NULL ); + } + else + { + mlt_properties_close( retain_list ); + } +} + mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data ) { xmlSAXHandler *sax, *sax_orig; @@ -1582,7 +1625,7 @@ if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 ) data += 7; - if ( data == NULL || !strcmp( data, "" ) || ( is_filename && !file_exists( data ) ) ) + if ( data == NULL || !strcmp( data, "" ) ) return NULL; context = calloc( 1, sizeof( struct deserialise_context_s ) ); @@ -1599,7 +1642,8 @@ mlt_properties_set( context->producer_map, "root", "" ); if ( is_filename ) { - filename = strdup( data ); + mlt_properties_set( context->params, "_mlt_xml_resource", data ); + filename = mlt_properties_get( context->params, "_mlt_xml_resource" ); parse_url( context->params, url_decode( filename, data ) ); // We need the directory prefix which was used for the xml @@ -1621,6 +1665,19 @@ free( cwd ); } } + + // Convert file name string encoding. + mlt_properties_from_utf8( context->params, "_mlt_xml_resource", "__mlt_xml_resource" ); + filename = mlt_properties_get( context->params, "__mlt_xml_resource" ); + + if ( !file_exists( filename ) ) + { + mlt_properties_close( context->producer_map ); + mlt_properties_close( context->destructors ); + mlt_properties_close( context->params ); + free( context ); + return NULL; + } } // We need to track the number of registered filters @@ -1652,7 +1709,6 @@ mlt_properties_close( context->params ); free( context ); free( sax ); - free( filename ); return NULL; } @@ -1681,7 +1737,6 @@ xmlFreeDoc( context->entity_doc ); free( context ); free( sax ); - free( filename ); return NULL; } @@ -1701,7 +1756,6 @@ xmlFreeDoc( context->entity_doc ); free( context ); free( sax ); - free( filename ); return NULL; } @@ -1709,7 +1763,9 @@ // may exist when trying to load glsl. or movit. services. // The "if requested" part can come from query string qglsl=1 or when // a service beginning with glsl. or movit. appears in the XML. - if ( mlt_properties_get_int( context->params, "qglsl" ) ) + if ( mlt_properties_get_int( context->params, "qglsl" ) && strcmp( id, "xml-nogl" ) + // Only if glslManager does not yet exist. + && !mlt_properties_get_data( mlt_global_properties(), "glslManager", NULL ) ) context->qglsl = mlt_factory_consumer( profile, "qglsl", NULL ); // Setup SAX callbacks for second pass @@ -1815,6 +1871,8 @@ (mlt_destructor) mlt_consumer_close, NULL ); mlt_properties_set_int( properties, "seekable", context->seekable ); + + retain_services( context, service ); } else { @@ -1832,7 +1890,6 @@ if ( context->lc_numeric ) free( context->lc_numeric ); free( context ); - free( filename ); return MLT_PRODUCER( service ); } diff -Nru mlt-0.9.0/src/modules/xml/producer_xml-nogl.yml mlt-0.9.2/src/modules/xml/producer_xml-nogl.yml --- mlt-0.9.0/src/modules/xml/producer_xml-nogl.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/producer_xml-nogl.yml 2014-06-29 20:23:17.000000000 +0000 @@ -0,0 +1,17 @@ +schema_version: 0.1 +type: producer +identifier: xml-nogl +title: XML without OpenGL +version: 1 +copyright: Ushodaya Enterprises Limited +creator: Dan Dennedy +license: LGPLv2.1 +language: en +tags: + - Audio + - Video +description: > + This is the same as the regular "xml" producer except it prevents + automatically creating the qglsl consumer when it detects the usage of + OpenGL-based services within the XML. + See ProducerXml for more information. diff -Nru mlt-0.9.0/src/modules/xml/producer_xml-string.yml mlt-0.9.2/src/modules/xml/producer_xml-string.yml --- mlt-0.9.0/src/modules/xml/producer_xml-string.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/producer_xml-string.yml 2014-06-29 20:23:17.000000000 +0000 @@ -10,3 +10,8 @@ tags: - Audio - Video +description: > + This is the same as the regular "xml" producer except it takes a pointer to + a string as the constructor argument. That means it can only be used + by applications and not directly exposed to users of those applications. + See ProducerXml for more information. diff -Nru mlt-0.9.0/src/modules/xml/producer_xml.yml mlt-0.9.2/src/modules/xml/producer_xml.yml --- mlt-0.9.0/src/modules/xml/producer_xml.yml 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/modules/xml/producer_xml.yml 2014-06-29 20:23:17.000000000 +0000 @@ -12,6 +12,16 @@ - Video description: | Construct a service network from an XML description. See docs/mlt-xml.txt. + +notes: > + If there is a service with a property "xml_retain=1" that is not the + producer, and if it also has an "id" property; then the extra service + is put into a properties list keyed on the id property. Then, that + properties list is placed as a property on the returned service with + the name "xml_retain". This lets an application retrieve additional + deserialized services that are not the lastmost producer or anywhere in + its graph. + parameters: - identifier: argument title: File diff -Nru mlt-0.9.0/src/swig/Makefile mlt-0.9.2/src/swig/Makefile --- mlt-0.9.0/src/swig/Makefile 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/swig/Makefile 2014-06-29 20:23:17.000000000 +0000 @@ -6,7 +6,7 @@ for subdir in $$list; do \ if [ -x $$subdir/build -a ! -f .$$subdir -o $@ = clean ] ; \ then echo -n Building $$subdir... ; \ - cd $$subdir && output=`CXXFLAGS="$(CXXFLAGS)" ./build $@ 2>&1` ; \ + cd $$subdir && output=$$(CXXFLAGS="$(CXXFLAGS)" ./build $@ 2>&1) ; \ if [ $$? -eq 0 ] ; \ then echo OK && touch ../.$$subdir ; \ else echo $$output && exit 1 ; \ diff -Nru mlt-0.9.0/src/swig/ruby/build mlt-0.9.2/src/swig/ruby/build --- mlt-0.9.0/src/swig/ruby/build 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/swig/ruby/build 2014-06-29 20:23:17.000000000 +0000 @@ -6,7 +6,7 @@ end system( "ln -sf ../mlt.i" ) system( "swig -c++ -ruby -I../../mlt++ -I../.. mlt.i" ) -$CFLAGS += " -I../.. " + ENV['CXXFLAGS'] +$CFLAGS += " -I../.. " + (ENV.has_key?('CXXFLAGS')? ENV['CXXFLAGS'] : '') $LDFLAGS += " -L../../mlt++ -lmlt++" create_makefile('mlt') system( "make" ) diff -Nru mlt-0.9.0/src/swig/ruby/metadata.rb mlt-0.9.2/src/swig/ruby/metadata.rb --- mlt-0.9.0/src/swig/ruby/metadata.rb 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/swig/ruby/metadata.rb 2014-06-29 20:23:17.000000000 +0000 @@ -27,7 +27,7 @@ % end %%BR% % end -description: <%= yml['description'] %> %BR% +description: <%= ERB::Util.h(yml['description']) %> %BR% version: <%= yml['version'] %> %BR% creator: <%= yml['creator'] %> %BR% % yml['contributor'] and yml['contributor'].each do |x| @@ -39,7 +39,7 @@ % if yml['notes'] ---++ Notes % yml['notes'].each do |x| -<%= x %> +<%= ERB::Util.h(x) %> % end % end @@ -55,7 +55,7 @@ % yml['parameters'].each do |param| ---+++ <%= param['identifier'] %> <%= "title: #{param['title']} %BR%\n" if param['title'] %> -<%= "description: #{param['description']} %BR%\n" if param['description'] %> +<%= "description: #{ERB::Util.h(param['description'])} %BR%\n" if param['description'] %> type: <%= param['type'] %> %BR% readonly: <%= param['readonly'] or 'no' %> %BR% required: <%= param['required'] or 'no' %> %BR% @@ -91,15 +91,19 @@ if meta.is_valid filename = type_title + name.capitalize.gsub('.', '-') puts "Processing #{filename}" - yml = YAML.load(meta.serialise_yaml) - if yml - File.open(filename + '.txt', 'w') do |f| - f.puts $processor.result(binding) + begin + yml = YAML.load(meta.serialise_yaml) + if yml + File.open(filename + '.txt', 'w') do |f| + f.puts $processor.result(binding) + end + else + puts "Failed to write file for #{filename}" end - else - puts "Failed to write file for #{filename}" + index.puts " * [[#{filename}][#{name}]]: #{meta.get('title')}\n" + rescue ArgumentError + puts "Failed to parse YAML for #{filename}" end - index.puts " * [[#{filename}][#{name}]]: #{meta.get('title')}\n" end end index.puts '' diff -Nru mlt-0.9.0/src/tests/test_properties/test_properties.cpp mlt-0.9.2/src/tests/test_properties/test_properties.cpp --- mlt-0.9.0/src/tests/test_properties/test_properties.cpp 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/tests/test_properties/test_properties.cpp 2014-06-29 20:23:17.000000000 +0000 @@ -325,6 +325,39 @@ QCOMPARE(p.get_double("key"), double(1) / double(8)); } + void AnimationInsert() + { + double fps = 25.0; + mlt_animation a = mlt_animation_new(); + struct mlt_animation_item_s item; + + item.is_key = 1; + item.keyframe_type = mlt_keyframe_discrete; + item.property = mlt_property_init(); + + item.frame = 0; + mlt_property_set_string(item.property, "0"); + mlt_animation_insert(a, &item); + + item.frame = 1; + mlt_property_set_string(item.property, "1"); + mlt_animation_insert(a, &item); + + item.frame = 2; + mlt_property_set_string(item.property, "2"); + mlt_animation_insert(a, &item); + + QCOMPARE(mlt_animation_get_length(a), 2); + + char *a_serialized = mlt_animation_serialize(a); + mlt_animation_parse(a, a_serialized, 0, fps, locale); + QCOMPARE(a_serialized, "0|=0;1|=1;2|=2"); + if (a_serialized) free(a_serialized); + + mlt_property_close(item.property); + mlt_animation_close(a); + } + void DoubleAnimation() { double fps = 25.0; @@ -912,3 +945,4 @@ QTEST_APPLESS_MAIN(TestProperties) #include "test_properties.moc" + diff -Nru mlt-0.9.0/src/win32/win32.c mlt-0.9.2/src/win32/win32.c --- mlt-0.9.0/src/win32/win32.c 2013-06-03 03:34:35.000000000 +0000 +++ mlt-0.9.2/src/win32/win32.c 2014-06-29 20:23:17.000000000 +0000 @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include "../framework/mlt_properties.h" + int usleep(unsigned int useconds) { HANDLE timer; @@ -41,3 +46,55 @@ return result; } +static int iconv_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) +{ + const char *text = mlt_properties_get( properties, prop_name ); + int result = -1; + + iconv_t cd = iconv_open( encoding, "UTF-8" ); + if ( text && ( cd != ( iconv_t )-1 ) ) { + size_t inbuf_n = strlen( text ); + size_t outbuf_n = inbuf_n * 6; + char *outbuf = mlt_pool_alloc( outbuf_n ); + char *outbuf_p = outbuf; + + memset( outbuf, 0, outbuf_n ); + + if ( text != NULL && strcmp( text, "" ) && iconv( cd, &text, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 ) + mlt_properties_set( properties, prop_name_out, outbuf ); + else + mlt_properties_set( properties, prop_name_out, "" ); + + mlt_pool_release( outbuf ); + result = 0; + } + iconv_close( cd ); + return result; +} + +int mlt_properties_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) +{ + int result = -1; + // Get the locale name. + const char *locale = setlocale( LC_CTYPE, NULL ); + if ( locale && strchr( locale, '.' ) ) { + // Check for a code page in locale format = language_country.codepage. + locale = strchr( locale, '.' ) + 1; + if ( isdigit( locale[0] ) ) { + // numeric code page + char codepage[10]; + snprintf( codepage, sizeof(codepage), "CP%s", locale ); + result = iconv_from_utf8( properties, prop_name, prop_name_out, codepage ); + } else { + // non-numeric code page possible on Windows? + // TODO: some code pages may require conversion from numeric to iconv + // compatible name. For example, maybe Shift-JIS or KOI8-R. + result = iconv_from_utf8( properties, prop_name, prop_name_out, locale ); + } + } + if ( result < 0 ) { + result = mlt_properties_set( properties, prop_name_out, + mlt_properties_get( properties, prop_name ) ); + } + return result; +}