diff -Nru mlt-0.9.8/ChangeLog mlt-6.0.0/ChangeLog --- mlt-0.9.8/ChangeLog 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/ChangeLog 2016-02-17 23:43:24.000000000 +0000 @@ -1,3 +1,268 @@ +2016-02-17 Dan Dennedy + + * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version + to 6.0.0 + +2016-02-11 Dan Dennedy + + * src/framework/mlt_frame.h, src/framework/mlt_service.c, + src/framework/mlt_service.h, src/framework/mlt_tractor.c, + src/modules/xine/filter_deinterlace.c: Fix YADIF in some multitrack + scenarios. + +2016-01-26 Maksym Veremeyenko + + * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: + Implement text cropping and fitting to specified width + +2016-01-18 Dan Dennedy + + * src/framework/mlt_tractor.c, src/framework/mlt_types.h, + src/modules/core/filter_audioconvert.c, src/modules/core/filter_brightness.c, + src/modules/frei0r/factory.c, src/modules/motion_est/arrow_code.c, + src/modules/motion_est/filter_autotrack_rectangle.c, + src/modules/motion_est/filter_motion_est.c, + src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_tcolor.c, + src/modules/oldfilm/filter_vignette.c, src/modules/plus/transition_affine.c, + src/modules/plusgpl/filter_rotoscoping.c, + src/modules/videostab/transform_image.c: Define MIN, MAX, CLAMP in + mlt_types.h. Consolidate all of the definitions of these macros and make + them more convenient. + +2016-01-13 Dan Dennedy + + * src/framework/mlt_multitrack.c, src/framework/mlt_transition.c: Fix + possible array index crashes in multitrack and transition. Reported by JBM. + +2016-01-11 Dan Dennedy + + * src/framework/mlt_consumer.c, src/framework/mlt_factory.c, + src/framework/mlt_pool.c, src/framework/mlt_properties.c, + src/framework/mlt_repository.c, src/framework/mlt_types.h, src/melt/io.c, + src/melt/melt.c, src/mlt++/config.h, src/modules/decklink/common.cpp, + src/modules/decklink/common.h, src/modules/decklink/consumer_decklink.cpp, + src/modules/decklink/producer_decklink.cpp, src/modules/frei0r/factory.c, + src/modules/gtk2/consumer_gtk2.c, src/modules/jackrack/consumer_jack.c, + src/modules/jackrack/jack_rack.c, src/modules/jackrack/plugin_mgr.c, + src/modules/opengl/factory.c, src/modules/opengl/filter_glsl_manager.cpp, + src/modules/opengl/filter_movit_convert.cpp, + src/modules/plusgpl/consumer_cbrts.c, src/modules/sdl/consumer_sdl.c, + src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, + src/modules/sdl/consumer_sdl_still.c, src/modules/videostab/stab/estimate.c, + src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Switch to + _WIN32 define for Windows. Based on de facto info collected at + http://predef.sourceforge.net/ + + * configure, src/framework/mlt_factory.c, src/framework/mlt_properties.c, + src/framework/mlt_property.c, src/framework/mlt_property.h, src/melt/melt.c, + src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/configure, + src/modules/decklink/common.cpp, src/modules/decklink/common.h, + src/modules/frei0r/factory.c, src/modules/jackrack/plugin.c, + src/modules/jackrack/plugin_mgr.c, src/modules/opengl/factory.c, + src/modules/opengl/filter_glsl_manager.cpp, src/modules/plusgpl/utils.c, + src/modules/plusgpl/utils.h, src/modules/sdl/consumer_sdl.c, + src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_osx.h, + src/modules/sdl/consumer_sdl_still.c, src/modules/videostab/stab/estimate.c, + src/tests/test_filter/test_filter.cpp, + src/tests/test_properties/test_properties.cpp: Switch to __APPLE__ define for + OS X. Based on de facto info collected at http://predef.sourceforge.net/ + +2016-01-06 Dan Dennedy + + * src/framework/mlt_filter.c, src/modules/xml/producer_xml.c: Fix saving and + loading filters attached to tractor. + +2016-01-05 Dan Dennedy + + * src/framework/mlt.vers, src/framework/mlt_properties.c, + src/framework/mlt_properties.h, src/modules/xml/consumer_xml.c, + src/win32/win32.c: Fix possible non-UTF-8 in XML on Windows (Shotcut-173). + +2015-12-22 Dan Dennedy + + * src/modules/avformat/producer_avformat.c, src/modules/xml/consumer_xml.c: + Fix some UTF-8 strings mangled by xml consumer. (Shotuct-163) This moves + filter_restricted() into the avformat producer since it was its meta tag + values that were not known to be UTF-8 being passed into MLT. + +2015-11-30 Brian Matherly + + * src/modules/core/Makefile, src/modules/core/factory.c, + src/modules/core/producer_timewarp.c, src/modules/core/producer_timewarp.yml: + Add timewarp producer. + +2015-11-29 Brian Matherly + + * src/framework/mlt.vers, src/framework/mlt_factory.c, + src/framework/mlt_factory.h: Add mlt_factory_repository() to mlt_factory. + +2015-11-20 Dan Dennedy + + * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, + src/modules/decklink/consumer_decklink.cpp: Add drop_count property to + mlt_consumer and verbose log it. Apps can use this to detect and display the + integrity of capture or realtime playout. + +2015-11-03 Dan Dennedy + + * presets/consumer/avformat/stills/BMP, presets/consumer/avformat/stills/DPX, + presets/consumer/avformat/stills/JPEG, presets/consumer/avformat/stills/PNG, + presets/consumer/avformat/stills/PPM, presets/consumer/avformat/stills/TGA, + presets/consumer/avformat/stills/TIFF: Set GOP size to 1 and B frames to 0 + for all stills presets. + +2015-10-19 Brian Matherly + + * src/modules/qt/filter_audiospectrum.cpp, + src/modules/qt/filter_audiospectrum.yml: Add "mirror" and "reverse" + parameters. + +2015-10-14 Brian Matherly + + * src/modules/plus/filter_dynamictext.c, + src/modules/plus/filter_dynamictext.yml: Add style property to dynamictext + filter. + +2015-10-11 Brian Matherly + + * src/modules/qt/filter_audiospectrum.cpp, + src/modules/qt/filter_audiospectrum.yml, src/modules/qt/graph.cpp, + src/modules/qt/graph.h: Add bar graph. + +2015-10-11 Dan Dennedy + + * src/modules/avformat/filter_avcolour_space.c, + src/modules/avformat/producer_avformat.c: Fix YCbCr colorspace conversion + wrong dest coefficients. + +2015-10-07 Brian Matherly + + * src/modules/qt/filter_audiospectrum.cpp, + src/modules/qt/filter_audiospectrum.yml, src/modules/qt/graph.cpp, + src/modules/qt/graph.h: Implement tension and fill parameters. + + * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/graph.cpp, + src/modules/qt/graph.h: Improve spectrum line graph. + +2015-09-30 Brian Matherly + + * src/modules/qt/Makefile, src/modules/qt/common.cpp, + src/modules/qt/common.h, src/modules/qt/filter_audiospectrum.cpp, + src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, + src/modules/qt/graph.cpp, src/modules/qt/graph.h: Move redundant code into + shared files for reuse. + +2015-09-26 Dan Dennedy + + * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp, + src/modules/qt/qimage_wrapper.h: Make qimage reject animated images. This + will allow loader to fallback to using avformat, which can already handle + animated images. + +2015-09-22 Bertrand Nouvel + + * src/modules/avformat/producer_avformat.c, + src/modules/avformat/producer_avformat.yml: Add seek_threshold property to + avformat producer. + +2015-09-21 Dan Dennedy + + * src/tests/test_tractor/test_tractor.cpp, + src/tests/test_tractor/test_tractor.pro, src/tests/tests.pro: Add unit tests + for tractor/multitrack/field. + +2015-09-17 Brian Matherly + + * src/modules/qt/Makefile, src/modules/qt/factory.c, + src/modules/qt/filter_audiospectrum.cpp, + src/modules/qt/filter_audiospectrum.yml: Add audiospectrum filter. + +2015-09-15 Dan Dennedy + + * presets/consumer/avformat/dv_ntsc/D10, + presets/consumer/avformat/dv_ntsc_wide/D10, + presets/consumer/avformat/dv_pal/D10, + presets/consumer/avformat/dv_pal_wide/D10, + presets/consumer/avformat/lossless/H.264, + presets/consumer/avformat/lossless/MPEG-2, + presets/consumer/avformat/lossless/MPEG-4, + presets/consumer/avformat/lossless/ProRes, + presets/consumer/avformat/lossless/ProRes-Kostya: Change GOP size 0 to 1 in + presets. As a matter of proactive correctness. + +2015-09-13 Dan Dennedy + + * presets/consumer/avformat/dv_ntsc/D10, + presets/consumer/avformat/dv_ntsc_wide/D10, + presets/consumer/avformat/dv_pal/D10, + presets/consumer/avformat/dv_pal_wide/D10: Set 0 B-frames for all D10 + presets. + +2015-09-11 Jean-Baptiste Mardelle + + * src/modules/motion_est/filter_autotrack_rectangle.c, + src/modules/vmfx/filter_shape.c: Use mlt_filter_get_length2 to get filter + length + +2015-09-05 Dan Dennedy + + * src/modules/avformat/consumer_avformat.c, + src/modules/avformat/filter_avcolour_space.c, + src/modules/avformat/filter_avdeinterlace.c, + src/modules/avformat/filter_swscale.c, + src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Fix + avformat build against FFmpeg and Libav master. This drops support for + FFmpeg v1.0; requires at least v1.1. Still works with Libav v9. CPU flags are + no longer required/used by libswscale. They are detected at runtime + automatically. + +2015-08-30 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Change the smpte2022 property prefix + to udp. + +2015-08-29 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Add smpte2022.interface property to + cbrts. + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Change the default UDP buffer size. + +2015-08-23 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Add RTP header to cbrts UDP output. + This can be disabled by settings smpte2022.rtp=0. + +2015-08-22 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Remove remux thread and add + smpte2022.buffer property. The remux thread was not necessary and adds + additional complexity. Now, the buffer between avformat and the socket is + configurable and self-adjusts. There was also some refactoring of types used + for times and muxrate. + +2015-08-21 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Add rate-controlled network delivery + to cbrts. This adds a strict network output thread based on a high + resolution timer with realtime process priority. + +2015-08-15 Dan Dennedy + + * src/modules/plusgpl/consumer_cbrts.c, + src/modules/plusgpl/consumer_cbrts.yml: Add UDP output to cbrts. This is + currently fixed to 7 TSPs, and there is not yet any sending rate control. + +2015-07-30 Dan Dennedy + + * configure, src/framework/mlt_version.h: Set interim version to 0.9.9. + 2015-07-29 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version diff -Nru mlt-0.9.8/configure mlt-6.0.0/configure --- mlt-0.9.8/configure 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/configure 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ #!/bin/sh -export version=0.9.8 +export version=6.0.0 export soversion=6 show_help() @@ -93,7 +93,7 @@ case $targetos in Darwin) - echo "CFLAGS+=-fPIC -D__DARWIN__ `sdl-config --cflags`" + echo "CFLAGS+=-fPIC `sdl-config --cflags`" echo "SHFLAGS=-dynamiclib" echo "LDFLAGS+=`sdl-config --libs`" ;; @@ -129,6 +129,13 @@ echo "LIBDL=-ldl" echo "RDYNAMIC=" echo "LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed" + # If the host system is MinGW running on Windows, then cause make to + # treat all .c files as C and not use CXX. mingw32-make is case + # insensitive, and .C is considered C++. + if uname -s | grep -iq mingw ; then + echo "%.o: %.c" + printf "\t\$(CC) -c \$(CFLAGS) \$(CPPFLAGS) -o \$@ \$<\n" + fi ;; *) ;; @@ -262,7 +269,7 @@ Linux|FreeBSD|NetBSD) LIBSUF=".so" ;; - MINGW32_NT-*|MinGW|mingw) + MINGW??_NT-*|MinGW|mingw) targetos="MinGW" LIBSUF=".dll" ;; diff -Nru mlt-0.9.8/debian/changelog mlt-6.0.0/debian/changelog --- mlt-0.9.8/debian/changelog 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/changelog 2016-02-18 19:35:48.000000000 +0000 @@ -1,3 +1,24 @@ +mlt (6.0.0-2) unstable; urgency=medium + + * Enable all hardening flags except of PIE, which produces a FTBFS. + * Overwrite false positive lintian warning hardening-no-bindnow for + python-mlt package. + * Add patch 01-empty-directory to fix installation of an empty directory. + * Add experimental patch 02-kfreebsd-ftbfs to fix a FTBFS on kfreebsd. + + -- Patrick Matthäi Thu, 18 Feb 2016 20:30:54 +0100 + +mlt (6.0.0-1) unstable; urgency=medium + + * New upstream release. + - Remove merged patch 01-mlt_ffmpeg-2.9. + * Bump Standards-Version to 3.9.7 (no changes required). + * Add new build dependencies on libexif-dev, libexif-gtk-dev, libmovit-dev + and libfftw3-dev to enable new features. + * Fix spelling error in debian/copyright. + + -- Patrick Matthäi Thu, 18 Feb 2016 10:03:56 +0100 + mlt (0.9.8-3) unstable; urgency=high * Add upstream patch 01-mlt_ffmpeg-2.9 to fix a FTBFS with newer FFmpeg diff -Nru mlt-0.9.8/debian/control mlt-6.0.0/debian/control --- mlt-0.9.8/debian/control 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/control 2016-02-18 19:35:48.000000000 +0000 @@ -11,6 +11,10 @@ libavformat-dev, libdv4-dev, libgtk2.0-dev, + libexif-dev, + libexif-gtk-dev, + libmovit-dev, + libfftw3-dev, libjack-dev, libquicktime-dev [!hurd-i386], libsamplerate-dev, @@ -25,7 +29,7 @@ swig, dh-python, python-all-dev (>= 2.6.6-3~) -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Section: libs Homepage: http://www.mltframework.org diff -Nru mlt-0.9.8/debian/copyright mlt-6.0.0/debian/copyright --- mlt-0.9.8/debian/copyright 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/copyright 2016-02-18 19:35:48.000000000 +0000 @@ -586,7 +586,7 @@ Free Software Foundation; version 2 of the License, or (at your option) any later version. . - On Debian systems, the complete text of version 2 of the GNU Library + On Debian systems, the complete text of version 2 of the GNU Library General Public License can be found in `/usr/share/common-licenses/LGPL-2'. License: LGPL-2.1+ diff -Nru mlt-0.9.8/debian/patches/01-empty-directory.diff mlt-6.0.0/debian/patches/01-empty-directory.diff --- mlt-0.9.8/debian/patches/01-empty-directory.diff 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/debian/patches/01-empty-directory.diff 2016-02-18 19:35:48.000000000 +0000 @@ -0,0 +1,14 @@ +Fix make install creates empty "movit" directory. + +diff -Naur mlt-6.0.0.orig/src/modules/opengl/Makefile mlt-6.0.0/src/modules/opengl/Makefile +--- mlt-6.0.0.orig/src/modules/opengl/Makefile 2016-02-18 00:43:24.000000000 +0100 ++++ mlt-6.0.0/src/modules/opengl/Makefile 2016-02-18 20:35:02.913144436 +0100 +@@ -69,7 +69,7 @@ + + install: all + install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt" +- install -d "$(DESTDIR)$(datadir)/mlt/opengl/movit" ++ install -d "$(DESTDIR)$(datadir)/mlt/opengl" + install -m 644 *.yml "$(DESTDIR)$(datadir)/mlt/opengl" + + ifneq ($(wildcard .depend),) diff -Nru mlt-0.9.8/debian/patches/01-mlt_ffmpeg-2.9.diff mlt-6.0.0/debian/patches/01-mlt_ffmpeg-2.9.diff --- mlt-0.9.8/debian/patches/01-mlt_ffmpeg-2.9.diff 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/patches/01-mlt_ffmpeg-2.9.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,507 +0,0 @@ -From 97c2dd0de4f578ad40d547eddf78fcb1e4a008a4 Mon Sep 17 00:00:00 2001 -From: Dan Dennedy -Date: Sat, 5 Sep 2015 13:06:21 -0700 -Subject: [PATCH] Fix avformat build against FFmpeg and Libav master. - -This drops support for FFmpeg v1.0; requires at least v1.1. Still works -with Libav v9. -CPU flags are no longer required/used by libswscale. They are detected -at runtime automatically. ---- - src/modules/avformat/consumer_avformat.c | 28 +++----- - src/modules/avformat/filter_avcolour_space.c | 20 ++---- - src/modules/avformat/filter_avdeinterlace.c | 22 +++---- - src/modules/avformat/filter_swscale.c | 18 ++---- - src/modules/avformat/producer_avformat.c | 95 +++++++++++++--------------- - src/modules/avformat/vdpau.c | 6 +- - 6 files changed, 81 insertions(+), 108 deletions(-) - -diff --git a/src/modules/avformat/consumer_avformat.c b/src/modules/avformat/consumer_avformat.c -index ef8b153..7947c47 100644 ---- a/src/modules/avformat/consumer_avformat.c -+++ b/src/modules/avformat/consumer_avformat.c -@@ -439,18 +439,18 @@ static void apply_properties( void *obj, mlt_properties properties, int flags ) - } - } - --static enum PixelFormat pick_pix_fmt( mlt_image_format img_fmt ) -+static enum AVPixelFormat pick_pix_fmt( mlt_image_format img_fmt ) - { - switch ( img_fmt ) - { - case mlt_image_rgb24: -- return PIX_FMT_RGB24; -+ return AV_PIX_FMT_RGB24; - case mlt_image_rgb24a: -- return PIX_FMT_RGBA; -+ return AV_PIX_FMT_RGBA; - case mlt_image_yuv420p: -- return PIX_FMT_YUV420P; -+ return AV_PIX_FMT_YUV420P; - default: -- return PIX_FMT_YUYV422; -+ return AV_PIX_FMT_YUYV422; - } - } - -@@ -798,7 +798,7 @@ static AVStream *add_video_stream( mlt_consumer consumer, AVFormatContext *oc, A - st->time_base = c->time_base; - - // Default to the codec's first pix_fmt if possible. -- c->pix_fmt = pix_fmt? av_get_pix_fmt( pix_fmt ) : codec? codec->pix_fmts[0] : PIX_FMT_YUV420P; -+ c->pix_fmt = pix_fmt? av_get_pix_fmt( pix_fmt ) : codec? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P; - - switch ( colorspace ) - { -@@ -1032,7 +1032,7 @@ static int open_video( mlt_properties properties, AVFormatContext *oc, AVStream - - if( codec && codec->pix_fmts ) - { -- const enum PixelFormat *p = codec->pix_fmts; -+ const enum AVPixelFormat *p = codec->pix_fmts; - for( ; *p!=-1; p++ ) - { - if( *p == video_enc->pix_fmt ) -@@ -1791,12 +1791,6 @@ static void *consumer_thread( void *arg ) - - // Do the colour space conversion - int flags = SWS_BICUBIC; --#ifdef USE_MMX -- flags |= SWS_CPU_CAPS_MMX; --#endif --#ifdef USE_SSE -- flags |= SWS_CPU_CAPS_MMX2; --#endif - struct SwsContext *context = sws_getContext( width, height, pick_pix_fmt( img_fmt ), - width, height, c->pix_fmt, flags, NULL, NULL, NULL); - sws_scale( context, (const uint8_t* const*) video_avframe->data, video_avframe->linesize, 0, height, -@@ -1808,9 +1802,9 @@ static void *consumer_thread( void *arg ) - // Apply the alpha if applicable - if ( !mlt_properties_get( properties, "mlt_image_format" ) || - strcmp( mlt_properties_get( properties, "mlt_image_format" ), "rgb24a" ) ) -- if ( c->pix_fmt == PIX_FMT_RGBA || -- c->pix_fmt == PIX_FMT_ARGB || -- c->pix_fmt == PIX_FMT_BGRA ) -+ if ( c->pix_fmt == AV_PIX_FMT_RGBA || -+ c->pix_fmt == AV_PIX_FMT_ARGB || -+ c->pix_fmt == AV_PIX_FMT_BGRA ) - { - uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); - register int n; -@@ -1844,8 +1838,6 @@ static void *consumer_thread( void *arg ) - av_init_packet(&pkt); - - // Set frame interlace hints -- c->coded_frame->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" ); -- c->coded_frame->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); - if ( mlt_properties_get_int( frame_properties, "progressive" ) ) - c->field_order = AV_FIELD_PROGRESSIVE; - else -diff --git a/src/modules/avformat/filter_avcolour_space.c b/src/modules/avformat/filter_avcolour_space.c -index f70fd08..910de0c 100644 ---- a/src/modules/avformat/filter_avcolour_space.c -+++ b/src/modules/avformat/filter_avcolour_space.c -@@ -47,17 +47,17 @@ static int convert_mlt_to_av_cs( mlt_image_format format ) - switch( format ) - { - case mlt_image_rgb24: -- value = PIX_FMT_RGB24; -+ value = AV_PIX_FMT_RGB24; - break; - case mlt_image_rgb24a: - case mlt_image_opengl: -- value = PIX_FMT_RGBA; -+ value = AV_PIX_FMT_RGBA; - break; - case mlt_image_yuv422: -- value = PIX_FMT_YUYV422; -+ value = AV_PIX_FMT_YUYV422; - break; - case mlt_image_yuv420p: -- value = PIX_FMT_YUV420P; -+ value = AV_PIX_FMT_YUV420P; - break; - default: - mlt_log_error( NULL, "[filter avcolor_space] Invalid format %s\n", -@@ -123,16 +123,10 @@ static int av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, - int flags = SWS_BICUBIC | SWS_ACCURATE_RND; - int error = -1; - -- if ( out_fmt == PIX_FMT_YUYV422 ) -+ if ( out_fmt == AV_PIX_FMT_YUYV422 ) - flags |= SWS_FULL_CHR_H_INP; - else - flags |= SWS_FULL_CHR_H_INT; --#ifdef USE_MMX -- flags |= SWS_CPU_CAPS_MMX; --#endif --#ifdef USE_SSE -- flags |= SWS_CPU_CAPS_MMX2; --#endif - - avpicture_fill( &input, in, in_fmt, width, height ); - avpicture_fill( &output, out, out_fmt, width, height ); -@@ -141,7 +135,7 @@ static int av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, - if ( context ) - { - // 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 ) -+ if ( out_fmt == AV_PIX_FMT_RGB24 || out_fmt == AV_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, -@@ -326,7 +320,7 @@ mlt_filter filter_avcolour_space_init( void *arg ) - int *width = (int*) arg; - if ( *width > 0 ) - { -- struct SwsContext *context = sws_getContext( *width, *width, PIX_FMT_RGB32, 64, 64, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); -+ struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); - if ( context ) - sws_freeContext( context ); - else -diff --git a/src/modules/avformat/filter_avdeinterlace.c b/src/modules/avformat/filter_avdeinterlace.c -index 9c0189e..91c435c 100644 ---- a/src/modules/avformat/filter_avdeinterlace.c -+++ b/src/modules/avformat/filter_avdeinterlace.c -@@ -234,28 +234,28 @@ static int mlt_avpicture_deinterlace(AVPicture *dst, const AVPicture *src, - { - int i; - -- if (pix_fmt != PIX_FMT_YUV420P && -- pix_fmt != PIX_FMT_YUV422P && -- pix_fmt != PIX_FMT_YUYV422 && -- pix_fmt != PIX_FMT_YUV444P && -- pix_fmt != PIX_FMT_YUV411P) -+ if (pix_fmt != AV_PIX_FMT_YUV420P && -+ pix_fmt != AV_PIX_FMT_YUV422P && -+ pix_fmt != AV_PIX_FMT_YUYV422 && -+ pix_fmt != AV_PIX_FMT_YUV444P && -+ pix_fmt != AV_PIX_FMT_YUV411P) - return -1; - if ((width & 3) != 0 || (height & 3) != 0) - return -1; - -- if ( pix_fmt != PIX_FMT_YUYV422 ) -+ if ( pix_fmt != AV_PIX_FMT_YUYV422 ) - { - for(i=0;i<3;i++) { - if (i == 1) { - switch(pix_fmt) { -- case PIX_FMT_YUV420P: -+ case AV_PIX_FMT_YUV420P: - width >>= 1; - height >>= 1; - break; -- case PIX_FMT_YUV422P: -+ case AV_PIX_FMT_YUV422P: - width >>= 1; - break; -- case PIX_FMT_YUV411P: -+ case AV_PIX_FMT_YUV411P: - width >>= 2; - break; - default: -@@ -312,8 +312,8 @@ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format - AVPicture *output = mlt_pool_alloc( sizeof( AVPicture ) ); - - // Fill the picture -- avpicture_fill( output, *image, PIX_FMT_YUYV422, *width, *height ); -- mlt_avpicture_deinterlace( output, output, PIX_FMT_YUYV422, *width, *height ); -+ avpicture_fill( output, *image, AV_PIX_FMT_YUYV422, *width, *height ); -+ mlt_avpicture_deinterlace( output, output, AV_PIX_FMT_YUYV422, *width, *height ); - - // Free the picture - mlt_pool_release( output ); -diff --git a/src/modules/avformat/filter_swscale.c b/src/modules/avformat/filter_swscale.c -index b8213a3..37c156d 100644 ---- a/src/modules/avformat/filter_swscale.c -+++ b/src/modules/avformat/filter_swscale.c -@@ -37,17 +37,17 @@ static inline int convert_mlt_to_av_cs( mlt_image_format format ) - switch( format ) - { - case mlt_image_rgb24: -- value = PIX_FMT_RGB24; -+ value = AV_PIX_FMT_RGB24; - break; - case mlt_image_rgb24a: - case mlt_image_opengl: -- value = PIX_FMT_RGBA; -+ value = AV_PIX_FMT_RGBA; - break; - case mlt_image_yuv422: -- value = PIX_FMT_YUYV422; -+ value = AV_PIX_FMT_YUYV422; - break; - case mlt_image_yuv420p: -- value = PIX_FMT_YUV420P; -+ value = AV_PIX_FMT_YUV420P; - break; - default: - fprintf( stderr, "Invalid format...\n" ); -@@ -108,12 +108,6 @@ static int filter_scale( mlt_frame frame, uint8_t **image, mlt_image_format *for - // XXX: we only know how to rescale packed formats - return 1; - } --#ifdef USE_MMX -- interp |= SWS_CPU_CAPS_MMX; --#endif --#ifdef USE_SSE -- interp |= SWS_CPU_CAPS_MMX2; --#endif - - // Convert the pixel formats - int avformat = convert_mlt_to_av_cs( *format ); -@@ -148,7 +142,7 @@ static int filter_scale( mlt_frame frame, uint8_t **image, mlt_image_format *for - uint8_t *alpha = mlt_frame_get_alpha( frame ); - if ( alpha ) - { -- avformat = PIX_FMT_GRAY8; -+ avformat = AV_PIX_FMT_GRAY8; - struct SwsContext *context = sws_getContext( iwidth, iheight, avformat, owidth, oheight, avformat, interp, NULL, NULL, NULL); - avpicture_fill( &input, alpha, avformat, iwidth, iheight ); - outbuf = mlt_pool_alloc( owidth * oheight ); -@@ -182,7 +176,7 @@ mlt_filter filter_swscale_init( mlt_profile profile, void *arg ) - int *width = (int*) arg; - if ( *width > 0 ) - { -- struct SwsContext *context = sws_getContext( *width, *width, PIX_FMT_RGB32, 64, 64, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); -+ struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); - if ( context ) - sws_freeContext( context ); - else -diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c -index 3495e2a..55e326d 100644 ---- a/src/modules/avformat/producer_avformat.c -+++ b/src/modules/avformat/producer_avformat.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - - #ifdef VDPAU - # include -@@ -504,21 +505,21 @@ static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **fo - return result; - } - --static enum PixelFormat pick_pix_fmt( enum PixelFormat pix_fmt ) -+static enum AVPixelFormat pick_pix_fmt( enum AVPixelFormat 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; -+ case AV_PIX_FMT_ARGB: -+ case AV_PIX_FMT_RGBA: -+ case AV_PIX_FMT_ABGR: -+ case AV_PIX_FMT_BGRA: -+ return AV_PIX_FMT_RGBA; - #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) - case AV_PIX_FMT_BAYER_RGGB16LE: -- return PIX_FMT_RGB24; -+ return AV_PIX_FMT_RGB24; - #endif - default: -- return PIX_FMT_YUV422P; -+ return AV_PIX_FMT_YUV422P; - } - } - -@@ -976,26 +977,26 @@ static int set_luma_transfer( struct SwsContext *context, int src_colorspace, - brightness, contrast, saturation ); - } - --static mlt_image_format pick_image_format( enum PixelFormat pix_fmt ) -+static mlt_image_format pick_image_format( enum AVPixelFormat pix_fmt ) - { - switch ( pix_fmt ) - { -- case PIX_FMT_ARGB: -- case PIX_FMT_RGBA: -- case PIX_FMT_ABGR: -- case PIX_FMT_BGRA: -+ case AV_PIX_FMT_ARGB: -+ case AV_PIX_FMT_RGBA: -+ case AV_PIX_FMT_ABGR: -+ case AV_PIX_FMT_BGRA: - return mlt_image_rgb24a; -- case PIX_FMT_YUV420P: -- case PIX_FMT_YUVJ420P: -- case PIX_FMT_YUVA420P: -+ case AV_PIX_FMT_YUV420P: -+ case AV_PIX_FMT_YUVJ420P: -+ case AV_PIX_FMT_YUVA420P: - return mlt_image_yuv420p; -- case PIX_FMT_RGB24: -- case PIX_FMT_BGR24: -- case PIX_FMT_GRAY8: -- case PIX_FMT_MONOWHITE: -- case PIX_FMT_MONOBLACK: -- case PIX_FMT_RGB8: -- case PIX_FMT_BGR8: -+ case AV_PIX_FMT_RGB24: -+ case AV_PIX_FMT_BGR24: -+ case AV_PIX_FMT_GRAY8: -+ case AV_PIX_FMT_MONOWHITE: -+ case AV_PIX_FMT_MONOBLACK: -+ case AV_PIX_FMT_RGB8: -+ case AV_PIX_FMT_BGR8: - #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) - case AV_PIX_FMT_BAYER_RGGB16LE: - return mlt_image_rgb24; -@@ -1071,21 +1072,14 @@ static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffe - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self->parent ) ); - int result = self->yuv_colorspace; - --#ifdef USE_MMX -- flags |= SWS_CPU_CAPS_MMX; --#endif --#ifdef USE_SSE -- 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 ( ( pix_fmt == AV_PIX_FMT_YUVA420P - #if defined(FFUDIV) -- || pix_fmt == PIX_FMT_YUVA444P -+ || pix_fmt == AV_PIX_FMT_YUVA444P - #endif - ) && - *format != mlt_image_rgb24a && *format != mlt_image_opengl && -@@ -1110,10 +1104,10 @@ static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffe - // avformat with no filters and explicitly requested. - #if defined(FFUDIV) && (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(48<<8)+100)) - struct SwsContext *context = sws_getContext(width, height, src_pix_fmt, -- width, height, PIX_FMT_YUV420P, flags, NULL, NULL, NULL); -+ width, height, AV_PIX_FMT_YUV420P, flags, NULL, NULL, NULL); - #else - struct SwsContext *context = sws_getContext( width, height, pix_fmt, -- width, height, self->full_luma ? PIX_FMT_YUVJ420P : PIX_FMT_YUV420P, -+ width, height, self->full_luma ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P, - flags, NULL, NULL, NULL); - #endif - -@@ -1133,9 +1127,9 @@ static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffe - else if ( *format == mlt_image_rgb24 ) - { - struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, -- width, height, PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); -+ width, height, AV_PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); - AVPicture output; -- avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); -+ avpicture_fill( &output, buffer, AV_PIX_FMT_RGB24, width, height ); - // 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, 0 ); - sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, -@@ -1145,9 +1139,9 @@ static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffe - else if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) - { - struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, -- width, height, PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); -+ width, height, AV_PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); - AVPicture output; -- avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); -+ avpicture_fill( &output, buffer, AV_PIX_FMT_RGBA, width, height ); - // 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, 0 ); - sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, -@@ -1158,13 +1152,13 @@ static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffe - { - #if defined(FFUDIV) && (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(48<<8)+100)) - struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, -- width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); -+ width, height, AV_PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); - #else - struct SwsContext *context = sws_getContext( width, height, pix_fmt, -- width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); -+ width, height, AV_PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); - #endif - AVPicture output; -- avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); -+ avpicture_fill( &output, buffer, AV_PIX_FMT_YUYV422, width, height ); - if ( !set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma, 0 ) ) - result = profile->colorspace; - sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, -@@ -1297,12 +1291,11 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form - double delay = mlt_properties_get_double( properties, "video_delay" ); - - // Seek if necessary -- const char *interp = mlt_properties_get( frame_properties, "rescale.interp" ); -- int preseek = must_decode -+ int preseek = must_decode && codec_context->has_b_frames; - #if defined(FFUDIV) -- && ( interp && strcmp( interp, "nearest" ) ) -+ const char *interp = mlt_properties_get( frame_properties, "rescale.interp" ); -+ preseek = preseek && interp && strcmp( interp, "nearest" ); - #endif -- && codec_context->has_b_frames; - int paused = seek_video( self, position, req_position, preseek ); - - // Seek might have reopened the file -@@ -1310,10 +1303,10 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form - stream = context->streams[ self->video_index ]; - codec_context = stream->codec; - 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 ) -+ codec_context->pix_fmt == AV_PIX_FMT_ARGB || -+ codec_context->pix_fmt == AV_PIX_FMT_RGBA || -+ codec_context->pix_fmt == AV_PIX_FMT_ABGR || -+ codec_context->pix_fmt == AV_PIX_FMT_BGRA ) - *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 ) { -@@ -1346,7 +1339,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form - picture.linesize[1] = codec_context->width / 2; - picture.linesize[2] = codec_context->width / 2; - yuv_colorspace = convert_image( self, (AVFrame*) &picture, *buffer, -- PIX_FMT_YUV420P, format, *width, *height, &alpha ); -+ AV_PIX_FMT_YUV420P, format, *width, *height, &alpha ); - } - else - #endif -@@ -1539,7 +1532,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form - VdpStatus status = vdp_surface_get_bits( render->surface, dest_format, planes, pitches ); - if ( status == VDP_STATUS_OK ) - { -- yuv_colorspace = convert_image( self, self->video_frame, *buffer, PIX_FMT_YUV420P, -+ yuv_colorspace = convert_image( self, self->video_frame, *buffer, AV_PIX_FMT_YUV420P, - format, *width, *height, &alpha ); - mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); - } -diff --git a/src/modules/avformat/vdpau.c b/src/modules/avformat/vdpau.c -index 719db1c..4f2e6b9 100644 ---- a/src/modules/avformat/vdpau.c -+++ b/src/modules/avformat/vdpau.c -@@ -136,9 +136,9 @@ static int vdpau_init( producer_avformat self ) - return success; - } - --static enum PixelFormat vdpau_get_format( struct AVCodecContext *s, const enum PixelFormat *fmt ) -+static enum AVPixelFormat vdpau_get_format( struct AVCodecContext *s, const enum AVPixelFormat *fmt ) - { -- return PIX_FMT_VDPAU_H264; -+ return AV_PIX_FMT_VDPAU_H264; - } - - static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame ) -@@ -240,7 +240,7 @@ static int vdpau_decoder_init( producer_avformat self ) - self->video_codec->release_buffer = vdpau_release_buffer; - self->video_codec->draw_horiz_band = vdpau_draw_horiz; - self->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; -- self->video_codec->pix_fmt = PIX_FMT_VDPAU_H264; -+ self->video_codec->pix_fmt = AV_PIX_FMT_VDPAU_H264; - - VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH; - uint32_t max_references = self->video_codec->refs; diff -Nru mlt-0.9.8/debian/patches/02-kfreebsd-ftbfs.diff mlt-6.0.0/debian/patches/02-kfreebsd-ftbfs.diff --- mlt-0.9.8/debian/patches/02-kfreebsd-ftbfs.diff 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/debian/patches/02-kfreebsd-ftbfs.diff 2016-02-18 19:35:48.000000000 +0000 @@ -0,0 +1,14 @@ +Experimental patch to fix a FTBFS on kfreebsd. + +diff --git a/src/modules/plusgpl/consumer_cbrts.c b/src/modules/plusgpl/consumer_cbrts.c +index 634cb56..563a7f9 100644 +--- a/src/modules/plusgpl/consumer_cbrts.c ++++ b/src/modules/plusgpl/consumer_cbrts.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + #endif + #include + #include diff -Nru mlt-0.9.8/debian/patches/series mlt-6.0.0/debian/patches/series --- mlt-0.9.8/debian/patches/series 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/patches/series 2016-02-18 19:35:48.000000000 +0000 @@ -1 +1,2 @@ -01-mlt_ffmpeg-2.9.diff +01-empty-directory.diff +02-kfreebsd-ftbfs.diff diff -Nru mlt-0.9.8/debian/python-mlt.lintian-overrides mlt-6.0.0/debian/python-mlt.lintian-overrides --- mlt-0.9.8/debian/python-mlt.lintian-overrides 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/python-mlt.lintian-overrides 2016-02-18 19:35:48.000000000 +0000 @@ -1,2 +1,3 @@ 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 +python-mlt: hardening-no-bindnow usr/lib/python*/dist-packages/_mlt.*.so diff -Nru mlt-0.9.8/debian/rules mlt-6.0.0/debian/rules --- mlt-0.9.8/debian/rules 2015-11-05 13:31:19.000000000 +0000 +++ mlt-6.0.0/debian/rules 2016-02-18 19:35:48.000000000 +0000 @@ -1,5 +1,7 @@ #!/usr/bin/make -f +export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie + # 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.8/docs/melt.1 mlt-6.0.0/docs/melt.1 --- mlt-0.9.8/docs/melt.1 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/docs/melt.1 2016-02-17 23:43:24.000000000 +0000 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.4. -.TH MELT "1" "March 2015" "melt 0.9.8" "User Commands" +.TH MELT "1" "February 2016" "melt 6.0.0" "User Commands" .SH NAME melt \- author, play, and encode multitrack audio/video compositions .SH SYNOPSIS diff -Nru mlt-0.9.8/Doxyfile mlt-6.0.0/Doxyfile --- mlt-0.9.8/Doxyfile 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/Doxyfile 2016-02-17 23:43:24.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.8 +PROJECT_NUMBER = 6.0.0 # 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.8/NEWS mlt-6.0.0/NEWS --- mlt-0.9.8/NEWS 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/NEWS 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,39 @@ MLT Release Notes ----------------- +Version 6.0.0 - February 17, 2015 + +This is a bugfix and minor enhancement release. Note that our release +versioning scheme has changed. We were approaching 1.0 but decided to +synchronize release version with the C library ABI version, which is currently +at v6. Here are some of the notable changes and enhancements: + +Framework + * Added unit tests for tractor, multitrack, and field. + * Deprecate mlt_frame_get_alpha_mask(). + * Added drop_count readable property to mlt_consumer. + * Added mlt_factory_repository(). + * Added mlt_properties_to_utf8(). + * Define MIN, MAX, CLAMP in mlt_types.h in not already defined. + * Switched to __APPLE__ and _WIN32 defines throughout codebase. + +Modules + * Added UDP and SMPTE 2022-2 support to cbrts consumer. + * Fixed build against latest FFmpeg versions - now requires v1.1 and up. + * Added audiospectrum filter to qt module. + * Added meta.media.0.codec.rotate property to avformat producer to let apps + and other services get the media orientation. + * Make the avformat producer handle animated images. + * Added style property to dynamictext filter. + * Added timewarp producer to core module. + * Fixed slowly accumulating A/V sync drift in mix audio transition. + * Added width_crop and width_fit properties to pango producer. + +Melt + * Added -abort option to simply exit without full cleanup. + * Fix key-press handling on Windows. + + Version 0.9.8 - July 29, 2015 Here is a list of the enhancements included in this release. There are many API diff -Nru mlt-0.9.8/presets/consumer/avformat/dv_ntsc/D10 mlt-6.0.0/presets/consumer/avformat/dv_ntsc/D10 --- mlt-0.9.8/presets/consumer/avformat/dv_ntsc/D10 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/dv_ntsc/D10 2016-02-17 23:43:24.000000000 +0000 @@ -4,7 +4,8 @@ minrate=50M maxrate=50M vb=50M -g=0 +g=1 +bf=0 flags=+ildct+low_delay dc=10 flags2=+ivlc+non_linear_q diff -Nru mlt-0.9.8/presets/consumer/avformat/dv_ntsc_wide/D10 mlt-6.0.0/presets/consumer/avformat/dv_ntsc_wide/D10 --- mlt-0.9.8/presets/consumer/avformat/dv_ntsc_wide/D10 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/dv_ntsc_wide/D10 2016-02-17 23:43:24.000000000 +0000 @@ -4,7 +4,8 @@ minrate=50M maxrate=50M vb=50M -g=0 +g=1 +bf=0 flags=+ildct+low_delay dc=10 flags2=+ivlc+non_linear_q diff -Nru mlt-0.9.8/presets/consumer/avformat/dv_pal/D10 mlt-6.0.0/presets/consumer/avformat/dv_pal/D10 --- mlt-0.9.8/presets/consumer/avformat/dv_pal/D10 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/dv_pal/D10 2016-02-17 23:43:24.000000000 +0000 @@ -5,7 +5,8 @@ minrate=50M maxrate=50M vb=50M -g=0 +g=1 +bf=0 flags=+ildct+low_delay dc=10 flags2=+ivlc+non_linear_q diff -Nru mlt-0.9.8/presets/consumer/avformat/dv_pal_wide/D10 mlt-6.0.0/presets/consumer/avformat/dv_pal_wide/D10 --- mlt-0.9.8/presets/consumer/avformat/dv_pal_wide/D10 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/dv_pal_wide/D10 2016-02-17 23:43:24.000000000 +0000 @@ -5,7 +5,8 @@ minrate=50M maxrate=50M vb=50M -g=0 +g=1 +bf=0 flags=+ildct+low_delay dc=10 flags2=+ivlc+non_linear_q diff -Nru mlt-0.9.8/presets/consumer/avformat/Flash mlt-6.0.0/presets/consumer/avformat/Flash --- mlt-0.9.8/presets/consumer/avformat/Flash 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/Flash 2016-02-17 23:43:24.000000000 +0000 @@ -7,6 +7,7 @@ vcodec=flv minrate=0 vb=1M +bf=0 progressive=1 meta.preset.extension=flv diff -Nru mlt-0.9.8/presets/consumer/avformat/lossless/H.264 mlt-6.0.0/presets/consumer/avformat/lossless/H.264 --- mlt-0.9.8/presets/consumer/avformat/lossless/H.264 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/lossless/H.264 2016-02-17 23:43:24.000000000 +0000 @@ -4,7 +4,7 @@ vcodec=libx264 intra=1 vb=0 -g=0 +g=1 bf=0 preset=medium qscale=1 diff -Nru mlt-0.9.8/presets/consumer/avformat/lossless/MPEG-2 mlt-6.0.0/presets/consumer/avformat/lossless/MPEG-2 --- mlt-0.9.8/presets/consumer/avformat/lossless/MPEG-2 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/lossless/MPEG-2 2016-02-17 23:43:24.000000000 +0000 @@ -4,7 +4,7 @@ vcodec=mpeg2video intra=1 vb=0 -g=0 +g=1 bf=0 qscale=1 diff -Nru mlt-0.9.8/presets/consumer/avformat/lossless/MPEG-4 mlt-6.0.0/presets/consumer/avformat/lossless/MPEG-4 --- mlt-0.9.8/presets/consumer/avformat/lossless/MPEG-4 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/lossless/MPEG-4 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ vcodec=mpeg4 qscale=1 intra=1 -g=0 +g=1 vb=0 bf=0 diff -Nru mlt-0.9.8/presets/consumer/avformat/lossless/ProRes mlt-6.0.0/presets/consumer/avformat/lossless/ProRes --- mlt-0.9.8/presets/consumer/avformat/lossless/ProRes 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/lossless/ProRes 2016-02-17 23:43:24.000000000 +0000 @@ -2,7 +2,7 @@ acodec=pcm_s16le vcodec=prores vb=0 -g=0 +g=1 bf=0 threads=1 vprofile=2 diff -Nru mlt-0.9.8/presets/consumer/avformat/lossless/ProRes-Kostya mlt-6.0.0/presets/consumer/avformat/lossless/ProRes-Kostya --- mlt-0.9.8/presets/consumer/avformat/lossless/ProRes-Kostya 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/lossless/ProRes-Kostya 2016-02-17 23:43:24.000000000 +0000 @@ -2,7 +2,7 @@ acodec=pcm_s16le vcodec=prores_ks vb=0 -g=0 +g=1 bf=0 vprofile=2 diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/BMP mlt-6.0.0/presets/consumer/avformat/stills/BMP --- mlt-0.9.8/presets/consumer/avformat/stills/BMP 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/BMP 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=bmp an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=bmp diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/DPX mlt-6.0.0/presets/consumer/avformat/stills/DPX --- mlt-0.9.8/presets/consumer/avformat/stills/DPX 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/DPX 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=dpx an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=dpx diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/JPEG mlt-6.0.0/presets/consumer/avformat/stills/JPEG --- mlt-0.9.8/presets/consumer/avformat/stills/JPEG 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/JPEG 2016-02-17 23:43:24.000000000 +0000 @@ -4,5 +4,7 @@ qscale=1 an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=jpg diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/PNG mlt-6.0.0/presets/consumer/avformat/stills/PNG --- mlt-0.9.8/presets/consumer/avformat/stills/PNG 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/PNG 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=png an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=png diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/PPM mlt-6.0.0/presets/consumer/avformat/stills/PPM --- mlt-0.9.8/presets/consumer/avformat/stills/PPM 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/PPM 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=ppm an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=ppm diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/TGA mlt-6.0.0/presets/consumer/avformat/stills/TGA --- mlt-0.9.8/presets/consumer/avformat/stills/TGA 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/TGA 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=targa an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=tga diff -Nru mlt-0.9.8/presets/consumer/avformat/stills/TIFF mlt-6.0.0/presets/consumer/avformat/stills/TIFF --- mlt-0.9.8/presets/consumer/avformat/stills/TIFF 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/stills/TIFF 2016-02-17 23:43:24.000000000 +0000 @@ -3,5 +3,7 @@ vcodec=tiff an=1 audio_off=1 +g=1 +bf=0 meta.preset.extension=tif diff -Nru mlt-0.9.8/presets/consumer/avformat/WMV mlt-6.0.0/presets/consumer/avformat/WMV --- mlt-0.9.8/presets/consumer/avformat/WMV 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/WMV 2016-02-17 23:43:24.000000000 +0000 @@ -4,6 +4,7 @@ vcodec=wmv2 g=100 vb=2M +bf=0 meta.preset.extension=wmv meta.preset.note=Windows Media Video (Windows Media Player 8 and up) diff -Nru mlt-0.9.8/presets/consumer/avformat/XDCAM-HD422 mlt-6.0.0/presets/consumer/avformat/XDCAM-HD422 --- mlt-0.9.8/presets/consumer/avformat/XDCAM-HD422 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/presets/consumer/avformat/XDCAM-HD422 2016-02-17 23:43:24.000000000 +0000 @@ -1,3 +1,5 @@ +f=mxf + vcodec=mpeg2video top_field_first=1 pix_fmt=yuv422p @@ -14,10 +16,11 @@ qmin=1 qmax=12 bufsize=36408333 +g=12 bf=2 vtag=xd5c acodec=pcm_s16le -meta.preset.extension=mov -meta.preset.note=use with mxf or mov +meta.preset.extension=mxf +meta.preset.note=Sony "workflow innovation" diff -Nru mlt-0.9.8/src/framework/mlt_animation.c mlt-6.0.0/src/framework/mlt_animation.c --- mlt-0.9.8/src/framework/mlt_animation.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_animation.c 2016-02-17 23:43:24.000000000 +0000 @@ -396,16 +396,19 @@ // Interpolation needed. else { - double progress; - mlt_property points[4]; - points[0] = node->prev? node->prev->item.property : node->item.property; - points[1] = node->item.property; - points[2] = node->next->item.property; - points[3] = node->next->next? node->next->next->item.property : node->next->item.property; - progress = position - node->item.frame; - progress /= node->next->item.frame - node->item.frame; - mlt_property_interpolate( item->property, points, progress, - self->fps, self->locale, item->keyframe_type ); + if ( item->property ) + { + double progress; + mlt_property points[4]; + points[0] = node->prev? node->prev->item.property : node->item.property; + points[1] = node->item.property; + points[2] = node->next->item.property; + points[3] = node->next->next? node->next->next->item.property : node->next->item.property; + progress = position - node->item.frame; + progress /= node->next->item.frame - node->item.frame; + mlt_property_interpolate( item->property, points, progress, + self->fps, self->locale, item->keyframe_type ); + } item->is_key = 0; } } diff -Nru mlt-0.9.8/src/framework/mlt_consumer.c mlt-6.0.0/src/framework/mlt_consumer.c --- mlt-0.9.8/src/framework/mlt_consumer.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_consumer.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief abstraction for all consumer services * \see mlt_consumer_s * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -574,6 +574,7 @@ } mlt_properties_set_int( properties, "frame_duration", frame_duration ); + mlt_properties_set_int( properties, "drop_count", 0 ); // Check and run an ante command if ( mlt_properties_get( properties, "ante" ) ) @@ -588,7 +589,7 @@ mlt_properties_set_int( properties, "_buffer", abs( priv->real_time ) + 1 ); priv->preroll = 1; -#ifdef WIN32 +#ifdef _WIN32 if ( priv->real_time == 1 || priv->real_time == -1 ) consumer_read_ahead_start( self ); #endif @@ -1512,6 +1513,12 @@ priv->consecutive_dropped = 0; } } + if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered") ) + { + int dropped = mlt_properties_get_int( properties, "drop_count" ); + mlt_properties_set_int( properties, "drop_count", ++dropped ); + mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped ); + } } if ( priv->is_purge ) { priv->is_purge = 0; @@ -1555,7 +1562,7 @@ { int buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); -#ifndef WIN32 +#ifndef _WIN32 consumer_read_ahead_start( self ); #endif if ( buffer > 1 ) @@ -1570,6 +1577,13 @@ frame = mlt_deque_pop_front( priv->queue ); pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); + if ( priv->real_time == 1 && frame && + !mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) ) + { + int dropped = mlt_properties_get_int( properties, "drop_count" ); + mlt_properties_set_int( properties, "drop_count", ++dropped ); + mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped ); + } } else // real_time == 0 { diff -Nru mlt-0.9.8/src/framework/mlt_consumer.h mlt-6.0.0/src/framework/mlt_consumer.h --- mlt-0.9.8/src/framework/mlt_consumer.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_consumer.h 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief abstraction for all consumer services * \see mlt_consumer_s * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -79,6 +79,7 @@ * \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 + * \properties \em drop_count the number of video frames not rendered since starting consumer */ struct mlt_consumer_s diff -Nru mlt-0.9.8/src/framework/mlt_factory.c mlt-6.0.0/src/framework/mlt_factory.c --- mlt-0.9.8/src/framework/mlt_factory.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_factory.c 2016-02-17 23:43:24.000000000 +0000 @@ -31,7 +31,7 @@ /** the default subdirectory of the datadir for holding presets */ #define PRESETS_DIR "/presets" -#ifdef WIN32 +#ifdef _WIN32 #ifdef PREFIX_LIB #undef PREFIX_LIB #endif @@ -43,7 +43,7 @@ #define PREFIX_LIB "\\lib\\mlt" /** the default subdirectory of the install prefix for holding module (plugin) data */ #define PREFIX_DATA "\\share\\mlt" -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) #include /** the default subdirectory of the libdir for holding modules (plugins) */ #define PREFIX_LIB "/lib/mlt" @@ -117,16 +117,16 @@ mlt_properties_set_or_default( global_properties, "MLT_PROFILE", getenv( "MLT_PROFILE" ), "dv_pal" ); mlt_properties_set_or_default( global_properties, "MLT_DATA", getenv( "MLT_DATA" ), PREFIX_DATA ); -#if defined(WIN32) +#if defined(_WIN32) char path[1024]; DWORD size = sizeof( path ); GetModuleFileName( NULL, path, size ); -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) char path[1024]; uint32_t size = sizeof( path ); _NSGetExecutablePath( path, &size ); #endif -#if defined(WIN32) || (defined(__DARWIN__) && defined(RELOCATABLE)) +#if defined(_WIN32) || (defined(__APPLE__) && defined(RELOCATABLE)) char *path2 = strdup( path ); char *appdir = dirname( path2 ); mlt_properties_set( global_properties, "MLT_APPDIR", appdir ); @@ -137,7 +137,7 @@ // Only initialise once if ( mlt_directory == NULL ) { -#if !defined(WIN32) && !(defined(__DARWIN__) && defined(RELOCATABLE)) +#if !defined(_WIN32) && !(defined(__APPLE__) && defined(RELOCATABLE)) // Allow user overrides if ( directory == NULL || !strcmp( directory, "" ) ) directory = getenv( "MLT_REPOSITORY" ); @@ -147,7 +147,7 @@ directory = PREFIX_LIB; // Store the prefix for later retrieval -#if defined(WIN32) || (defined(__DARWIN__) && defined(RELOCATABLE)) +#if defined(_WIN32) || (defined(__APPLE__) && defined(RELOCATABLE)) char *exedir = mlt_environment( "MLT_APPDIR" ); size_t size = strlen( exedir ); if ( global_properties && !getenv( "MLT_DATA" ) ) @@ -207,6 +207,16 @@ return repository; } +/** Fetch the repository. + * + * \return the global repository object + */ + +mlt_repository mlt_factory_repository( ) +{ + return repository; +} + /** Fetch the events object. * * \return the global factory event object @@ -427,7 +437,7 @@ { mlt_properties_close( event_object ); event_object = NULL; -#if !defined(WIN32) +#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 ); diff -Nru mlt-0.9.8/src/framework/mlt_factory.h mlt-6.0.0/src/framework/mlt_factory.h --- mlt-0.9.8/src/framework/mlt_factory.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_factory.h 2016-02-17 23:43:24.000000000 +0000 @@ -46,6 +46,7 @@ */ extern mlt_repository mlt_factory_init( const char *directory ); +extern mlt_repository mlt_factory_repository(); extern const char *mlt_factory_directory( ); extern char *mlt_environment( const char *name ); extern int mlt_environment_set( const char *name, const char *value ); diff -Nru mlt-0.9.8/src/framework/mlt_filter.c mlt-6.0.0/src/framework/mlt_filter.c --- mlt-0.9.8/src/framework/mlt_filter.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_filter.c 2016-02-17 23:43:24.000000000 +0000 @@ -57,7 +57,6 @@ // Default in, out, track properties mlt_properties_set_position( properties, "in", 0 ); mlt_properties_set_position( properties, "out", 0 ); - mlt_properties_set_int( properties, "track", 0 ); return 0; } diff -Nru mlt-0.9.8/src/framework/mlt_frame.c mlt-6.0.0/src/framework/mlt_frame.c --- mlt-0.9.8/src/framework/mlt_frame.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_frame.c 2016-02-17 23:43:24.000000000 +0000 @@ -622,6 +622,7 @@ * channel if one does not already exist. * * \public \memberof mlt_frame_s + * \deprecated use mlt_frame_get_alpha() instead * \param self a frame * \return the alpha channel */ diff -Nru mlt-0.9.8/src/framework/mlt_multitrack.c mlt-6.0.0/src/framework/mlt_multitrack.c --- mlt-0.9.8/src/framework/mlt_multitrack.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_multitrack.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief multitrack service class * \see mlt_multitrack_s * - * Copyright (C) 2003-2015 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -346,7 +346,10 @@ int mlt_multitrack_count( mlt_multitrack self ) { - return self->count; + if ( self == NULL ) + return 0; + else + return self->count; } /** Get an individual track as a producer. @@ -361,7 +364,7 @@ { mlt_producer producer = NULL; - if ( self->list != NULL && track < self->count ) + if ( self->list != NULL && track >= 0 && track < self->count ) producer = self->list[ track ]->producer; return producer; @@ -545,7 +548,7 @@ mlt_multitrack self = parent->child; // Check if we have a track for this index - if ( index < self->count && self->list[ index ] != NULL ) + if ( index >= 0 && index < self->count && self->list[ index ] != NULL ) { // Get the producer for this track mlt_producer producer = self->list[ index ]->producer; diff -Nru mlt-0.9.8/src/framework/mlt_pool.c mlt-6.0.0/src/framework/mlt_pool.c --- mlt-0.9.8/src/framework/mlt_pool.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_pool.c 2016-02-17 23:43:24.000000000 +0000 @@ -34,7 +34,7 @@ #endif // Macros to re-assign system functions. -#ifdef WIN32 +#ifdef _WIN32 # define mlt_free _aligned_free # define mlt_alloc(X) _aligned_malloc( (X), 16 ) #else diff -Nru mlt-0.9.8/src/framework/mlt_profile.c mlt-6.0.0/src/framework/mlt_profile.c --- mlt-0.9.8/src/framework/mlt_profile.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_profile.c 2016-02-17 23:43:24.000000000 +0000 @@ -54,7 +54,7 @@ filename = calloc( 1, strlen( name ) + 1 ); } // Load from $datadir/mlt/profiles - else if ( prefix == NULL ) + else if ( !prefix && mlt_environment( "MLT_DATA" ) ) { prefix = mlt_environment( "MLT_DATA" ); filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 1 ); @@ -62,13 +62,18 @@ strcat( filename, PROFILES_DIR ); } // Use environment variable instead - else + else if ( prefix ) { filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 ); strcpy( filename, prefix ); if ( filename[ strlen( filename ) - 1 ] != '/' ) filename[ strlen( filename ) ] = '/'; } + else + { + mlt_properties_close( properties ); + return profile; + } // Finish loading strcat( filename, name ); diff -Nru mlt-0.9.8/src/framework/mlt_properties.c mlt-6.0.0/src/framework/mlt_properties.c --- mlt-0.9.8/src/framework/mlt_properties.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_properties.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief Properties class definition * \see mlt_properties_s * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -140,7 +140,7 @@ { property_list *list = self->local; -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( list->locale ) freelocale( list->locale ); list->locale = newlocale( LC_NUMERIC_MASK, locale, NULL ); @@ -173,13 +173,22 @@ if ( list->locale ) { -#if defined(__DARWIN__) +#if defined(__APPLE__) result = querylocale( LC_NUMERIC, list->locale ); #elif defined(__GLIBC__) result = list->locale->__names[ LC_NUMERIC ]; #else result = list->locale; #endif +#if defined(_WIN32) + if ( result ) + { + // Convert the string from ANSI code page to UTF-8. + mlt_properties_set( self, "_lcnumeric_in", result ); + mlt_properties_to_utf8( self, "_lcnumeric_in", "_lcnumeric_out" ); + result = mlt_properties_get( self, "_lcnumeric_out" ); + } +#endif } return result; } @@ -435,6 +444,9 @@ int mlt_properties_inherit( mlt_properties self, mlt_properties that ) { if ( !self || !that ) return 1; + + mlt_properties_lock( that ); + int count = mlt_properties_count( that ); int i = 0; for ( i = 0; i < count; i ++ ) @@ -446,6 +458,9 @@ mlt_properties_set( self, name, value ); } } + + mlt_properties_unlock( that ); + return 0; } @@ -699,7 +714,7 @@ // Determine the value if ( isdigit( id[ 0 ] ) ) { -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) property_list *list = self->local; if ( list->locale ) current = strtod_l( id, NULL, list->locale ); @@ -1395,7 +1410,7 @@ free( list->name[ index ] ); } -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) // Cleanup locale if ( list->locale ) freelocale( list->locale ); @@ -2515,7 +2530,7 @@ return value == NULL ? rect : mlt_property_anim_get_rect( value, fps, list->locale, position, length ); } -#ifndef WIN32 +#ifndef _WIN32 // See win32/win32.c for win32 implementation. diff -Nru mlt-0.9.8/src/framework/mlt_properties.h mlt-6.0.0/src/framework/mlt_properties.h --- mlt-0.9.8/src/framework/mlt_properties.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_properties.h 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief Properties class declaration * \see mlt_properties_s * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -108,4 +108,5 @@ 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 ); +extern int mlt_properties_to_utf8( mlt_properties properties, const char *name_from, const char *name_to ); #endif diff -Nru mlt-0.9.8/src/framework/mlt_property.c mlt-6.0.0/src/framework/mlt_property.c --- mlt-0.9.8/src/framework/mlt_property.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_property.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief Property class definition * \see mlt_property_s * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -278,7 +278,7 @@ s = copy; pos = strrchr( s, ':' ); -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) char *orig_localename = NULL; if ( locale ) { @@ -294,7 +294,7 @@ #endif if ( pos ) { -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( locale ) seconds = strtod_l( pos + 1, NULL, locale ); else @@ -312,7 +312,7 @@ } } else { -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( locale ) seconds = strtod_l( s, NULL, locale ); else @@ -320,7 +320,7 @@ seconds = strtod( s, NULL ); } -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); @@ -489,7 +489,7 @@ char *end = NULL; double result; -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( locale ) result = strtod_l( value, &end, locale ); else @@ -511,7 +511,7 @@ if ( end && end[0] == '%' ) result /= 100.0; -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); @@ -695,7 +695,7 @@ { // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale -#if defined(__DARWIN__) +#if defined(__APPLE__) const char *localename = querylocale( LC_NUMERIC, locale ); #elif defined(__GLIBC__) const char *localename = locale->__names[ LC_NUMERIC ]; @@ -846,30 +846,30 @@ { int hours, mins, secs; char frame_sep = ':'; + int temp_frames; if ( fps == 30000.0/1001.0 ) { fps = 30.0; if ( drop ) { - int i, max_frames = frames; - for ( i = 1800; i <= max_frames; i += 1800 ) + int i; + for ( i = 1800; i <= frames; i += 1800 ) { if ( i % 18000 ) - { - max_frames += 2; frames += 2; - } } frame_sep = ';'; } } hours = frames / ( fps * 3600 ); - frames -= hours * ( fps * 3600 ); - mins = frames / ( fps * 60 ); - frames -= mins * ( fps * 60 ); - secs = frames / fps; - frames -= secs * fps; + temp_frames = frames - hours * 3600 * fps; + + mins = temp_frames / ( fps * 60 ); + temp_frames = frames - ( hours * 3600 + mins * 60 ) * fps; + + secs = temp_frames / fps; + frames -= lrint( ( hours * 3600 + mins * 60 + secs ) * fps ); sprintf( s, "%02d:%02d:%02d%c%0*d", hours, mins, secs, frame_sep, ( fps > 999? 4 : fps > 99? 3 : 2 ), frames ); @@ -887,11 +887,12 @@ { int hours, mins; double secs; + int temp_frames; hours = frames / ( fps * 3600 ); - frames -= hours * ( fps * 3600 ); - mins = frames / ( fps * 60 ); - frames -= mins * ( fps * 60 ); + temp_frames = frames - hours * 3600 * fps; + mins = temp_frames / ( fps * 60 ); + frames -= lrint( ( hours * 3600 + mins * 60 ) * fps ); secs = (double) frames / fps; sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs ); @@ -928,7 +929,7 @@ { // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale -#if defined(__DARWIN__) +#if defined(__APPLE__) const char *localename = querylocale( LC_NUMERIC, locale ); #elif defined(__GLIBC__) const char *localename = locale->__names[ LC_NUMERIC ]; @@ -1023,7 +1024,7 @@ double temp; char *p = NULL; -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( locale ) temp = strtod_l( self->prop_string, &p, locale ); else @@ -1043,7 +1044,7 @@ temp = strtod( self->prop_string, &p ); -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); @@ -1527,7 +1528,7 @@ char *p = NULL; int count = 0; -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) char *orig_localename = NULL; if ( locale ) { // Protect damaging the global locale from a temporary locale on another thread. @@ -1544,7 +1545,7 @@ while ( *value ) { double temp; -#if defined(__GLIBC__) || defined(__DARWIN__) +#if defined(__GLIBC__) || defined(__APPLE__) if ( locale ) temp = strtod_l( value, &p, locale ); else @@ -1579,7 +1580,7 @@ count ++; } -#if !defined(__GLIBC__) && !defined(__DARWIN__) +#if !defined(__GLIBC__) && !defined(__APPLE__) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); diff -Nru mlt-0.9.8/src/framework/mlt_property.h mlt-6.0.0/src/framework/mlt_property.h --- mlt-0.9.8/src/framework/mlt_property.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_property.h 2016-02-17 23:43:24.000000000 +0000 @@ -30,7 +30,7 @@ #include #endif -#if defined(__GLIBC__) || defined(__DARWIN__) || (__FreeBSD_version >= 900506) +#if defined(__GLIBC__) || defined(__APPLE__) || (__FreeBSD_version >= 900506) #include #else typedef char* locale_t; diff -Nru mlt-0.9.8/src/framework/mlt_repository.c mlt-6.0.0/src/framework/mlt_repository.c --- mlt-0.9.8/src/framework/mlt_repository.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_repository.c 2016-02-17 23:43:24.000000000 +0000 @@ -78,7 +78,7 @@ int i; int plugin_count = 0; -#ifdef WIN32 +#ifdef _WIN32 char *syspath = getenv("PATH"); char *exedir = mlt_environment( "MLT_APPDIR" ); char *newpath = "PATH="; diff -Nru mlt-0.9.8/src/framework/mlt_tractor.c mlt-6.0.0/src/framework/mlt_tractor.c --- mlt-0.9.8/src/framework/mlt_tractor.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_tractor.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief tractor service class * \see mlt_tractor_s * - * Copyright (C) 2003-2015 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -318,18 +318,18 @@ int a_track = mlt_transition_get_a_track( transition ); int b_track = mlt_transition_get_b_track( transition ); - if ( a_track > index || b_track > index ) + if ( a_track >= index || b_track >= index ) { - a_track = a_track > index ? a_track - 1 : a_track; - b_track = b_track > index ? b_track - 1 : b_track; + a_track = MAX( a_track >= index ? a_track - 1 : a_track, 0 ); + b_track = MAX( b_track >= index ? b_track - 1 : b_track, 0 ); mlt_transition_set_tracks( transition, a_track, b_track ); } } else if ( type == filter_type ) { int current_track = mlt_properties_get_int( properties, "track" ); - if ( current_track > index ) - mlt_properties_set_int( properties, "track", current_track - 1 ); + if ( current_track >= index ) + mlt_properties_set_int( properties, "track", MAX( current_track - 1, 0 ) ); } service = mlt_service_producer( service ); } diff -Nru mlt-0.9.8/src/framework/mlt_transition.c mlt-6.0.0/src/framework/mlt_transition.c --- mlt-0.9.8/src/framework/mlt_transition.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_transition.c 2016-02-17 23:43:24.000000000 +0000 @@ -3,7 +3,7 @@ * \brief abstraction for all transition services * \see mlt_transition_s * - * Copyright (C) 2003-2015 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -427,6 +427,8 @@ a_track = b_track; b_track = mlt_properties_get_int( properties, "a_track" ); } + a_track = a_track < 0 ? 0 : a_track; + b_track = b_track < 0 ? 0 : b_track; // Only act on this operation once per multitrack iteration from the tractor if ( !self->held ) diff -Nru mlt-0.9.8/src/framework/mlt_types.h mlt-6.0.0/src/framework/mlt_types.h --- mlt-0.9.8/src/framework/mlt_types.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_types.h 2016-02-17 23:43:24.000000000 +0000 @@ -2,7 +2,7 @@ * \file mlt_types.h * \brief Provides forward definitions of all public types * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -180,7 +180,17 @@ #define MLT_CONSUMER(x) ( ( mlt_consumer )( x ) ) /**< Cast to a Consumer pointer */ #define MLT_FRAME(x) ( ( mlt_frame )( x ) ) /**< Cast to a Frame pointer */ -#ifdef WIN32 +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef CLAMP +#define CLAMP(x, min, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) +#endif + +#ifdef _WIN32 #include /* Win32 compatibility function declarations */ extern int usleep(unsigned int useconds); diff -Nru mlt-0.9.8/src/framework/mlt.vers mlt-6.0.0/src/framework/mlt.vers --- mlt-0.9.8/src/framework/mlt.vers 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt.vers 2016-02-17 23:43:24.000000000 +0000 @@ -475,3 +475,9 @@ mlt_animation_key_count; mlt_animation_key_get; } MLT_0.9.4; + +MLT_0.9.10 { + global: + mlt_factory_repository; + mlt_properties_to_utf8; +} MLT_0.9.8; diff -Nru mlt-0.9.8/src/framework/mlt_version.h mlt-6.0.0/src/framework/mlt_version.h --- mlt-0.9.8/src/framework/mlt_version.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/framework/mlt_version.h 2016-02-17 23:43:24.000000000 +0000 @@ -2,7 +2,7 @@ * \file mlt_version.h * \brief contains version information * - * Copyright (C) 2010-2015 Meltytech, LLC + * Copyright (C) 2010-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,9 +26,9 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define LIBMLT_VERSION_MAJOR 0 -#define LIBMLT_VERSION_MINOR 9 -#define LIBMLT_VERSION_REVISION 8 +#define LIBMLT_VERSION_MAJOR 6 +#define LIBMLT_VERSION_MINOR 0 +#define LIBMLT_VERSION_REVISION 0 #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.8/src/melt/io.c mlt-6.0.0/src/melt/io.c --- mlt-0.9.8/src/melt/io.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/melt/io.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * io.c -- melt input/output - * Copyright (C) 2002-2014 Meltytech, LLC + * Copyright (C) 2002-2015 Meltytech, LLC * * 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 @@ -26,13 +26,14 @@ #include #include #include -#ifndef WIN32 +#ifndef _WIN32 #include #else // MinGW defines struct timespec in pthread.h #include // for nanosleep() #include +#include #endif #include #include @@ -99,10 +100,12 @@ /** This stores the previous settings */ -#ifndef WIN32 +#ifndef _WIN32 static struct termios oldtty; -static int mode = 0; +#else +static DWORD oldtty; #endif +static int mode = 0; /** This is called automatically on application exit to restore the previous tty settings. @@ -110,13 +113,18 @@ void term_exit(void) { -#ifndef WIN32 if ( mode == 1 ) { +#ifndef _WIN32 tcsetattr( 0, TCSANOW, &oldtty ); +#else + HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); + if (h) { + SetConsoleMode( h, oldtty ); + } +#endif mode = 0; } -#endif } /** Init terminal so that we can grab keys without blocking. @@ -124,7 +132,7 @@ void term_init( ) { -#ifndef WIN32 +#ifndef _WIN32 struct termios tty; tcgetattr( 0, &tty ); @@ -139,11 +147,19 @@ tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSANOW, &tty ); +#else + HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); + if (h) { + DWORD tty; + GetConsoleMode( h, &tty ); + oldtty = tty; + SetConsoleMode( h, mode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT ) ); + } +#endif mode = 1; atexit( term_exit ); -#endif } /** Check for a keypress without blocking infinitely. @@ -152,28 +168,38 @@ int term_read( ) { -#ifndef WIN32 - int n = 1; - unsigned char ch; - struct timeval tv; - fd_set rfds; - - FD_ZERO( &rfds ); - FD_SET( 0, &rfds ); - tv.tv_sec = 0; - tv.tv_usec = 40000; - n = select( 1, &rfds, NULL, NULL, &tv ); - if (n > 0) +#ifndef _WIN32 + int n = 1; + unsigned char ch; + struct timeval tv; + fd_set rfds; + + FD_ZERO( &rfds ); + FD_SET( 0, &rfds ); + tv.tv_sec = 0; + tv.tv_usec = 40000; + n = select( 1, &rfds, NULL, NULL, &tv ); + if (n > 0) { - n = read( 0, &ch, 1 ); + n = read( 0, &ch, 1 ); tcflush( 0, TCIFLUSH ); - if (n == 1) - return ch; - return n; - } + if (n == 1) + return ch; + return n; + } #else - struct timespec tm = { 0, 40000000 }; - nanosleep( &tm, NULL ); + HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); + if ( h && WaitForSingleObject( h, 0 ) == WAIT_OBJECT_0 ) + { + DWORD count; + TCHAR c = 0; + ReadConsole( h, &c, 1, &count, NULL ); + return (int) c; + } else { + struct timespec tm = { 0, 40000000 }; + nanosleep( &tm, NULL ); + return 0; + } #endif return -1; } diff -Nru mlt-0.9.8/src/melt/melt.c mlt-6.0.0/src/melt/melt.c --- mlt-0.9.8/src/melt/melt.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/melt/melt.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * melt.c -- MLT command line utility - * Copyright (C) 2002-2014 Meltytech, LLC + * Copyright (C) 2002-2016 Meltytech, LLC * * 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 @@ -31,7 +31,7 @@ #include -#if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL) +#if (defined(__APPLE__) || defined(_WIN32)) && !defined(MELT_NOSDL) #include #endif @@ -350,7 +350,7 @@ } } -#if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL) +#if (defined(__APPLE__) || defined(_WIN32)) && !defined(MELT_NOSDL) static void event_handling( mlt_producer producer, mlt_consumer consumer ) { @@ -382,6 +382,7 @@ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); int silent = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent" ); int progress = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress" ); + int is_getc = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_getc" ); struct timespec tm = { 0, 40000000 }; int total_length = mlt_producer_get_length( producer ); int last_position = 0; @@ -390,7 +391,8 @@ { if ( !silent && !progress ) { - term_init( ); + if ( !is_getc ) + term_init( ); fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" ); fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" ); @@ -406,7 +408,12 @@ while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) ) { - int value = ( silent || progress )? -1 : term_read( ); + int value = ( silent || progress || is_getc )? -1 : term_read( ); + if ( is_getc ) + { + value = getc( stdin ); + value = ( value == EOF )? 'q' : value; + } if ( value != -1 ) { @@ -414,7 +421,7 @@ transport_action( producer, string ); } -#if (defined(__DARWIN__) || defined(WIN32)) && !defined(MELT_NOSDL) +#if (defined(__APPLE__) || defined(_WIN32)) && !defined(MELT_NOSDL) event_handling( producer, consumer ); #endif @@ -461,6 +468,7 @@ " -consumer id[:arg] [name=value]* Set the consumer (sink)\n" " -debug Set the logging level to debug\n" " -filter filter[:arg] [name=value]* Add a filter to the current track\n" +" -getc Get keyboard input using getc\n" " -group [name=value]* Apply properties repeatedly\n" " -help Show this message\n" " -jack Enable JACK transport synchronization\n" @@ -684,6 +692,7 @@ static void on_fatal_error( mlt_properties owner, mlt_consumer consumer ) { mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "done", 1 ); + mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "melt_error", 1 ); } int main( int argc, char **argv ) @@ -695,6 +704,9 @@ mlt_profile profile = NULL; int is_progress = 0; int is_silent = 0; + int is_abort = 0; + int is_getc = 0; + int error = 0; mlt_profile backup_profile; // Handle abnormal exit situations. @@ -705,10 +717,6 @@ // Construct the factory mlt_repository repo = mlt_factory_init( NULL ); -#if defined(WIN32) && !defined(MELT_NOSDL) - is_silent = 1; -#endif - for ( i = 1; i < argc; i ++ ) { // Check for serialisation switch @@ -803,7 +811,7 @@ else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) ) { fprintf( stdout, "%s " VERSION "\n" - "Copyright (C) 2002-2014 Meltytech, LLC\n" + "Copyright (C) 2002-2016 Meltytech, LLC\n" "\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", @@ -814,6 +822,14 @@ { mlt_log_set_level( MLT_LOG_DEBUG ); } + else if ( !strcmp( argv[ i ], "-abort" ) ) + { + is_abort = 1; + } + else if ( !strcmp( argv[ i ], "-getc" ) ) + { + is_getc = 1; + } } if ( !is_silent && !isatty( STDIN_FILENO ) && !is_progress ) is_progress = 1; @@ -886,6 +902,8 @@ mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress ); if ( is_silent ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent ); + if ( is_getc ) + mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_getc", is_getc ); } if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 ) @@ -942,7 +960,7 @@ // Try to exit gracefully upon these signals signal( SIGINT, stop_handler ); signal( SIGTERM, stop_handler ); -#ifndef WIN32 +#ifndef _WIN32 signal( SIGHUP, stop_handler ); signal( SIGPIPE, stop_handler ); #endif @@ -968,10 +986,15 @@ // Disconnect producer from consumer to prevent ref cycles from closing services if ( consumer ) { + error = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_error" ); mlt_consumer_connect( consumer, NULL ); - mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-cleanup", NULL); + if ( !is_abort ) + mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-cleanup", NULL); } + if ( is_abort ) + return error; + // Close the producer if ( melt != NULL ) mlt_producer_close( melt ); @@ -990,5 +1013,5 @@ mlt_factory_close( ); #endif - return 0; + return error; } diff -Nru mlt-0.9.8/src/mlt++/config.h mlt-6.0.0/src/mlt++/config.h --- mlt-0.9.8/src/mlt++/config.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/mlt++/config.h 2016-02-17 23:43:24.000000000 +0000 @@ -21,7 +21,7 @@ #ifndef MLTPP_CONFIG_H #define MLTPP_CONFIG_H -#if defined(WIN32) +#if defined(_WIN32) #ifdef MLTPP_EXPORTS #define MLTPP_DECLSPEC __declspec( dllexport ) #else diff -Nru mlt-0.9.8/src/mlt++/configure mlt-6.0.0/src/mlt++/configure --- mlt-0.9.8/src/mlt++/configure 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/mlt++/configure 2016-02-17 23:43:24.000000000 +0000 @@ -7,7 +7,7 @@ case $targetos in Darwin) echo LIBSUF=.dylib - echo "CXXFLAGS+=-D__DARWIN__ -Wall -fPIC" + echo "CXXFLAGS+=-Wall -fPIC" echo "LIBFLAGS=-dynamiclib -single_module" ;; Linux|FreeBSD|NetBSD|GNU/kFreeBSD|GNU) diff -Nru mlt-0.9.8/src/mlt++/MltProperties.cpp mlt-6.0.0/src/mlt++/MltProperties.cpp --- mlt-0.9.8/src/mlt++/MltProperties.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/mlt++/MltProperties.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -246,7 +246,7 @@ return mlt_properties_save( get_properties( ), file ); } -#if defined( __DARWIN__ ) && GCC_VERSION < 40000 +#if defined( __APPLE__ ) && GCC_VERSION < 40000 Event *Properties::listen( const char *id, void *object, void (*listener)( ... ) ) { diff -Nru mlt-0.9.8/src/mlt++/MltProperties.h mlt-6.0.0/src/mlt++/MltProperties.h --- mlt-0.9.8/src/mlt++/MltProperties.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/mlt++/MltProperties.h 2016-02-17 23:43:24.000000000 +0000 @@ -82,7 +82,7 @@ void debug( const char *title = "Object", FILE *output = stderr ); void load( const char *file ); int save( const char *file ); - #if defined( __DARWIN__ ) && GCC_VERSION < 40000 + #if defined( __APPLE__ ) && GCC_VERSION < 40000 Event *listen( const char *id, void *object, void (*)( ... ) ); #else Event *listen( const char *id, void *object, mlt_listener ); diff -Nru mlt-0.9.8/src/modules/avformat/consumer_avformat.c mlt-6.0.0/src/modules/avformat/consumer_avformat.c --- mlt-0.9.8/src/modules/avformat/consumer_avformat.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/consumer_avformat.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * consumer_avformat.c -- an encoder based on avformat - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -58,7 +58,7 @@ #define MAX_AUDIO_STREAMS (8) #define AUDIO_ENCODE_BUFFER_SIZE (48000 * 2 * MAX_AUDIO_STREAMS) #define AUDIO_BUFFER_SIZE (1024 * 42) -#define VIDEO_BUFFER_SIZE (2048 * 2048) +#define VIDEO_BUFFER_SIZE (8192 * 8192) // // This structure should be extended and made globally available in mlt @@ -439,18 +439,18 @@ } } -static enum PixelFormat pick_pix_fmt( mlt_image_format img_fmt ) +static enum AVPixelFormat pick_pix_fmt( mlt_image_format img_fmt ) { switch ( img_fmt ) { case mlt_image_rgb24: - return PIX_FMT_RGB24; + return AV_PIX_FMT_RGB24; case mlt_image_rgb24a: - return PIX_FMT_RGBA; + return AV_PIX_FMT_RGBA; case mlt_image_yuv420p: - return PIX_FMT_YUV420P; + return AV_PIX_FMT_YUV420P; default: - return PIX_FMT_YUYV422; + return AV_PIX_FMT_YUYV422; } } @@ -798,7 +798,7 @@ st->time_base = c->time_base; // Default to the codec's first pix_fmt if possible. - c->pix_fmt = pix_fmt? av_get_pix_fmt( pix_fmt ) : codec? codec->pix_fmts[0] : PIX_FMT_YUV420P; + c->pix_fmt = pix_fmt? av_get_pix_fmt( pix_fmt ) : codec? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P; switch ( colorspace ) { @@ -1032,7 +1032,7 @@ if( codec && codec->pix_fmts ) { - const enum PixelFormat *p = codec->pix_fmts; + const enum AVPixelFormat *p = codec->pix_fmts; for( ; *p!=-1; p++ ) { if( *p == video_enc->pix_fmt ) @@ -1776,11 +1776,12 @@ // Convert the mlt frame to an AVPicture if ( img_fmt == mlt_image_yuv420p ) { - memcpy( video_avframe->data[0], q, video_avframe->linesize[0] ); + stride = width * height; + memcpy( video_avframe->data[0], q, video_avframe->linesize[0] * height ); q += stride; - memcpy( video_avframe->data[1], q, video_avframe->linesize[1] ); + memcpy( video_avframe->data[1], q, video_avframe->linesize[1] * height / 2 ); q += stride / 4; - memcpy( video_avframe->data[2], q, video_avframe->linesize[2] ); + memcpy( video_avframe->data[2], q, video_avframe->linesize[2] * height / 2 ); } else for ( i = 0; i < height; i ++ ) { @@ -1791,12 +1792,6 @@ // Do the colour space conversion int flags = SWS_BICUBIC; -#ifdef USE_MMX - flags |= SWS_CPU_CAPS_MMX; -#endif -#ifdef USE_SSE - flags |= SWS_CPU_CAPS_MMX2; -#endif struct SwsContext *context = sws_getContext( width, height, pick_pix_fmt( img_fmt ), width, height, c->pix_fmt, flags, NULL, NULL, NULL); sws_scale( context, (const uint8_t* const*) video_avframe->data, video_avframe->linesize, 0, height, @@ -1808,9 +1803,9 @@ // Apply the alpha if applicable if ( !mlt_properties_get( properties, "mlt_image_format" ) || strcmp( mlt_properties_get( properties, "mlt_image_format" ), "rgb24a" ) ) - if ( c->pix_fmt == PIX_FMT_RGBA || - c->pix_fmt == PIX_FMT_ARGB || - c->pix_fmt == PIX_FMT_BGRA ) + if ( c->pix_fmt == AV_PIX_FMT_RGBA || + c->pix_fmt == AV_PIX_FMT_ARGB || + c->pix_fmt == AV_PIX_FMT_BGRA ) { uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); register int n; @@ -1844,12 +1839,10 @@ av_init_packet(&pkt); // Set frame interlace hints - c->coded_frame->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" ); - c->coded_frame->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); if ( mlt_properties_get_int( frame_properties, "progressive" ) ) c->field_order = AV_FIELD_PROGRESSIVE; else - c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TT : AV_FIELD_BB; + c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TB : AV_FIELD_BT; pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = video_st->index; pkt.data = (uint8_t *)converted_avframe; diff -Nru mlt-0.9.8/src/modules/avformat/filter_avcolour_space.c mlt-6.0.0/src/modules/avformat/filter_avcolour_space.c --- mlt-0.9.8/src/modules/avformat/filter_avcolour_space.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/filter_avcolour_space.c 2016-02-17 23:43:24.000000000 +0000 @@ -47,17 +47,17 @@ switch( format ) { case mlt_image_rgb24: - value = PIX_FMT_RGB24; + value = AV_PIX_FMT_RGB24; break; case mlt_image_rgb24a: case mlt_image_opengl: - value = PIX_FMT_RGBA; + value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: - value = PIX_FMT_YUYV422; + value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: - value = PIX_FMT_YUV420P; + value = AV_PIX_FMT_YUV420P; break; default: mlt_log_error( NULL, "[filter avcolor_space] Invalid format %s\n", @@ -99,13 +99,13 @@ case 470: case 601: case 624: - src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + dst_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); break; case 240: - src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + dst_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); break; case 709: - src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + dst_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); break; default: break; @@ -123,16 +123,10 @@ int flags = SWS_BICUBIC | SWS_ACCURATE_RND; int error = -1; - if ( out_fmt == PIX_FMT_YUYV422 ) + if ( out_fmt == AV_PIX_FMT_YUYV422 ) flags |= SWS_FULL_CHR_H_INP; else flags |= SWS_FULL_CHR_H_INT; -#ifdef USE_MMX - flags |= SWS_CPU_CAPS_MMX; -#endif -#ifdef USE_SSE - flags |= SWS_CPU_CAPS_MMX2; -#endif avpicture_fill( &input, in, in_fmt, width, height ); avpicture_fill( &output, out, out_fmt, width, height ); @@ -141,7 +135,7 @@ if ( context ) { // 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 ) + if ( out_fmt == AV_PIX_FMT_RGB24 || out_fmt == AV_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, @@ -326,7 +320,7 @@ int *width = (int*) arg; if ( *width > 0 ) { - struct SwsContext *context = sws_getContext( *width, *width, PIX_FMT_RGB32, 64, 64, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); + struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else diff -Nru mlt-0.9.8/src/modules/avformat/filter_avdeinterlace.c mlt-6.0.0/src/modules/avformat/filter_avdeinterlace.c --- mlt-0.9.8/src/modules/avformat/filter_avdeinterlace.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/filter_avdeinterlace.c 2016-02-17 23:43:24.000000000 +0000 @@ -234,28 +234,28 @@ { int i; - if (pix_fmt != PIX_FMT_YUV420P && - pix_fmt != PIX_FMT_YUV422P && - pix_fmt != PIX_FMT_YUYV422 && - pix_fmt != PIX_FMT_YUV444P && - pix_fmt != PIX_FMT_YUV411P) + if (pix_fmt != AV_PIX_FMT_YUV420P && + pix_fmt != AV_PIX_FMT_YUV422P && + pix_fmt != AV_PIX_FMT_YUYV422 && + pix_fmt != AV_PIX_FMT_YUV444P && + pix_fmt != AV_PIX_FMT_YUV411P) return -1; if ((width & 3) != 0 || (height & 3) != 0) return -1; - if ( pix_fmt != PIX_FMT_YUYV422 ) + if ( pix_fmt != AV_PIX_FMT_YUYV422 ) { for(i=0;i<3;i++) { if (i == 1) { switch(pix_fmt) { - case PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV420P: width >>= 1; height >>= 1; break; - case PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV422P: width >>= 1; break; - case PIX_FMT_YUV411P: + case AV_PIX_FMT_YUV411P: width >>= 2; break; default: @@ -312,8 +312,8 @@ AVPicture *output = mlt_pool_alloc( sizeof( AVPicture ) ); // Fill the picture - avpicture_fill( output, *image, PIX_FMT_YUYV422, *width, *height ); - mlt_avpicture_deinterlace( output, output, PIX_FMT_YUYV422, *width, *height ); + avpicture_fill( output, *image, AV_PIX_FMT_YUYV422, *width, *height ); + mlt_avpicture_deinterlace( output, output, AV_PIX_FMT_YUYV422, *width, *height ); // Free the picture mlt_pool_release( output ); diff -Nru mlt-0.9.8/src/modules/avformat/filter_swscale.c mlt-6.0.0/src/modules/avformat/filter_swscale.c --- mlt-0.9.8/src/modules/avformat/filter_swscale.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/filter_swscale.c 2016-02-17 23:43:24.000000000 +0000 @@ -37,17 +37,17 @@ switch( format ) { case mlt_image_rgb24: - value = PIX_FMT_RGB24; + value = AV_PIX_FMT_RGB24; break; case mlt_image_rgb24a: case mlt_image_opengl: - value = PIX_FMT_RGBA; + value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: - value = PIX_FMT_YUYV422; + value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: - value = PIX_FMT_YUV420P; + value = AV_PIX_FMT_YUV420P; break; default: fprintf( stderr, "Invalid format...\n" ); @@ -108,12 +108,6 @@ // XXX: we only know how to rescale packed formats return 1; } -#ifdef USE_MMX - interp |= SWS_CPU_CAPS_MMX; -#endif -#ifdef USE_SSE - interp |= SWS_CPU_CAPS_MMX2; -#endif // Convert the pixel formats int avformat = convert_mlt_to_av_cs( *format ); @@ -148,7 +142,7 @@ uint8_t *alpha = mlt_frame_get_alpha( frame ); if ( alpha ) { - avformat = PIX_FMT_GRAY8; + avformat = AV_PIX_FMT_GRAY8; struct SwsContext *context = sws_getContext( iwidth, iheight, avformat, owidth, oheight, avformat, interp, NULL, NULL, NULL); avpicture_fill( &input, alpha, avformat, iwidth, iheight ); outbuf = mlt_pool_alloc( owidth * oheight ); @@ -182,7 +176,7 @@ int *width = (int*) arg; if ( *width > 0 ) { - struct SwsContext *context = sws_getContext( *width, *width, PIX_FMT_RGB32, 64, 64, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); + struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else diff -Nru mlt-0.9.8/src/modules/avformat/producer_avformat.c mlt-6.0.0/src/modules/avformat/producer_avformat.c --- mlt-0.9.8/src/modules/avformat/producer_avformat.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/producer_avformat.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_avformat.c -- avformat producer - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef VDPAU # include @@ -44,6 +45,7 @@ #include #include #include +#include #if LIBAVCODEC_VERSION_MAJOR < 55 #define AV_CODEC_ID_H264 CODEC_ID_H264 @@ -271,6 +273,61 @@ return i; } +#if (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(39<<8)+100)) +#include + +static double get_rotation(AVStream *st) +{ + AVDictionaryEntry *rotate_tag = av_dict_get( st->metadata, "rotate", NULL, 0 ); + uint8_t* displaymatrix = av_stream_get_side_data( st, AV_PKT_DATA_DISPLAYMATRIX, NULL); + double theta = 0; + + if ( rotate_tag && *rotate_tag->value && strcmp( rotate_tag->value, "0" ) ) + { + char *tail; + theta = strtod( rotate_tag->value, &tail ); + if ( *tail ) + theta = 0; + } + if ( displaymatrix && !theta ) + theta = -av_display_rotation_get( (int32_t*) displaymatrix ); + + theta -= 360 * floor( theta/360 + 0.9/360 ); + + return theta; +} +#endif + +static char* filter_restricted( const char *in ) +{ + if ( !in ) return NULL; + size_t n = strlen( in ); + char *out = calloc( 1, n + 1 ); + char *p = out; + mbstate_t mbs; + memset( &mbs, 0, sizeof(mbs) ); + while ( *in ) + { + wchar_t w; + size_t c = mbrtowc( &w, in, n, &mbs ); + if ( c <= 0 || c > n ) break; + n -= c; + in += c; + if ( w == 0x9 || w == 0xA || w == 0xD || + ( w >= 0x20 && w <= 0xD7FF ) || + ( w >= 0xE000 && w <= 0xFFFD ) || + ( w >= 0x10000 && w <= 0x10FFFF ) ) + { + mbstate_t ps; + memset( &ps, 0, sizeof(ps) ); + c = wcrtomb( p, w, &ps ); + if ( c > 0 ) + p += c; + } + } + return out; +} + /** Find the default streams. */ @@ -323,6 +380,10 @@ mlt_properties_set_int( meta_media, key, codec_context->width ); snprintf( key, sizeof(key), "meta.media.%d.codec.height", i ); mlt_properties_set_int( meta_media, key, codec_context->height ); +#if (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(39<<8)+100)) + snprintf( key, sizeof(key), "meta.media.%d.codec.rotate", i ); + mlt_properties_set_int( meta_media, key, get_rotation(context->streams[i]) ); +#endif snprintf( key, sizeof(key), "meta.media.%d.codec.frame_rate", i ); AVRational frame_rate = { codec_context->time_base.den, codec_context->time_base.num * codec_context->ticks_per_frame }; mlt_properties_set_double( meta_media, key, av_q2d( frame_rate ) ); @@ -393,7 +454,9 @@ if ( tag->value && strcmp( tag->value, "" ) && strcmp( tag->value, "und" ) ) { snprintf( key, sizeof(key), "meta.attr.%d.stream.%s.markup", i, tag->key ); - mlt_properties_set( meta_media, key, tag->value ); + char* value = filter_restricted( tag->value ); + mlt_properties_set( meta_media, key, value ); + free( value ); } } } @@ -402,7 +465,9 @@ if ( tag->value && strcmp( tag->value, "" ) && strcmp( tag->value, "und" ) ) { snprintf( key, sizeof(key), "meta.attr.%s.markup", tag->key ); - mlt_properties_set( meta_media, key, tag->value ); + char* value = filter_restricted( tag->value ); + mlt_properties_set( meta_media, key, value ); + free( value ); } } @@ -504,21 +569,21 @@ return result; } -static enum PixelFormat pick_pix_fmt( enum PixelFormat pix_fmt ) +static enum AVPixelFormat pick_pix_fmt( enum AVPixelFormat 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; + case AV_PIX_FMT_ARGB: + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_ABGR: + case AV_PIX_FMT_BGRA: + return AV_PIX_FMT_RGBA; #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) case AV_PIX_FMT_BAYER_RGGB16LE: - return PIX_FMT_RGB24; + return AV_PIX_FMT_RGB24; #endif default: - return PIX_FMT_YUV422P; + return AV_PIX_FMT_YUV422P; } } @@ -586,13 +651,17 @@ int pix_fmt = codec_context->pix_fmt; pick_av_pixel_format( &pix_fmt ); - // Verify that we can convert this to YUV 4:2:2 - struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, pix_fmt, - codec_context->width, codec_context->height, pick_pix_fmt( codec_context->pix_fmt ), SWS_BILINEAR, NULL, NULL, NULL); - if ( context ) - sws_freeContext( context ); - else - error = 1; + if ( pix_fmt != AV_PIX_FMT_NONE ) { + // Verify that we can convert this to one of our image formats. + struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, pix_fmt, + codec_context->width, codec_context->height, pick_pix_fmt( codec_context->pix_fmt ), SWS_BILINEAR, NULL, NULL, NULL); + if ( context ) + sws_freeContext( context ); + else + error = 1; + } else { + self->video_index = -1; + } } return error; } @@ -798,6 +867,7 @@ int toscan = 500; AVPacket pkt; + av_init_packet( &pkt ); while ( ret >= 0 && toscan-- > 0 ) { ret = av_read_frame( context, &pkt ); @@ -806,7 +876,17 @@ mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "first_pts %"PRId64" dts %"PRId64" pts_dts_delta %d\n", pkt.pts, pkt.dts, (int)(pkt.pts - pkt.dts) ); - self->first_pts = best_pts( self, pkt.pts, pkt.dts ); + if ( pkt.dts != AV_NOPTS_VALUE && pkt.dts < 0 ) + // Decoding Time Stamps with negative values are reported by ffmpeg code for + // (at least) MP4 files containing h.264 video using b-frames. + // For reasons not understood yet, the first PTS computed then is that of the + // third frame, causing MLT to display the third frame as if it was the first. + // This if-clause is meant to catch and work around this issue - if there is + // a valid, but negative DTS value, we just guess that the first valid + // Presentation Time Stamp is == 0. + self->first_pts = 0; + else + self->first_pts = best_pts( self, pkt.pts, pkt.dts ); if ( self->first_pts != AV_NOPTS_VALUE ) toscan = 0; } @@ -819,13 +899,15 @@ int64_t req_position, int preseek ) { mlt_producer producer = self->parent; + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); int paused = 0; + int seek_threshold = mlt_properties_get_int( properties, "seek_threshold" ); + if ( seek_threshold <= 0 ) seek_threshold = 12; pthread_mutex_lock( &self->packets_mutex ); if ( self->seekable && ( position != self->video_expected || self->last_position < 0 ) ) { - mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Fetch the video format context AVFormatContext *context = self->video_format; @@ -848,7 +930,7 @@ // We're paused - use last image paused = 1; } - else if ( self->seekable && ( position < self->video_expected || position - self->video_expected >= 12 || self->last_position < 0 ) ) + else if ( self->seekable && ( position < self->video_expected || position - self->video_expected >= seek_threshold || self->last_position < 0 ) ) { // Calculate the timestamp for the requested frame int64_t timestamp = req_position / ( av_q2d( self->video_time_base ) * source_fps ); @@ -961,13 +1043,13 @@ case 470: case 601: case 624: - src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + dst_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); break; case 240: - src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + dst_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); break; case 709: - src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + dst_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); break; default: break; @@ -976,26 +1058,26 @@ brightness, contrast, saturation ); } -static mlt_image_format pick_image_format( enum PixelFormat pix_fmt ) +static mlt_image_format pick_image_format( enum AVPixelFormat pix_fmt ) { switch ( pix_fmt ) { - case PIX_FMT_ARGB: - case PIX_FMT_RGBA: - case PIX_FMT_ABGR: - case PIX_FMT_BGRA: + case AV_PIX_FMT_ARGB: + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_ABGR: + case AV_PIX_FMT_BGRA: return mlt_image_rgb24a; - case PIX_FMT_YUV420P: - case PIX_FMT_YUVJ420P: - case PIX_FMT_YUVA420P: + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVA420P: return mlt_image_yuv420p; - case PIX_FMT_RGB24: - case PIX_FMT_BGR24: - case PIX_FMT_GRAY8: - case PIX_FMT_MONOWHITE: - case PIX_FMT_MONOBLACK: - case PIX_FMT_RGB8: - case PIX_FMT_BGR8: + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_MONOWHITE: + case AV_PIX_FMT_MONOBLACK: + case AV_PIX_FMT_RGB8: + case AV_PIX_FMT_BGR8: #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((2<<16)+(5<<8)+102)) case AV_PIX_FMT_BAYER_RGGB16LE: return mlt_image_rgb24; @@ -1071,21 +1153,14 @@ mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self->parent ) ); int result = self->yuv_colorspace; -#ifdef USE_MMX - flags |= SWS_CPU_CAPS_MMX; -#endif -#ifdef USE_SSE - 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 ( ( pix_fmt == AV_PIX_FMT_YUVA420P #if defined(FFUDIV) - || pix_fmt == PIX_FMT_YUVA444P + || pix_fmt == AV_PIX_FMT_YUVA444P #endif ) && *format != mlt_image_rgb24a && *format != mlt_image_opengl && @@ -1110,10 +1185,10 @@ // avformat with no filters and explicitly requested. #if defined(FFUDIV) && (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(48<<8)+100)) struct SwsContext *context = sws_getContext(width, height, src_pix_fmt, - width, height, PIX_FMT_YUV420P, flags, NULL, NULL, NULL); + width, height, AV_PIX_FMT_YUV420P, flags, NULL, NULL, NULL); #else struct SwsContext *context = sws_getContext( width, height, pix_fmt, - width, height, self->full_luma ? PIX_FMT_YUVJ420P : PIX_FMT_YUV420P, + width, height, self->full_luma ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P, flags, NULL, NULL, NULL); #endif @@ -1133,9 +1208,9 @@ else if ( *format == mlt_image_rgb24 ) { struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, - width, height, PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); + width, height, AV_PIX_FMT_RGB24, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; - avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); + avpicture_fill( &output, buffer, AV_PIX_FMT_RGB24, width, height ); // 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, 0 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, @@ -1145,9 +1220,9 @@ else if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, - width, height, PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); + width, height, AV_PIX_FMT_RGBA, flags | SWS_FULL_CHR_H_INT, NULL, NULL, NULL); AVPicture output; - avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); + avpicture_fill( &output, buffer, AV_PIX_FMT_RGBA, width, height ); // 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, 0 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, @@ -1158,13 +1233,13 @@ { #if defined(FFUDIV) && (LIBAVFORMAT_VERSION_INT >= ((55<<16)+(48<<8)+100)) struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, - width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); + width, height, AV_PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); #else struct SwsContext *context = sws_getContext( width, height, pix_fmt, - width, height, PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); + width, height, AV_PIX_FMT_YUYV422, flags | SWS_FULL_CHR_H_INP, NULL, NULL, NULL); #endif AVPicture output; - avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); + avpicture_fill( &output, buffer, AV_PIX_FMT_YUYV422, width, height ); if ( !set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma, 0 ) ) result = profile->colorspace; sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, @@ -1297,12 +1372,11 @@ double delay = mlt_properties_get_double( properties, "video_delay" ); // Seek if necessary - const char *interp = mlt_properties_get( frame_properties, "rescale.interp" ); - int preseek = must_decode + int preseek = must_decode && codec_context->has_b_frames; #if defined(FFUDIV) - && ( interp && strcmp( interp, "nearest" ) ) + const char *interp = mlt_properties_get( frame_properties, "rescale.interp" ); + preseek = preseek && interp && strcmp( interp, "nearest" ); #endif - && codec_context->has_b_frames; int paused = seek_video( self, position, req_position, preseek ); // Seek might have reopened the file @@ -1310,10 +1384,10 @@ stream = context->streams[ self->video_index ]; codec_context = stream->codec; 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 ) + codec_context->pix_fmt == AV_PIX_FMT_ARGB || + codec_context->pix_fmt == AV_PIX_FMT_RGBA || + codec_context->pix_fmt == AV_PIX_FMT_ABGR || + codec_context->pix_fmt == AV_PIX_FMT_BGRA ) *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 ) { @@ -1346,7 +1420,7 @@ picture.linesize[1] = codec_context->width / 2; picture.linesize[2] = codec_context->width / 2; yuv_colorspace = convert_image( self, (AVFrame*) &picture, *buffer, - PIX_FMT_YUV420P, format, *width, *height, &alpha ); + AV_PIX_FMT_YUV420P, format, *width, *height, &alpha ); } else #endif @@ -1443,7 +1517,7 @@ if ( llabs( req_position - int_position ) > 999 ) { int_position = req_position; - mlt_log_warning( MLT_PRODUCER_SERVICE(producer), " WILD TIMESTAMP!\n" ); + mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), " WILD TIMESTAMP!\n" ); } self->last_position = int_position; @@ -1539,7 +1613,7 @@ VdpStatus status = vdp_surface_get_bits( render->surface, dest_format, planes, pitches ); if ( status == VDP_STATUS_OK ) { - yuv_colorspace = convert_image( self, self->video_frame, *buffer, PIX_FMT_YUV420P, + yuv_colorspace = convert_image( self, self->video_frame, *buffer, AV_PIX_FMT_YUV420P, format, *width, *height, &alpha ); mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); } @@ -2505,7 +2579,7 @@ } // Update the audio properties if the index changed - if ( context && index > -1 && index != self->audio_index ) + if ( context && index > -1 && self->audio_index > -1 && index != self->audio_index ) { pthread_mutex_lock( &self->open_mutex ); if ( self->audio_codec[ self->audio_index ] ) @@ -2586,7 +2660,7 @@ producer_set_up_audio( self, *frame ); // Set the position of this producer - mlt_position position = self->seekable ? mlt_producer_frame( producer ) : self->nonseek_position++; + mlt_position position = mlt_producer_frame( producer ); mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "original_position", position ); // Calculate the next timecode diff -Nru mlt-0.9.8/src/modules/avformat/producer_avformat.yml mlt-6.0.0/src/modules/avformat/producer_avformat.yml --- mlt-0.9.8/src/modules/avformat/producer_avformat.yml 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/producer_avformat.yml 2016-02-17 23:43:24.000000000 +0000 @@ -228,3 +228,14 @@ minimum: 0 maximum: 1 widget: checkbox + + - identifier: seek_threshold + title: Seek Threshold + description: > + Number of frames required to trigger a seek forward rather than continuous read + when reading forward. This can be useful to optimize some applications which + rely on accelerated reading of a media file or in cases where lack of I-frames + cause libavformat to face issues in seeking and where user tries to minimize the + number of seek calls. + type: integer + unit: frames diff -Nru mlt-0.9.8/src/modules/avformat/vdpau.c mlt-6.0.0/src/modules/avformat/vdpau.c --- mlt-0.9.8/src/modules/avformat/vdpau.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/avformat/vdpau.c 2016-02-17 23:43:24.000000000 +0000 @@ -136,9 +136,9 @@ return success; } -static enum PixelFormat vdpau_get_format( struct AVCodecContext *s, const enum PixelFormat *fmt ) +static enum AVPixelFormat vdpau_get_format( struct AVCodecContext *s, const enum AVPixelFormat *fmt ) { - return PIX_FMT_VDPAU_H264; + return AV_PIX_FMT_VDPAU_H264; } static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame ) @@ -240,7 +240,7 @@ self->video_codec->release_buffer = vdpau_release_buffer; self->video_codec->draw_horiz_band = vdpau_draw_horiz; self->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; - self->video_codec->pix_fmt = PIX_FMT_VDPAU_H264; + self->video_codec->pix_fmt = AV_PIX_FMT_VDPAU_H264; VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH; uint32_t max_references = self->video_codec->refs; diff -Nru mlt-0.9.8/src/modules/core/factory.c mlt-6.0.0/src/modules/core/factory.c --- mlt-0.9.8/src/modules/core/factory.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/factory.c 2016-02-17 23:43:24.000000000 +0000 @@ -53,6 +53,7 @@ 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_timewarp_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_tone_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 ); @@ -105,6 +106,7 @@ 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, "timewarp", producer_timewarp_init ); MLT_REGISTER( producer_type, "tone", producer_tone_init ); MLT_REGISTER( transition_type, "composite", transition_composite_init ); MLT_REGISTER( transition_type, "luma", transition_luma_init ); @@ -142,6 +144,7 @@ MLT_REGISTER_METADATA( producer_type, "melt", metadata, "producer_melt.yml" ); MLT_REGISTER_METADATA( producer_type, "melt_file", metadata, "producer_melt_file.yml" ); MLT_REGISTER_METADATA( producer_type, "noise", metadata, "producer_noise.yml" ); + MLT_REGISTER_METADATA( producer_type, "timewarp", metadata, "producer_timewarp.yml" ); MLT_REGISTER_METADATA( producer_type, "tone", metadata, "producer_tone.yml" ); MLT_REGISTER_METADATA( transition_type, "composite", metadata, "transition_composite.yml" ); MLT_REGISTER_METADATA( transition_type, "luma", metadata, "transition_luma.yml" ); diff -Nru mlt-0.9.8/src/modules/core/filter_audioconvert.c mlt-6.0.0/src/modules/core/filter_audioconvert.c --- mlt-0.9.8/src/modules/core/filter_audioconvert.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/filter_audioconvert.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * filter_audioconvert.c -- convert from one audio format to another - * Copyright (C) 2009-2014 Meltytech, LLC + * Copyright (C) 2009-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,10 +24,6 @@ #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; diff -Nru mlt-0.9.8/src/modules/core/filter_brightness.c mlt-6.0.0/src/modules/core/filter_brightness.c --- mlt-0.9.8/src/modules/core/filter_brightness.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/filter_brightness.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * filter_brightness.c -- brightness, fade, and opacity filter - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,8 +24,6 @@ #include #include -#define CLAMP( x, min, max ) (x) < (min) ? (min) : (x) > (max) ? (max) : (x) - /** Do it :-). */ diff -Nru mlt-0.9.8/src/modules/core/filter_rescale.c mlt-6.0.0/src/modules/core/filter_rescale.c --- mlt-0.9.8/src/modules/core/filter_rescale.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/filter_rescale.c 2016-02-17 23:43:24.000000000 +0000 @@ -170,12 +170,17 @@ { int iwidth = *width; int iheight = *height; - double factor = mlt_properties_get_double( filter_properties, "factor" ); - factor = factor > 0 ? factor : 1.0; - int owidth = *width * factor; - int oheight = *height * factor; + int owidth = *width; + int oheight = *height; char *interps = mlt_properties_get( properties, "rescale.interp" ); + if ( mlt_properties_get( filter_properties, "factor" ) ) + { + double factor = mlt_properties_get_double( filter_properties, "factor" ); + owidth *= factor; + oheight *= factor; + } + // Default from the scaler if not specifed on the frame if ( interps == NULL ) { diff -Nru mlt-0.9.8/src/modules/core/Makefile mlt-6.0.0/src/modules/core/Makefile --- mlt-0.9.8/src/modules/core/Makefile 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/Makefile 2016-02-17 23:43:24.000000000 +0000 @@ -13,6 +13,7 @@ producer_loader.o \ producer_melt.o \ producer_noise.o \ + producer_timewarp.o \ producer_tone.o \ filter_audiochannels.o \ filter_audiomap.o \ @@ -64,19 +65,19 @@ all: $(TARGET) $(TARGET): $(OBJS) $(ASM_OBJS) - $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS) + $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS) composite_line_yuv_mmx.o: composite_line_yuv_mmx.S $(CC) -o $@ -c composite_line_yuv_mmx.S depend: $(SRCS) - $(CC) -MM $(CFLAGS) $^ 1>.depend + $(CC) -MM $(CFLAGS) $^ 1>.depend distclean: clean - rm -f .depend + rm -f .depend clean: - rm -f $(OBJS) $(ASM_OBJS) $(TARGET) + rm -f $(OBJS) $(ASM_OBJS) $(TARGET) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" diff -Nru mlt-0.9.8/src/modules/core/producer_timewarp.c mlt-6.0.0/src/modules/core/producer_timewarp.c --- mlt-0.9.8/src/modules/core/producer_timewarp.c 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/core/producer_timewarp.c 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,413 @@ +/* + * producer_timewarp.c -- modify speed and direction of a clip + * Copyright (C) 2015-2016 Meltytech, LLC + * 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 Types +typedef struct +{ + int first_frame; + double speed; + int reverse; + mlt_producer clip_producer; + mlt_profile clip_profile; + mlt_properties clip_parameters; +} private_data; + +// Private Functions + +static void timewarp_property_changed( mlt_service owner, mlt_producer producer, char *name ) +{ + private_data* pdata = (private_data*)producer->child; + if ( mlt_properties_get_int( pdata->clip_parameters, name ) || + !strcmp( name, "length" ) || + !strcmp( name, "in" ) || + !strcmp( name, "out" ) || + !strcmp( name, "ignore_points" ) || + !strcmp( name, "eof" ) || + !strncmp( name, "meta.", 5 ) ) + { + // Pass parameter changes from this producer to the encapsulated clip + // producer. + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); + mlt_events_block( clip_properties, producer ); + mlt_properties_pass_property( clip_properties, producer_properties, name ); + mlt_events_unblock( clip_properties, producer ); + } +} + +static void clip_property_changed( mlt_service owner, mlt_producer producer, char *name ) +{ + private_data* pdata = (private_data*)producer->child; + if ( mlt_properties_get_int( pdata->clip_parameters, name ) || + !strcmp( name, "length" ) || + !strcmp( name, "in" ) || + !strcmp( name, "out" ) || + !strcmp( name, "ignore_points" ) || + !strcmp( name, "eof" ) || + !strncmp( name, "meta.", 5 ) ) + { + // The encapsulated clip producer might change its own parameters. + // Pass those changes to this producer. + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); + mlt_events_block( producer_properties, producer ); + mlt_properties_pass_property( producer_properties, clip_properties, name ); + mlt_events_unblock( producer_properties, producer ); + } +} + +static int producer_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) +{ + mlt_producer producer = mlt_frame_pop_audio( frame ); + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + private_data* pdata = (private_data*)producer->child; + + int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); + + // Scale the frequency to account for the speed change. + // The resample normalizer will convert it to the requested frequency + *frequency = (double)*frequency * fabs(pdata->speed); + + if( pdata->speed < 0.0 ) + { + // Reverse the audio in this frame + int c = 0; + switch ( *format ) + { + // Interleaved 8bit formats + case mlt_audio_u8: + { + int8_t tmp; + for ( c = 0; c < *channels; c++ ) + { + // Pointer to first sample + int8_t* a = (int8_t*)*buffer + c; + // Pointer to last sample + int8_t* b = (int8_t*)*buffer + ((*samples - 1) * *channels) + c; + while( a < b ) + { + tmp = *a; + *a = *b; + *b = tmp; + a += *channels; + b -= *channels; + } + } + break; + } + // Interleaved 16bit formats + case mlt_audio_s16: + { + int16_t tmp; + for ( c = 0; c < *channels; c++ ) + { + // Pointer to first sample + int16_t *a = (int16_t*)*buffer + c; + // Pointer to last sample + int16_t *b = (int16_t*)*buffer + ((*samples - 1) * *channels) + c; + while( a < b ) + { + tmp = *a; + *a = *b; + *b = tmp; + a += *channels; + b -= *channels; + } + } + break; + } + // Interleaved 32bit formats + case mlt_audio_s32le: + case mlt_audio_f32le: + { + int32_t tmp; + for ( c = 0; c < *channels; c++ ) + { + // Pointer to first sample + int32_t *a = (int32_t*)*buffer + c; + // Pointer to last sample + int32_t *b = (int32_t*)*buffer + ((*samples - 1)* *channels) + c; + while( a < b ) + { + tmp = *a; + *a = *b; + *b = tmp; + a += *channels; + b -= *channels; + } + } + break; + } + // Non-Interleaved 32bit formats + case mlt_audio_s32: + case mlt_audio_float: + { + int32_t tmp; + for ( c = 0; c < *channels; c++ ) + { + // Pointer to first sample + int32_t *a = (int32_t*)*buffer + (c * *samples); + // Pointer to last sample + int32_t *b = (int32_t*)*buffer + ((c + 1) * *samples) - 1; + while( a < b ) + { + tmp = *a; + *a = *b; + *b = tmp; + a++; + b--; + } + } + break; + } + case mlt_audio_none: + break; + default: + mlt_log_error( MLT_PRODUCER_SERVICE(producer), + "Unknown Audio Format %s\n", + mlt_audio_format_name( *format ) ); + break; + } + } + + return error; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + private_data* pdata = (private_data*)producer->child; + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + if( pdata->first_frame && pdata->clip_producer ) + { + // Pass parameters from this producer to the clip producer + // Properties that are set after initialization are not always caught by + // the property_changed event - so do this once after init. + int n = mlt_properties_count( pdata->clip_parameters ); + int i = 0; + mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); + + mlt_events_block( clip_properties, producer ); + for ( i = 0; i < n; i++ ) + { + char* name = mlt_properties_get_name( pdata->clip_parameters, i ); + if( mlt_properties_get_int( clip_properties, name ) && + mlt_properties_get( producer_properties, name ) ) + { + mlt_properties_pass_property( clip_properties, producer_properties, name ); + } + } + mlt_events_unblock( clip_properties, producer ); + + pdata->first_frame = 0; + } + + if( pdata->clip_producer ) + { + // Seek the clip producer to the appropriate position + mlt_position clip_position = mlt_producer_position( producer ); + if( pdata->speed < 0.0 ) + { + clip_position = mlt_properties_get_int( producer_properties, "out" ) - clip_position; + } + mlt_producer_seek( pdata->clip_producer, clip_position ); + + // Get the frame from the clip producer + mlt_service_get_frame( MLT_PRODUCER_SERVICE( pdata->clip_producer), frame, index ); + + // Configure callbacks + if ( !mlt_frame_is_test_audio( *frame ) ) + { + mlt_frame_push_audio( *frame, producer ); + mlt_frame_push_audio( *frame, producer_get_audio ); + } + } + else + { + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); + } + + // Set the correct position on the frame + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + // Calculate the next time code + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer producer ) +{ + private_data* pdata = (private_data*)producer->child; + + if ( pdata ) + { + mlt_producer_close( pdata->clip_producer ); + mlt_profile_close( pdata->clip_profile ); + mlt_properties_close( pdata->clip_parameters ); + free( pdata ); + } + + producer->child = NULL; + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); +} + +mlt_producer producer_timewarp_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 ); + private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); + + if ( arg && producer && pdata ) + { + mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); + + // Initialize the producer + mlt_properties_set( producer_properties, "resource", arg ); + producer->child = pdata; + producer->get_frame = producer_get_frame; + producer->close = (mlt_destructor)producer_close; + + // Get the resource to be passed to the clip producer + char* resource = strchr( arg, ':' ); + if ( resource == NULL ) + resource = arg; // Apparently speed was not specified. + else + resource++; // move past the delimiter. + + // Initialize private data + pdata->first_frame = 1; + pdata->speed = atof( arg ); + if( pdata->speed == 0.0 ) + { + pdata->speed = 1.0; + } + pdata->clip_profile = NULL; + pdata->clip_parameters = NULL; + pdata->clip_producer = NULL; + + // Create a false profile to be used by the clip producer. + pdata->clip_profile = mlt_profile_clone( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ); + // Frame rate must be recalculated for the clip profile to change the time base. + if( pdata->clip_profile->frame_rate_num < 1000 ) + { + // Scale the frame rate fraction so we keep more accuracy when + // the speed is factored in. + pdata->clip_profile->frame_rate_num *= 1000; + pdata->clip_profile->frame_rate_den *= 1000; + } + pdata->clip_profile->frame_rate_num = (double)pdata->clip_profile->frame_rate_num / fabs(pdata->speed); + + // Create a producer for the clip using the false profile. + pdata->clip_producer = mlt_factory_producer( pdata->clip_profile, "abnormal", resource ); + + if( pdata->clip_producer ) + { + mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); + int n = 0; + int i = 0; + + // Set the speed to 0 since we will control the seeking + mlt_producer_set_speed( pdata->clip_producer, 0 ); + + // Create a list of all parameters used by the clip producer so that + // they can be passed between the clip producer and this producer. + pdata->clip_parameters = mlt_properties_new(); + mlt_properties_init( pdata->clip_parameters, NULL ); + mlt_repository repository = mlt_factory_repository(); + mlt_properties clip_metadata = mlt_repository_metadata( repository, producer_type, mlt_properties_get( clip_properties, "mlt_service" ) ); + if ( clip_metadata ) + { + mlt_properties params = (mlt_properties) mlt_properties_get_data( clip_metadata, "parameters", NULL ); + if ( params ) + { + n = mlt_properties_count( params ); + for ( i = 0; i < n; i++ ) + { + mlt_properties param = (mlt_properties) mlt_properties_get_data( params, mlt_properties_get_name( params, i ), NULL ); + char* identifier = mlt_properties_get( param, "identifier" ); + if ( identifier ) + { + // Set the value to 1 to indicate the parameter exists. + mlt_properties_set_int( pdata->clip_parameters, identifier, 1 ); + } + } + // Explicitly exclude the "resource" parameter since it needs to be different. + mlt_properties_set_int( pdata->clip_parameters, "resource", 0 ); + } + } + + // Pass parameters and properties from the clip producer to this producer. + // Some properties may have been set during initialization. + n = mlt_properties_count( clip_properties ); + for ( i = 0; i < n; i++ ) + { + char* name = mlt_properties_get_name( clip_properties, i ); + if ( mlt_properties_get_int( pdata->clip_parameters, name ) || + !strcmp( name, "length" ) || + !strcmp( name, "in" ) || + !strcmp( name, "out" ) || + !strncmp( name, "meta.", 5 ) ) + { + mlt_properties_pass_property( producer_properties, clip_properties, name ); + } + } + + // Initialize warp producer properties + mlt_properties_set_double( producer_properties, "warp_speed", pdata->speed ); + mlt_properties_set( producer_properties, "warp_resource", mlt_properties_get( clip_properties, "resource" ) ); + + // Monitor property changes from both producers so that the clip + // parameters can be passed back and forth. + mlt_events_listen( clip_properties, producer, "property-changed", ( mlt_listener )clip_property_changed ); + mlt_events_listen( producer_properties, producer, "property-changed", ( mlt_listener )timewarp_property_changed ); + } + } + + if ( !producer || !pdata || !pdata->clip_producer ) + { + if ( pdata ) + { + mlt_producer_close( pdata->clip_producer ); + mlt_profile_close( pdata->clip_profile ); + mlt_properties_close( pdata->clip_parameters ); + free( pdata ); + } + + if ( producer ) + { + producer->child = NULL; + producer->close = NULL; + mlt_producer_close( producer ); + free( producer ); + producer = NULL; + } + } + + return producer; +} diff -Nru mlt-0.9.8/src/modules/core/producer_timewarp.yml mlt-6.0.0/src/modules/core/producer_timewarp.yml --- mlt-0.9.8/src/modules/core/producer_timewarp.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/core/producer_timewarp.yml 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,66 @@ +schema_version: 0.3 +type: producer +identifier: timewarp +title: Time Warp +version: 1 +copyright: Meltytech, LLC +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Audio + - Video +description: > + Timewarp is a wrapper producer that allows temporal effects on an encapsulated + producer. The encapsulated producer can be modified to change the speed + (faster/slower) or direction (forward/reverse) + + In addition to the parameters listed below, this producer inherits all + parameters of the encapsulated producer. The encapsulated producer parameters + can be accessed directly (without any prefix) by getting and setting those + parameters on the timewarp producer. + + In addition to modifying the speed of the video, the audio is also slowed down + or sped up to match the video. +parameters: + - identifier: argument + title: Speed and Resource + type: string + description: | + The speed factor and the producer resource in the form: [speed:resource] + The speed can be any decimal number between 20 and 0.01. + Negative speed values cause the file to be played backwards. + The resource can be a file name or any producer service name. + The resource will be passed to the loader to create the encapsulated + producer. + + Examples: + File opened for 2x speed forward: + "2.0:example.mp4" + File opened for 2x speed reverse: + "-2.0:example.mp4" + File opened for 1x speed reverse: + "-1.0:example.mp4" + File opened for 0.25x speed (slow motion) forward: + "0.25:example.mp4" + + The most common use for this producer is to change the speed of a file. + However, any arbitrary producer can be specified. E.g.: + "2.0:colour:red" + readonly: no + required: yes + mutable: no + - identifier: warp_speed + title: Warp Speed + type: float + description: > + A convenience parameter to access the speed that was passed as part of + the argument. + readonly: yes + - identifier: warp_resource + title: Warp Producer + type: string + description: > + A convenience parameter to access the resource that was passed as part of + the argument + readonly: yes diff -Nru mlt-0.9.8/src/modules/core/transition_composite.c mlt-6.0.0/src/modules/core/transition_composite.c --- mlt-0.9.8/src/modules/core/transition_composite.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/transition_composite.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * transition_composite.c -- compose one image over another using alpha channel - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -375,7 +375,7 @@ for ( ; j < width; j ++ ) { - mix = calculate_mix( luma, j, soft, weight, (!alpha_b)?0x255:(*alpha_b ++), step ); + mix = calculate_mix( luma, j, soft, weight, alpha_b? *alpha_b : 255, step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); @@ -384,7 +384,8 @@ { *alpha_a = ( mix >> 8 ) | *alpha_a; alpha_a ++; - }; + } + if ( alpha_b ) alpha_b ++; } } @@ -395,12 +396,13 @@ for ( j = 0; j < width; j ++ ) { - mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ | *alpha_a, step ); + mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) | (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; - *alpha_a ++ = mix >> 8; + if (alpha_a) *alpha_a ++ = mix >> 8; + if (alpha_b) alpha_b++; } } @@ -411,12 +413,13 @@ for ( j = 0; j < width; j ++ ) { - mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ & *alpha_a, step ); + mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) & (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; - *alpha_a ++ = mix >> 8; + if (alpha_a) *alpha_a ++ = mix >> 8; + if (alpha_b) alpha_b++; } } @@ -427,12 +430,13 @@ for ( j = 0; j < width; j ++ ) { - mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ ^ *alpha_a, step ); + mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) ^ (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; - *alpha_a ++ = mix >> 8; + if (alpha_a) *alpha_a ++ = mix >> 8; + if (alpha_b) alpha_b++; } } @@ -651,6 +655,7 @@ if ( old_luma && old_luma[0] ) { mlt_properties_set_data( properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL ); + mlt_properties_set_data( properties, "_luma.bitmap", NULL, 0, NULL, NULL ); luma_bitmap = NULL; mlt_properties_set( properties, "_luma", NULL); } diff -Nru mlt-0.9.8/src/modules/core/transition_mix.c mlt-6.0.0/src/modules/core/transition_mix.c --- mlt-0.9.8/src/modules/core/transition_mix.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/transition_mix.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * transition_mix.c -- mix two audio streams - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,175 +19,221 @@ #include #include +#include #include #include #include #include +#define MAX_CHANNELS (6) +#define MAX_SAMPLES (192000/(24000/1001)) +#define PCM16_BYTES(samples, channels) ((samples) * (channels) * sizeof(int16_t)) +#define MAX_BYTES PCM16_BYTES( MAX_SAMPLES, MAX_CHANNELS ) -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 ) +typedef struct transition_mix_s { - int ret = 0; - int16_t *src, *dest; - int frequency_src = *frequency, frequency_dest = *frequency; - int channels_src = *channels, channels_dest = *channels; - int samples_src = *samples, samples_dest = *samples; - int i, j; - double d = 0, s = 0; - - mlt_frame_get_audio( that, (void**) &src, format, &frequency_src, &channels_src, &samples_src ); - mlt_frame_get_audio( frame, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); - - 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 ) ); + mlt_transition parent; + int16_t src_buffer[MAX_SAMPLES * MAX_CHANNELS]; + int16_t dest_buffer[MAX_SAMPLES * MAX_CHANNELS]; + int src_buffer_count; + int dest_buffer_count; +} *transition_mix; - silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 ); - if ( silent ) - memset( src, 0, samples_src * channels_src * sizeof( int16_t ) ); - - // determine number of samples to process - *samples = samples_src < samples_dest ? samples_src : samples_dest; - *channels = channels_src < channels_dest ? channels_src : channels_dest; - *buffer = dest; - *frequency = frequency_dest; +static void mix_audio( double weight_start, double weight_end, int16_t *buffer_a, + int16_t *buffer_b, int channels_a, int channels_b, int channels_out, int samples ) +{ + int i, j; + double a, b, v; // Compute a smooth ramp over start to end - float weight = weight_start; - float weight_step = ( weight_end - weight_start ) / *samples; + double mix = weight_start; + double mix_step = ( weight_end - weight_start ) / samples; - if ( src == dest ) + for ( i = 0; i < samples; i++ ) { - *samples = samples_src; - *channels = channels_src; - *buffer = src; - *frequency = frequency_src; - return ret; - } - - // Mixdown - for ( i = 0; i < *samples; i++ ) - { - for ( j = 0; j < *channels; j++ ) + for ( j = 0; j < channels_out; j++ ) { - if ( j < channels_dest ) - d = (double) dest[ i * channels_dest + j ]; - if ( j < channels_src ) - s = (double) src[ i * channels_src + j ]; - dest[ i * channels_dest + j ] = s * weight + d * ( 1.0 - weight ); + a = (double) buffer_a[ i * channels_a + j ]; + b = (double) buffer_b[ i * channels_b + j ]; + v = mix * b + (1.0 - mix) * a; + buffer_a[ i * channels_a + j ] = CLAMP( v, -32767.0, 32768.0 ); } - weight += weight_step; + mix += mix_step; } - - return ret; } -// 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 frame, mlt_frame that, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +// This filter uses an inline low pass filter to allow mixing without volume hacking. +static void combine_audio( double weight, int16_t *buffer_a, int16_t *buffer_b, + int channels_a, int channels_b, int channels_out, int samples ) { - int ret = 0; - int16_t *src, *dest; - int frequency_src = *frequency, frequency_dest = *frequency; - int channels_src = *channels, channels_dest = *channels; - int samples_src = *samples, samples_dest = *samples; int i, j; - double vp[ 6 ]; - double b_weight = 1.0; - - 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( frame, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest ); - - 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 ) ); - - silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 ); - if ( silent ) - memset( src, 0, samples_src * channels_src * sizeof( int16_t ) ); - - if ( src == dest ) - { - *samples = samples_src; - *channels = channels_src; - *buffer = src; - *frequency = frequency_src; - return ret; - } - - // determine number of samples to process - *samples = samples_src < samples_dest ? samples_src : samples_dest; - *channels = channels_src < channels_dest ? channels_src : channels_dest; - *buffer = dest; - *frequency = frequency_dest; - - for ( j = 0; j < *channels; j++ ) - vp[ j ] = ( double )dest[ j ]; - double Fc = 0.5; double B = exp(-2.0 * M_PI * Fc); double A = 1.0 - B; - double v; + double a, b, v; + double v_prev[MAX_CHANNELS]; + + for ( j = 0; j < channels_out; j++ ) + v_prev[j] = (double) buffer_a[j]; - for ( i = 0; i < *samples; i++ ) + for ( i = 0; i < samples; i++ ) { - for ( j = 0; j < *channels; j++ ) + for ( j = 0; j < channels_out; j++ ) { - v = ( double )( b_weight * dest[ i * channels_dest + j ] + src[ i * channels_src + j ] ); - v = v < -32767 ? -32767 : v > 32768 ? 32768 : v; - vp[ j ] = dest[ i * channels_dest + j ] = ( int16_t )( v * A + vp[ j ] * B ); + a = (double) buffer_a[ i * channels_a + j ]; + b = (double) buffer_b[ i * channels_b + j ]; + v = weight * a + b; + v = CLAMP( v, -32767.0, 32768.0 ); + v_prev[j] = buffer_a[ i * channels_a + j ] = (int16_t)( v * A + v_prev[j] * B ); } } - - return ret; } /** Get the audio. */ -static int transition_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +static int transition_get_audio( mlt_frame frame_a, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { + int error = 0; + // Get the b frame from the stack - mlt_frame b_frame = mlt_frame_pop_audio( frame ); + mlt_frame frame_b = mlt_frame_pop_audio( frame_a ); // Get the effect - mlt_transition effect = mlt_frame_pop_audio( frame ); + mlt_transition transition = mlt_frame_pop_audio( frame_a ); // Get the properties of the b frame - mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); + mlt_properties b_props = MLT_FRAME_PROPERTIES( frame_b ); + + transition_mix self = transition->child; + int16_t *buffer_b, *buffer_a; + int frequency_b = *frequency, frequency_a = *frequency; + int channels_b = *channels, channels_a = *channels; + int samples_b = *samples, samples_a = *samples; // We can only mix s16 *format = mlt_audio_s16; + mlt_frame_get_audio( frame_b, (void**) &buffer_b, format, &frequency_b, &channels_b, &samples_b ); + mlt_frame_get_audio( frame_a, (void**) &buffer_a, format, &frequency_a, &channels_a, &samples_a ); - if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( effect ), "combine" ) == 0 ) + if ( buffer_b == buffer_a ) + { + *samples = samples_b; + *channels = channels_b; + *buffer = buffer_b; + *frequency = frequency_b; + return error; + } + + int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame_a ), "silent_audio" ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame_a ), "silent_audio", 0 ); + if ( silent ) + memset( buffer_a, 0, samples_a * channels_a * sizeof( int16_t ) ); + + silent = mlt_properties_get_int( b_props, "silent_audio" ); + mlt_properties_set_int( b_props, "silent_audio", 0 ); + if ( silent ) + memset( buffer_b, 0, samples_b * channels_b * sizeof( int16_t ) ); + + // determine number of samples to process + *samples = MIN( self->src_buffer_count + samples_b, self->dest_buffer_count + samples_a ); + *channels = MIN( MIN( channels_b, channels_a ), MAX_CHANNELS ); + *frequency = frequency_a; + + // Prevent src buffer overflow by discarding oldest samples. + samples_b = MIN( samples_b, MAX_SAMPLES * MAX_CHANNELS / channels_b ); + size_t bytes = PCM16_BYTES( samples_b, channels_b ); + if ( PCM16_BYTES( self->src_buffer_count + samples_b, channels_b ) > MAX_BYTES ) { + mlt_log_verbose( MLT_TRANSITION_SERVICE(transition), "buffer overflow: src_buffer_count %d\n", + self->src_buffer_count ); + self->src_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_b - samples_b; + memmove( self->src_buffer, &self->src_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_b * channels_b], + PCM16_BYTES( samples_b, channels_b ) ); + } + // Buffer new src samples. + memcpy( &self->src_buffer[self->src_buffer_count * channels_b], buffer_b, bytes ); + self->src_buffer_count += samples_b; + buffer_b = self->src_buffer; + + // Prevent dest buffer overflow by discarding oldest samples. + samples_a = MIN( samples_a, MAX_SAMPLES * MAX_CHANNELS / channels_a ); + bytes = PCM16_BYTES( samples_a, channels_a ); + if ( PCM16_BYTES( self->dest_buffer_count + samples_a, channels_a ) > MAX_BYTES ) { + mlt_log_verbose( MLT_TRANSITION_SERVICE(transition), "buffer overflow: dest_buffer_count %d\n", + self->dest_buffer_count ); + self->dest_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_a - samples_a; + memmove( self->dest_buffer, &self->dest_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_a * channels_a], + PCM16_BYTES( samples_a, channels_a ) ); + } + // Buffer the new dest samples. + memcpy( &self->dest_buffer[self->dest_buffer_count * channels_a], buffer_a, bytes ); + self->dest_buffer_count += samples_a; + buffer_a = self->dest_buffer; + + // Do the mixing. + if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES(transition), "combine" ) ) + { + double weight = 1.0; + if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame_a ), "meta.mixdown" ) ) + weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame_a ), "meta.volume" ); + combine_audio( weight, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples ); + } + else { double mix_start = 0.5, mix_end = 0.5; - if ( mlt_properties_get( b_props, "audio.previous_mix" ) != NULL ) + if ( mlt_properties_get( b_props, "audio.previous_mix" ) ) mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" ); - if ( mlt_properties_get( b_props, "audio.mix" ) != NULL ) + if ( mlt_properties_get( b_props, "audio.mix" ) ) mix_end = mlt_properties_get_double( b_props, "audio.mix" ); if ( mlt_properties_get_int( b_props, "audio.reverse" ) ) { - mix_start = 1 - mix_start; - mix_end = 1 - mix_end; + mix_start = 1.0 - mix_start; + mix_end = 1.0 - mix_end; } + mix_audio( mix_start, mix_end, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples ); + } + + // Copy the audio into the frame. + bytes = PCM16_BYTES( *samples, *channels ); + *buffer = mlt_pool_alloc( bytes ); + memcpy( *buffer, buffer_a, bytes ); + mlt_frame_set_audio( frame_a, *buffer, *format, bytes, mlt_pool_release ); - mix_audio( frame, b_frame, mix_start, mix_end, buffer, format, frequency, channels, samples ); + if ( mlt_properties_get_int( b_props, "_speed" ) == 0 ) + { + // Flush the buffer when paused and scrubbing. + samples_b = self->src_buffer_count; + samples_a = self->dest_buffer_count; } else { - combine_audio( frame, b_frame, buffer, format, frequency, channels, samples ); + // Determine the maximum amount of latency permitted in the buffer. + int max_latency = CLAMP( *frequency / 1000, 0, MAX_SAMPLES ); // samples in 1ms + // samples_b becomes the new target src buffer count. + samples_b = CLAMP( self->src_buffer_count - *samples, 0, max_latency ); + // samples_b becomes the number of samples to consume: difference between actual and the target. + samples_b = self->src_buffer_count - samples_b; + // samples_a becomes the new target dest buffer count. + samples_a = CLAMP( self->dest_buffer_count - *samples, 0, max_latency ); + // samples_a becomes the number of samples to consume: difference between actual and the target. + samples_a = self->dest_buffer_count - samples_a; } - return 0; + // Consume the src buffer. + self->src_buffer_count -= samples_b; + if ( self->src_buffer_count ) { + memmove( self->src_buffer, &self->src_buffer[samples_b * channels_b], + PCM16_BYTES( self->src_buffer_count, channels_b )); + } + // Consume the dest buffer. + self->dest_buffer_count -= samples_a; + if ( self->dest_buffer_count ) { + memmove( self->dest_buffer, &self->dest_buffer[samples_a * channels_a], + PCM16_BYTES( self->dest_buffer_count, channels_a )); + } + + return error; } @@ -200,7 +246,7 @@ mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); // Only if mix is specified, otherwise a producer may set the mix - if ( mlt_properties_get( properties, "start" ) != NULL ) + if ( mlt_properties_get( properties, "start" ) ) { // Determine the time position of this frame in the transition duration mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL ); @@ -216,7 +262,7 @@ if ( length == 0 ) { // If there is an end mix level adjust mix to the range - if ( mlt_properties_get( properties, "end" ) != NULL ) + if ( mlt_properties_get( properties, "end" ) ) { double start = mlt_properties_get_double( properties, "start" ); double end = mlt_properties_get_double( properties, "end" ); @@ -236,7 +282,7 @@ mlt_position last_position = mlt_properties_get_position( properties, "_last_position" ); mlt_position current_position = mlt_frame_get_position( b_frame ); mlt_properties_set_position( properties, "_last_position", current_position ); - if ( mlt_properties_get( properties, "_previous_mix" ) == NULL + if ( !mlt_properties_get( properties, "_previous_mix" ) || current_position != last_position + 1 ) mlt_properties_set_double( properties, "_previous_mix", mix ); @@ -280,19 +326,34 @@ return a_frame; } +static void transition_close( mlt_transition transition ) +{ + free( transition->child ); + transition->close = NULL; + mlt_transition_close( transition ); +} + /** Constructor for the transition. */ mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { + transition_mix mix = calloc( 1 , sizeof( struct transition_mix_s ) ); mlt_transition transition = calloc( 1, sizeof( struct mlt_transition_s ) ); - if ( transition != NULL && mlt_transition_init( transition, NULL ) == 0 ) + if ( mix && transition && !mlt_transition_init( transition, mix ) ) { + mix->parent = transition; + transition->close = transition_close; transition->process = transition_process; - if ( arg != NULL ) + if ( 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( transition ), "_transition_type", 2 ); + } else { + if ( transition ) + mlt_transition_close( transition ); + if ( mix ) + free( mix ); } return transition; } diff -Nru mlt-0.9.8/src/modules/core/transition_mix.yml mlt-6.0.0/src/modules/core/transition_mix.yml --- mlt-0.9.8/src/modules/core/transition_mix.yml 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/core/transition_mix.yml 2016-02-17 23:43:24.000000000 +0000 @@ -1,4 +1,4 @@ -schema_version: 0.1 +schema_version: 0.2 type: transition identifier: mix title: Mix @@ -15,11 +15,13 @@ parameters: - identifier: start title: Start + argument: yes type: float mutable: yes description: > The mix level to apply to the second frame. Any negative value causes an automatic crossfade from 0 to 1. + - identifier: end title: End type: float @@ -27,13 +29,21 @@ description: > The ending value of the mix level. Mix level will be interpolated from start to end over the in-out range. + - identifier: reverse title: Reverse - type: integer + type: boolean mutable: yes description: > Set to 1 to reverse the direction of the mix. default: 0 - minimum: 0 - maximum: 1 widget: checkbox + + - identifier: combine + title: Use better mixing algorithm + description: > + Mix using a low pass filter to prevent affecting audio levels. + This is incompatible with start < 0. + type: boolean + default: 0 + mutable: yes diff -Nru mlt-0.9.8/src/modules/decklink/common.cpp mlt-6.0.0/src/modules/decklink/common.cpp --- mlt-0.9.8/src/modules/decklink/common.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/decklink/common.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -21,7 +21,7 @@ #include #include -#ifdef __DARWIN__ +#ifdef __APPLE__ char* getCString( DLString aDLString ) { @@ -40,7 +40,7 @@ if ( aDLString ) CFRelease( aDLString ); } -#elif defined(WIN32) +#elif defined(_WIN32) char* getCString( DLString aDLString ) { diff -Nru mlt-0.9.8/src/modules/decklink/common.h mlt-6.0.0/src/modules/decklink/common.h --- mlt-0.9.8/src/modules/decklink/common.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/decklink/common.h 2016-02-17 23:43:24.000000000 +0000 @@ -20,13 +20,13 @@ #ifndef DECKLINK_COMMON_H #define DECKLINK_COMMON_H -#ifdef WIN32 +#ifdef _WIN32 # include # include "DeckLinkAPI_h.h" typedef BSTR DLString; #else # include "DeckLinkAPI.h" -# ifdef __DARWIN__ +# ifdef __APPLE__ typedef CFStringRef DLString; # else typedef const char* DLString; diff -Nru mlt-0.9.8/src/modules/decklink/consumer_decklink.cpp mlt-6.0.0/src/modules/decklink/consumer_decklink.cpp --- mlt-0.9.8/src/modules/decklink/consumer_decklink.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/decklink/consumer_decklink.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* - * consumer_decklink.c -- output through Blackmagic Design DeckLink - * Copyright (C) 2010 Dan Dennedy + * consumer_decklink.cpp -- output through Blackmagic Design DeckLink + * Copyright (C) 2010-2015 Dan Dennedy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -45,7 +45,6 @@ double m_fps; uint64_t m_count; int m_channels; - unsigned m_dropped; IDeckLinkMutableVideoFrame* m_decklinkFrame; bool m_isAudio; int m_isKeyer; @@ -111,7 +110,7 @@ bool open( unsigned card = 0 ) { unsigned i = 0; -#ifdef WIN32 +#ifdef _WIN32 IDeckLinkIterator* deckLinkIterator = NULL; HRESULT result = CoInitialize( NULL ); if ( FAILED( result ) ) @@ -162,7 +161,7 @@ IDeckLinkAttributes *deckLinkAttributes = 0; if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK ) { -#ifdef WIN32 +#ifdef _WIN32 BOOL flag = FALSE; #else bool flag = false; @@ -214,7 +213,6 @@ // Initialize members m_count = 0; - m_dropped = 0; m_decklinkFrame = NULL; preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll; m_channels = mlt_properties_get_int( properties, "channels" ); @@ -315,7 +313,7 @@ if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) ) { -#ifdef WIN32 +#ifdef _WIN32 #define DECKLINK_UNSIGNED_FORMAT "%lu" unsigned long written = 0; #else @@ -329,7 +327,7 @@ mlt_log_verbose( getConsumer(), "renderAudio: will flush " DECKLINK_UNSIGNED_FORMAT " audiosamples\n", written ); m_deckLinkOutput->FlushBufferedAudioSamples(); }; -#ifdef WIN32 +#ifdef _WIN32 m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written ); #else m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written ); @@ -477,9 +475,6 @@ 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 ); } HRESULT render( mlt_frame frame ) @@ -549,7 +544,7 @@ m_reprio = true; }; -#ifdef WIN32 +#ifdef _WIN32 unsigned long cnt; #else uint32_t cnt; @@ -686,7 +681,7 @@ else return; -#ifdef WIN32 +#ifdef _WIN32 if ( FAILED( CoInitialize( NULL ) ) ) return; if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) ) diff -Nru mlt-0.9.8/src/modules/decklink/producer_decklink.cpp mlt-6.0.0/src/modules/decklink/producer_decklink.cpp --- mlt-0.9.8/src/modules/decklink/producer_decklink.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/decklink/producer_decklink.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -114,7 +114,7 @@ IDeckLinkIterator* decklinkIterator = NULL; try { -#ifdef WIN32 +#ifdef _WIN32 HRESULT result = CoInitialize( NULL ); if ( FAILED( result ) ) throw "COM initialization failed"; @@ -191,7 +191,7 @@ } // Determine if supports input format detection -#ifdef WIN32 +#ifdef _WIN32 BOOL doesDetectFormat = FALSE; #else bool doesDetectFormat = false; @@ -697,7 +697,7 @@ else return; -#ifdef WIN32 +#ifdef _WIN32 if ( FAILED( CoInitialize( NULL ) ) ) return; if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) ) diff -Nru mlt-0.9.8/src/modules/frei0r/factory.c mlt-6.0.0/src/modules/frei0r/factory.c --- mlt-0.9.8/src/modules/frei0r/factory.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/frei0r/factory.c 2016-02-17 23:43:24.000000000 +0000 @@ -32,10 +32,10 @@ #include -#if defined(WIN32) +#if defined(_WIN32) #define LIBSUF ".dll" #define FREI0R_PLUGIN_PATH "\\lib\\frei0r-1" -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) #define LIBSUF ".so" #define FREI0R_PLUGIN_PATH "/lib/frei0r-1" #else @@ -45,8 +45,6 @@ #define GET_FREI0R_PATH (getenv("FREI0R_PATH") ? getenv("FREI0R_PATH") : getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH) -#define CLAMP( x, min, max ) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) - extern mlt_filter filter_frei0r_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_frame filter_process( mlt_filter this, mlt_frame frame ); extern void filter_close( mlt_filter this ); @@ -57,12 +55,12 @@ static char* get_frei0r_path() { -#ifdef WIN32 +#ifdef _WIN32 char *dirname = malloc( strlen( mlt_environment( "MLT_APPDIR" ) ) + strlen( FREI0R_PLUGIN_PATH ) + 1 ); strcpy( dirname, mlt_environment( "MLT_APPDIR" ) ); strcat( dirname, FREI0R_PLUGIN_PATH ); return dirname; -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) char *dirname = malloc( strlen( mlt_environment( "MLT_APPDIR" ) ) + strlen( FREI0R_PLUGIN_PATH ) + 1 ); strcpy( dirname, mlt_environment( "MLT_APPDIR" ) ); strcat( dirname, FREI0R_PLUGIN_PATH ); @@ -363,7 +361,7 @@ char soname[PATH_MAX]; char *myid = strdup( id ); -#ifdef WIN32 +#ifdef _WIN32 char *firstname = strtok( myid, "." ); #else char *save_firstptr = NULL; @@ -371,7 +369,7 @@ #endif char* directory = mlt_tokeniser_get_string (tokeniser, dircount); -#ifdef WIN32 +#ifdef _WIN32 firstname = strtok( NULL, "." ); #else firstname = strtok_r( NULL, ".", &save_firstptr ); @@ -433,7 +431,7 @@ for (i=0; i #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #else #include @@ -36,7 +36,7 @@ // This is a nasty little hack which is required by SDL if ( widget != NULL ) { -#ifdef WIN32 +#ifdef _WIN32 HWND xwin = GDK_WINDOW_HWND( widget->window ); #else Window xwin = GDK_WINDOW_XWINDOW( widget->window ); diff -Nru mlt-0.9.8/src/modules/gtk2/producer_pango.c mlt-6.0.0/src/modules/gtk2/producer_pango.c --- mlt-0.9.8/src/modules/gtk2/producer_pango.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/gtk2/producer_pango.c 2016-02-17 23:43:24.000000000 +0000 @@ -81,6 +81,9 @@ int style; int weight; int rotate; + int width_crop; + int width_fit; + double aspect_ratio; }; static void clean_cached( producer_pango self ) @@ -99,7 +102,9 @@ static void producer_close( mlt_producer parent ); static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg ); static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font, - rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char* family, int style, int weight, int size, int outline, int rotate ); + rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char* family, + int style, int weight, int size, int outline, int rotate, int width_crop, int width_fit, + double aspect_ratio ); static void fill_pixbuf( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg ); static void fill_pixbuf_with_outline( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg, rgba_color ol, int outline ); @@ -398,6 +403,9 @@ int weight = mlt_properties_get_int( producer_props, "weight" ); int rotate = mlt_properties_get_int( producer_props, "rotate" ); int size = mlt_properties_get_int( producer_props, "size" ); + int width_crop = mlt_properties_get_int( producer_props, "width_crop" ); + int width_fit = mlt_properties_get_int( producer_props, "width_fit" ); + double aspect_ratio = mlt_properties_get_double( properties, "aspect_ratio" ); int property_changed = 0; if ( pixbuf == NULL ) @@ -413,7 +421,7 @@ sprintf( temp, "%d", item.frame ); markup = mlt_properties_get( contents, temp ); } - + // See if any properties changed property_changed = ( align != this->align ); property_changed = property_changed || ( this->fgcolor == NULL || ( fg && strcmp( fg, this->fgcolor ) ) ); @@ -429,6 +437,9 @@ property_changed = property_changed || ( rotate != this->rotate ); property_changed = property_changed || ( style != this->style ); property_changed = property_changed || ( size != this->size ); + property_changed = property_changed || ( width_crop != this->width_crop ); + property_changed = property_changed || ( width_fit != this->width_fit ); + property_changed = property_changed || ( aspect_ratio != this->aspect_ratio ); // Save the properties for next comparison this->align = align; @@ -445,6 +456,9 @@ this->rotate = rotate; this->style = style; this->size = size; + this->width_crop = width_crop; + this->width_fit = width_fit; + this->aspect_ratio = aspect_ratio; } if ( pixbuf == NULL && property_changed ) @@ -474,7 +488,9 @@ } // Render the title - pixbuf = pango_get_pixbuf( markup, text, font, fgcolor, bgcolor, olcolor, pad, align, family, style, weight, size, outline, rotate ); + pixbuf = pango_get_pixbuf( markup, text, font, fgcolor, bgcolor, olcolor, pad, align, family, + style, weight, size, outline, rotate, width_crop, width_fit, + aspect_ratio ); if ( pixbuf != NULL ) { @@ -680,18 +696,21 @@ // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - // Refresh the pango image - pthread_mutex_lock( &pango_mutex ); - refresh_image( *frame, 0, 0 ); - pthread_mutex_unlock( &pango_mutex ); - // Set producer-specific frame properties mlt_properties_set_int( properties, "progressive", 1 ); 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", 1.0); + { + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); + mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); + } + + // Refresh the pango image + pthread_mutex_lock( &pango_mutex ); + refresh_image( *frame, 0, 0 ); + pthread_mutex_unlock( &pango_mutex ); // Stack the get image callback mlt_frame_push_service( *frame, this ); @@ -740,7 +759,7 @@ } } -static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char* family, int style, int weight, int size, int outline, int rotate ) +static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, rgba_color ol, int pad, int align, char* family, int style, int weight, int size, int outline, int rotate, int width_crop, int width_fit, double aspect_ratio ) { PangoContext *context = pango_ft2_font_map_create_context( fontmap ); PangoLayout *layout = pango_layout_new( context ); @@ -819,6 +838,73 @@ else pango_layout_get_pixel_size( layout, &w, &h ); + // respect aspect ratio + if ( 0.0 < aspect_ratio && aspect_ratio != 1.0) + { + double n_x, n_y; + PangoRectangle rect; + PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; +#if 1 + pango_matrix_scale( &m_layout, 1.0 / aspect_ratio, 1.0 ); + pango_matrix_scale( &m_offset, 1.0 / aspect_ratio, 1.0 ); +#else + pango_matrix_scale( &m_layout, 1.0, aspect_ratio ); + pango_matrix_scale( &m_offset, 1.0, aspect_ratio ); +#endif + pango_context_set_base_gravity( context, PANGO_GRAVITY_AUTO ); + pango_context_set_matrix( context, &m_layout ); + pango_layout_context_changed( layout ); + pango_layout_get_extents( layout, NULL, &rect ); + pango_matrix_transform_rectangle( pango_context_get_matrix( context ), &rect); + + n_x = -rect.x; + n_y = -rect.y; + pango_matrix_transform_point( &m_offset, &n_x, &n_y ); + rect.x = n_x; + rect.y = n_y; + + pango_extents_to_pixels( &rect, NULL ); + + w = rect.width; + h = rect.height; + + x = rect.x; + y = rect.y; + } + + // limit width + if ( width_crop && w > width_crop) + w = width_crop; + else if (width_fit && w > width_fit) + { + double n_x, n_y; + PangoRectangle rect; + PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; + + pango_matrix_scale( &m_layout, width_fit / (double)w, 1.0 ); + pango_matrix_scale( &m_offset, width_fit / (double)w, 1.0 ); + + pango_context_set_base_gravity( context, PANGO_GRAVITY_AUTO ); + pango_context_set_matrix( context, &m_layout ); + pango_layout_context_changed( layout ); + pango_layout_get_extents( layout, NULL, &rect ); + pango_matrix_transform_rectangle( pango_context_get_matrix( context ), &rect); + + n_x = -rect.x; + n_y = -rect.y; + pango_matrix_transform_point( &m_offset, &n_x, &n_y ); + rect.x = n_x; + rect.y = n_y; + + pango_extents_to_pixels( &rect, NULL ); + + w = rect.width; + h = rect.height; + + x = rect.x; + y = rect.y; + } + if ( pad == 0 ) pad = 1; diff -Nru mlt-0.9.8/src/modules/gtk2/producer_pango.yml mlt-6.0.0/src/modules/gtk2/producer_pango.yml --- mlt-0.9.8/src/modules/gtk2/producer_pango.yml 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/gtk2/producer_pango.yml 2016-02-17 23:43:24.000000000 +0000 @@ -33,7 +33,7 @@ type: string description: | A text file containing Pango markup, see: - http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html + https://developer.gnome.org/pango/stable/PangoMarkupFormat.html requires xml-like encoding special chars from: <, >, & -to- <, >, & readonly: no @@ -232,4 +232,24 @@ default: 0 readonly: no mutable: yes + widget: spinner + + - identifier: width_crop + title: Width to crop + type: integer + description: > + Limit width of rendered image. + default: 0 + readonly: no + mutable: yes + widget: spinner + + - identifier: width_fit + title: Fit width + type: integer + description: > + Scale pango layout to fit specified width. + default: 0 + readonly: no + mutable: yes widget: spinner diff -Nru mlt-0.9.8/src/modules/gtk2/producer_pixbuf.c mlt-6.0.0/src/modules/gtk2/producer_pixbuf.c --- mlt-0.9.8/src/modules/gtk2/producer_pixbuf.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/gtk2/producer_pixbuf.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -78,7 +78,25 @@ // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); - + + // Reject if animation. + GError *error = NULL; + pthread_mutex_lock( &g_mutex ); + GdkPixbufAnimation *anim = gdk_pixbuf_animation_new_from_file( filename, &error ); + if ( anim ) + { + gboolean is_anim = !gdk_pixbuf_animation_is_static_image( anim ); + g_object_unref( anim ); + if ( is_anim ) + { + pthread_mutex_unlock( &g_mutex ); + mlt_producer_close( &self->parent ); + free( self ); + return NULL; + } + } + pthread_mutex_unlock( &g_mutex ); + // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; diff -Nru mlt-0.9.8/src/modules/jackrack/consumer_jack.c mlt-6.0.0/src/modules/jackrack/consumer_jack.c --- mlt-0.9.8/src/modules/jackrack/consumer_jack.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/jackrack/consumer_jack.c 2016-02-17 23:43:24.000000000 +0000 @@ -184,7 +184,7 @@ pthread_mutex_unlock( &self->refresh_mutex ); // Cleanup the main thread -#ifndef WIN32 +#ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); diff -Nru mlt-0.9.8/src/modules/jackrack/jack_rack.c mlt-6.0.0/src/modules/jackrack/jack_rack.c --- mlt-0.9.8/src/modules/jackrack/jack_rack.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/jackrack/jack_rack.c 2016-02-17 23:43:24.000000000 +0000 @@ -170,7 +170,7 @@ xmlChar *content; unsigned long num; unsigned long control = 0; -#ifdef WIN32 +#ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif @@ -267,7 +267,7 @@ xmlNodePtr node; xmlChar *content; saved_plugin_t * saved_plugin; -#ifdef WIN32 +#ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif diff -Nru mlt-0.9.8/src/modules/jackrack/plugin.c mlt-6.0.0/src/modules/jackrack/plugin.c --- mlt-0.9.8/src/modules/jackrack/plugin.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/jackrack/plugin.c 2016-02-17 23:43:24.000000000 +0000 @@ -313,7 +313,7 @@ return 1; } -#ifdef __DARWIN__ +#ifdef __APPLE__ if (!get_descriptor (desc->index)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); diff -Nru mlt-0.9.8/src/modules/jackrack/plugin_mgr.c mlt-6.0.0/src/modules/jackrack/plugin_mgr.c --- mlt-0.9.8/src/modules/jackrack/plugin_mgr.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/jackrack/plugin_mgr.c 2016-02-17 23:43:24.000000000 +0000 @@ -102,7 +102,7 @@ return; } -#ifdef __DARWIN__ +#ifdef __APPLE__ if (!get_descriptor (0)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); @@ -218,14 +218,14 @@ char * ladspa_path, * dir; ladspa_path = g_strdup (getenv ("LADSPA_PATH")); -#ifdef WIN32 +#ifdef _WIN32 if (!ladspa_path) { ladspa_path = malloc (strlen (mlt_environment("MLT_APPDIR")) + strlen ("\\lib\\ladspa") + 1); strcpy (ladspa_path, mlt_environment("MLT_APPDIR")); strcat (ladspa_path, "\\lib\\ladspa"); } -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) { ladspa_path = malloc( strlen (mlt_environment ("MLT_APPDIR")) + strlen ("/lib/ladspa") + 1 ); strcpy (ladspa_path, mlt_environment ("MLT_APPDIR")); diff -Nru mlt-0.9.8/src/modules/motion_est/arrow_code.c mlt-6.0.0/src/modules/motion_est/arrow_code.c --- mlt-0.9.8/src/modules/motion_est/arrow_code.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/motion_est/arrow_code.c 2016-02-17 23:43:24.000000000 +0000 @@ -25,8 +25,6 @@ #include #include -#define MIN(a,b) ((a) > (b) ? (b) : (a)) - #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) #define ABS(a) ((a) >= 0 ? (a) : (-(a))) diff -Nru mlt-0.9.8/src/modules/motion_est/filter_autotrack_rectangle.c mlt-6.0.0/src/modules/motion_est/filter_autotrack_rectangle.c --- mlt-0.9.8/src/modules/motion_est/filter_autotrack_rectangle.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/motion_est/filter_autotrack_rectangle.c 2016-02-17 23:43:24.000000000 +0000 @@ -30,8 +30,6 @@ #include #include -#define MIN(a,b) ((a) > (b) ? (b) : (a)) - #define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) #define ABS(a) ((a) >= 0 ? (a) : (-(a))) @@ -254,6 +252,11 @@ mlt_geometry geom = mlt_geometry_init(); char *arg = mlt_properties_get(filter_properties, "geometry"); + // Parse the geometry if we have one + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); + mlt_geometry_parse( geom, arg, length, profile->width, profile->height ); + // Initialize with the supplied geometry struct mlt_geometry_item_s item; mlt_geometry_parse_item( geom, &item, arg ); diff -Nru mlt-0.9.8/src/modules/motion_est/filter_motion_est.c mlt-6.0.0/src/modules/motion_est/filter_motion_est.c --- mlt-0.9.8/src/modules/motion_est/filter_motion_est.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/motion_est/filter_motion_est.c 2016-02-17 23:43:24.000000000 +0000 @@ -49,7 +49,6 @@ #define DIAMOND_SEARCH 0x0 #define FULL_SEARCH 0x1 #define SHIFT 8 -#define MIN(a,b) ((a) > (b) ? (b) : (a)) #define ABS(a) ((a) >= 0 ? (a) : (-(a))) diff -Nru mlt-0.9.8/src/modules/normalize/filter_audiolevel.c mlt-6.0.0/src/modules/normalize/filter_audiolevel.c --- mlt-0.9.8/src/modules/normalize/filter_audiolevel.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/normalize/filter_audiolevel.c 2016-02-17 23:43:24.000000000 +0000 @@ -70,12 +70,12 @@ for ( c = 0; c < *channels; c++ ) { - long val = 0; + double val = 0; double level = 0.0; for ( s = 0; s < num_samples; s++ ) { - int sample = abs( pcm[c + s * num_channels] / 128 ); + double sample = fabs( pcm[c + s * num_channels] / 128.0 ); val += sample; if ( sample == 128 ) num_oversample++; diff -Nru mlt-0.9.8/src/modules/oldfilm/filter_grain.c mlt-6.0.0/src/modules/oldfilm/filter_grain.c --- mlt-0.9.8/src/modules/oldfilm/filter_grain.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/oldfilm/filter_grain.c 2016-02-17 23:43:24.000000000 +0000 @@ -23,8 +23,6 @@ #include #include -#define MIN(a,b) (ab?a:b) static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { diff -Nru mlt-0.9.8/src/modules/oldfilm/filter_tcolor.c mlt-6.0.0/src/modules/oldfilm/filter_tcolor.c --- mlt-0.9.8/src/modules/oldfilm/filter_tcolor.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/oldfilm/filter_tcolor.c 2016-02-17 23:43:24.000000000 +0000 @@ -24,8 +24,6 @@ #include #include -#define MIN(a,b) (a #include -#define MIN(a,b) (a } -#if defined(__DARWIN__) +#if defined(__APPLE__) #include -#elif defined(WIN32) +#elif defined(_WIN32) #include #include #else @@ -219,9 +219,9 @@ void GlslManager::onInit( mlt_properties owner, GlslManager* filter ) { mlt_log_debug( filter->get_service(), "%s\n", __FUNCTION__ ); -#ifdef WIN32 +#ifdef _WIN32 std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit"); -#elif defined(__DARWIN__) && defined(RELOCATABLE) +#elif defined(__APPLE__) && defined(RELOCATABLE) std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/share/movit"); #else std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR); diff -Nru mlt-0.9.8/src/modules/opengl/filter_movit_convert.cpp mlt-6.0.0/src/modules/opengl/filter_movit_convert.cpp --- mlt-0.9.8/src/modules/opengl/filter_movit_convert.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/opengl/filter_movit_convert.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * filter_movit_convert.cpp - * Copyright (C) 2013 Dan Dennedy + * Copyright (C) 2013-2015 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 @@ -473,15 +473,15 @@ input->useFlatInput( FORMAT_RGB, width, height ); } else if ( format == mlt_image_yuv420p ) { - ImageFormat image_format; - YCbCrFormat ycbcr_format; + 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; + 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; @@ -683,7 +683,7 @@ { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); -#ifdef WIN32 +#ifdef _WIN32 // XXX avcolor_space is crashing on Windows in this context! mlt_filter cpu_csc = NULL; #else diff -Nru mlt-0.9.8/src/modules/plus/filter_dynamictext.c mlt-6.0.0/src/modules/plus/filter_dynamictext.c --- mlt-0.9.8/src/modules/plus/filter_dynamictext.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plus/filter_dynamictext.c 2016-02-17 23:43:24.000000000 +0000 @@ -239,6 +239,7 @@ 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, "style", mlt_properties_get( my_properties, "style" ) ); 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" ) ); @@ -252,11 +253,13 @@ mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); + mlt_service_lock( MLT_TRANSITION_SERVICE(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 ); + mlt_service_unlock( MLT_TRANSITION_SERVICE(transition) ); } @@ -281,7 +284,6 @@ 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 ); @@ -290,6 +292,10 @@ // Get the b frame and process with transition if successful if ( !error && mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &b_frame, 0 ) == 0 ) { + // This lock needs to also protect the producer properties from being + // modified in setup_producer() while also being used in mlt_service_get_frame(). + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + // Create a temporary frame so the original stays in tact. a_frame = mlt_frame_clone( frame, 0 ); @@ -316,6 +322,10 @@ mlt_frame_close( a_frame ); mlt_frame_close( b_frame ); } + else + { + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + } return error; } @@ -373,6 +383,7 @@ 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, "style", "normal" ); mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); mlt_properties_set( my_properties, "olcolour", "0x00000000" ); diff -Nru mlt-0.9.8/src/modules/plus/filter_dynamictext.yml mlt-6.0.0/src/modules/plus/filter_dynamictext.yml --- mlt-0.9.8/src/modules/plus/filter_dynamictext.yml 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plus/filter_dynamictext.yml 2016-02-17 23:43:24.000000000 +0000 @@ -68,6 +68,19 @@ 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 diff -Nru mlt-0.9.8/src/modules/plus/transition_affine.c mlt-6.0.0/src/modules/plus/transition_affine.c --- mlt-0.9.8/src/modules/plus/transition_affine.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plus/transition_affine.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * transition_affine.c -- affine transformations - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -275,9 +275,6 @@ return affine[2][0] * x + affine[2][1] * y + affine[2][2]; } -#define MAX( x, y ) x > y ? x : y -#define MIN( x, y ) x < y ? x : y - static void affine_max_output( float affine[3][3], float *w, float *h, float dz, float max_width, float max_height ) { int tlx = MapX( affine, -max_width, max_height ) / dz; @@ -575,7 +572,7 @@ { dx = MapX( affine.matrix, x, y ) / dz + x_offset; dy = MapY( affine.matrix, x, y ) / dz + y_offset; - if ( dx >= 0 && dx < (b_width - 1) && dy >=0 && dy < (b_height - 1) ) + if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height ) interp( b_image, b_width, b_height, dx, dy, result.mix/100.0, p, b_alpha ); p += 4; } diff -Nru mlt-0.9.8/src/modules/plusgpl/consumer_cbrts.c mlt-6.0.0/src/modules/plusgpl/consumer_cbrts.c --- mlt-0.9.8/src/modules/plusgpl/consumer_cbrts.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/consumer_cbrts.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,7 +1,7 @@ /* * consumer_cbrts.c -- output constant bitrate MPEG-2 transport stream * - * Copyright (C) 2010-2014 Broadcasting Center Europe S.A. http://www.bce.lu + * Copyright (C) 2010-2015 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 @@ -30,12 +30,22 @@ #include #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #else #include #endif #include +// includes for socket IO +#if (_POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE) && (_POSIX_TIMERS > 0) +#define CBRTS_BSD_SOCKETS 1 +#include +#include +#include +#include +#endif +#include +#include #define TSP_BYTES (188) #define MAX_PID (8192) @@ -45,6 +55,15 @@ #define SDT_PID (0x11) #define PCR_SMOOTHING (12) #define PCR_PERIOD_MS (20) +#define RTP_BYTES (12) +#define UDP_MTU (RTP_BYTES + TSP_BYTES * 7) +#define REMUX_BUFFER_MIN (10) +#define REMUX_BUFFER_MAX (50) +#define UDP_BUFFER_MINIMUM (100) +#define UDP_BUFFER_DEFAULT (1000) +#define RTP_VERSION (2) +#define RTP_PAYLOAD (33) +#define RTP_HZ (90000) #define PIDOF( packet ) ( ntohs( *( ( uint16_t* )( packet + 1 ) ) ) & 0x1fff ) #define HASPCR( packet ) ( (packet[3] & 0x20) && (packet[4] != 0) && (packet[5] & 0x10) ) @@ -65,16 +84,12 @@ int fd; uint8_t *leftover_data[TSP_BYTES]; int leftover_size; - mlt_deque packets; - mlt_deque packets2; + mlt_deque tsp_packets; 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; + int thread_running; uint8_t pcr_count; uint16_t pmt_pid; int is_si_sdt; @@ -83,6 +98,26 @@ int dropped; uint8_t continuity_count[MAX_PID]; uint64_t output_counter; +#ifdef CBRTS_BSD_SOCKETS + struct addrinfo *addr; + struct timespec timer; + uint32_t nsec_per_packet; + uint32_t femto_per_packet; + uint64_t femto_counter; +#endif + int ( *write_tsp )( consumer_cbrts, const void *buf, size_t count ); + uint8_t udp_packet[UDP_MTU]; + size_t udp_bytes; + size_t udp_packet_size; + mlt_deque udp_packets; + pthread_t output_thread; + pthread_mutex_t udp_deque_mutex; + pthread_cond_t udp_deque_cond; + uint64_t muxrate; + int udp_buffer_max; + uint16_t rtp_sequence; + uint32_t rtp_ssrc; + uint32_t rtp_counter; }; typedef struct { @@ -123,8 +158,8 @@ parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; self->joined = 1; - self->packets = mlt_deque_init(); - self->packets2 = mlt_deque_init(); + self->tsp_packets = mlt_deque_init(); + self->udp_packets = mlt_deque_init(); // Create the null packet memset( null_packet, 0xFF, TSP_BYTES ); @@ -134,8 +169,8 @@ null_packet[3] = 0x10; // Create the deque mutex and condition - pthread_mutex_init( &self->deque_mutex, NULL ); - pthread_cond_init( &self->deque_cond, NULL ); + pthread_mutex_init( &self->udp_deque_mutex, NULL ); + pthread_cond_init( &self->udp_deque_cond, NULL ); // Set consumer property defaults mlt_properties_set_int( properties, "real_time", -1 ); @@ -251,8 +286,7 @@ 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 ); + section->period = ( self->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 ); @@ -297,7 +331,7 @@ if ( len > 0 ) memset( p, 0xff, len ); - mlt_deque_push_back( self->packets2, packet ); + mlt_deque_push_back( self->tsp_packets, packet ); self->packet_count++; data_ptr += len; @@ -355,6 +389,11 @@ return self->previous_pcr + packets * TSP_BYTES * 8 * SCR_HZ / muxrate; } +static uint32_t get_rtp_timestamp( consumer_cbrts self ) +{ + return self->rtp_counter++ * self->udp_packet_size * 8 * RTP_HZ / self->muxrate; +} + static double measure_bitrate( consumer_cbrts self, uint64_t pcr, int drop ) { double muxrate = 0; @@ -372,22 +411,282 @@ return muxrate; } -static int writen( int fd, const void *buf, size_t count ) +static int writen( consumer_cbrts self, const void *buf, size_t count ) { int result = 0; int written = 0; while ( written < count ) { - if ( ( result = write( fd, buf + written, count - written ) ) < 0 ) + if ( ( result = write( self->fd, buf + written, count - written ) ) < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to write: %s\n", strerror( errno ) ); + break; + } + written += result; + } + return result; +} + +static int sendn( consumer_cbrts self, const void *buf, size_t count ) +{ + int result = 0; + +#ifdef CBRTS_BSD_SOCKETS + int written = 0; + while ( written < count ) + { + result = sendto(self->fd, buf + written, count - written, 0, + self->addr->ai_addr, self->addr->ai_addrlen); + if ( result < 0 ) { - mlt_log_error( NULL, "Failed to write: %s\n", strerror( errno ) ); + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to send: %s\n", strerror( errno ) ); + exit( EXIT_FAILURE ); break; } written += result; } +#endif + + return result; +} + +static int write_udp( consumer_cbrts self, const void *buf, size_t count ) +{ + int result = 0; + +#ifdef CBRTS_BSD_SOCKETS + if ( !self->timer.tv_sec ) + clock_gettime( CLOCK_MONOTONIC, &self->timer ); + self->femto_counter += self->femto_per_packet; + self->timer.tv_nsec += self->femto_counter / 1000000; + self->femto_counter = self->femto_counter % 1000000; + self->timer.tv_nsec += self->nsec_per_packet; + self->timer.tv_sec += self->timer.tv_nsec / 1000000000; + self->timer.tv_nsec = self->timer.tv_nsec % 1000000000; + clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &self->timer, NULL ); + result = sendn( self, buf, count ); +#endif + + return result; +} + +// socket IO code +static int create_socket( consumer_cbrts self ) +{ + int result = -1; + +#ifdef CBRTS_BSD_SOCKETS + struct addrinfo hints = {0}; + mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); + const char *hostname = mlt_properties_get( properties, "udp.address" ); + const char *port = "1234"; + + if ( mlt_properties_get( properties, "udp.port" ) ) + port = mlt_properties_get( properties, "udp.port" ); + + // Resolve the address string and port. + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + result = getaddrinfo( hostname, port, &hints, &self->addr ); + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error resolving UDP address and port: %s.\n", gai_strerror( result ) ); + return result; + } + + // Create the socket descriptor. + struct addrinfo *addr = self->addr; + for ( ; addr; addr = addr->ai_next ) { + result = socket( addr->ai_addr->sa_family, SOCK_DGRAM, addr->ai_protocol ); + if ( result != -1 ) + { + // success + self->fd = result; + result = 0; + break; + } + } + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error creating socket: %s.\n", strerror( errno ) ); + freeaddrinfo( self->addr ); self->addr = NULL; + return result; + } + + // Set the reuse address socket option if not disabled (explicitly set 0). + int reuse = mlt_properties_get_int( properties, "udp.reuse" ) || + !mlt_properties_get( properties, "udp.reuse" ); + if ( reuse ) + { + result = setsockopt( self->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) ); + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error setting the reuse address socket option.\n" ); + close( self->fd ); + freeaddrinfo( self->addr ); self->addr = NULL; + return result; + } + } + + // Set the socket buffer size if supplied. + if ( mlt_properties_get( properties, "udp.sockbufsize" ) ) + { + int sockbufsize = mlt_properties_get_int( properties, "udp.sockbufsize" ); + result = setsockopt( self->fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof(sockbufsize) ); + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error setting the socket buffer size.\n" ); + close( self->fd ); + freeaddrinfo( self->addr ); self->addr = NULL; + return result; + } + } + + // Set the multicast TTL if supplied. + if ( mlt_properties_get( properties, "udp.ttl" ) ) + { + int ttl = mlt_properties_get_int( properties, "udp.ttl" ); + if ( addr->ai_addr->sa_family == AF_INET ) + { + result = setsockopt( self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl) ); + } + else if ( addr->ai_addr->sa_family == AF_INET6 ) + { + result = setsockopt( self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl) ); + } + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error setting the multicast TTL.\n" ); + close( self->fd ); + freeaddrinfo( self->addr ); self->addr = NULL; + return result; + } + } + + // Set the multicast interface if supplied. + if ( mlt_properties_get( properties, "udp.interface" ) ) + { + const char *interface = mlt_properties_get( properties, "udp.interface" ); + unsigned int iface = if_nametoindex( interface ); + + if ( iface ) + { + if ( addr->ai_addr->sa_family == AF_INET ) + { + struct ip_mreqn req = {{0}}; + req.imr_ifindex = iface; + result = setsockopt( self->fd, IPPROTO_IP, IP_MULTICAST_IF, &req, sizeof(req) ); + } + else if ( addr->ai_addr->sa_family == AF_INET6 ) + { + result = setsockopt( self->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface) ); + } + if ( result < 0 ) + { + mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), + "Error setting the multicast interface.\n" ); + close( self->fd ); + freeaddrinfo( self->addr ); self->addr = NULL; + return result; + } + } + else + { + mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), + "The network interface \"%s\" was not found.\n", interface ); + } + } +#endif return result; } +static void *output_thread( void *arg ) +{ + consumer_cbrts self = arg; + int result = 0; + + while ( self->thread_running ) + { + pthread_mutex_lock( &self->udp_deque_mutex ); + while ( self->thread_running && mlt_deque_count( self->udp_packets ) < 1 ) + pthread_cond_wait( &self->udp_deque_cond, &self->udp_deque_mutex ); + pthread_mutex_unlock( &self->udp_deque_mutex ); + + // Dequeue the UDP packets and write them. + int i = mlt_deque_count( self->udp_packets ); + mlt_log_debug( MLT_CONSUMER_SERVICE(&self->parent), "%s: count %d\n", __FUNCTION__, i ); + while ( self->thread_running && i-- && result >= 0 ) + { + pthread_mutex_lock( &self->udp_deque_mutex ); + uint8_t *packet = mlt_deque_pop_front( self->udp_packets ); + pthread_cond_broadcast( &self->udp_deque_cond ); + pthread_mutex_unlock( &self->udp_deque_mutex ); + + size_t size = self->rtp_ssrc ? RTP_BYTES + self->udp_packet_size : self->udp_packet_size; + result = write_udp( self, packet, size ); + free( packet ); + } + } + return NULL; +} + +static int enqueue_udp( consumer_cbrts self, const void *buf, size_t count ) +{ + // Append TSP to the UDP packet. + memcpy( &self->udp_packet[self->udp_bytes], buf, count ); + self->udp_bytes = ( self->udp_bytes + count ) % self->udp_packet_size; + + // Send the UDP packet. + if ( !self->udp_bytes ) + { + size_t offset = self->rtp_ssrc ? RTP_BYTES : 0; + + // Duplicate the packet. + uint8_t *packet = malloc( self->udp_packet_size + offset ); + memcpy( packet + offset, self->udp_packet, self->udp_packet_size ); + + // Add the RTP header. + if ( self->rtp_ssrc ) { + // Padding, extension, and CSRC count are all 0. + packet[0] = RTP_VERSION << 6; + // Marker bit is 0. + packet[1] = RTP_PAYLOAD & 0x7f; + packet[2] = (self->rtp_sequence >> 8) & 0xff; + packet[3] = (self->rtp_sequence >> 0) & 0xff; + // Timestamp in next 4 bytes. + uint32_t timestamp = get_rtp_timestamp( self ); + packet[4] = (timestamp >> 24) & 0xff; + packet[5] = (timestamp >> 16) & 0xff; + packet[6] = (timestamp >> 8) & 0xff; + packet[7] = (timestamp >> 0) & 0xff; + // SSRC in next 4 bytes. + packet[8] = (self->rtp_ssrc >> 24) & 0xff; + packet[9] = (self->rtp_ssrc >> 16) & 0xff; + packet[10] = (self->rtp_ssrc >> 8) & 0xff; + packet[11] = (self->rtp_ssrc >> 0) & 0xff; + self->rtp_sequence++; + } + + // Wait for room in the fifo. + pthread_mutex_lock( &self->udp_deque_mutex ); + while ( self->thread_running && mlt_deque_count( self->udp_packets ) >= self->udp_buffer_max ) + pthread_cond_wait( &self->udp_deque_cond, &self->udp_deque_mutex ); + + // Add the packet to the fifo. + mlt_deque_push_back( self->udp_packets, packet ); + pthread_cond_broadcast( &self->udp_deque_cond ); + pthread_mutex_unlock( &self->udp_deque_mutex ); + } + + return 0; +} + static int insert_pcr( consumer_cbrts self, uint16_t pid, uint8_t cc, uint64_t pcr ) { uint8_t packet[ TSP_BYTES ]; @@ -404,12 +703,12 @@ p += 6; // 6 pcr bytes memset( p, 0xff, TSP_BYTES - ( p - packet ) ); // stuffing - return writen( self->fd, packet, TSP_BYTES ); + return self->write_tsp( self, 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 ); + int n = mlt_deque_count( self->tsp_packets ); unsigned output_packets = 0; unsigned packets_since_pcr = 0; int result = 0; @@ -423,9 +722,9 @@ 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 ) + while ( self->thread_running && n-- && result >= 0 ) { - uint8_t *packet = mlt_deque_pop_front( self->packets2 ); + uint8_t *packet = mlt_deque_pop_front( self->tsp_packets ); uint16_t pid = PIDOF( packet ); // Check for overflow @@ -463,7 +762,7 @@ if ( pcr_pid && pid == pcr_pid ) cc = CCOF( packet ); - result = writen( self->fd, packet, TSP_BYTES ); + result = self->write_tsp( self, packet, TSP_BYTES ); free( packet ); if ( result < 0 ) break; @@ -488,12 +787,12 @@ } // Output null packets as needed - while ( input_counter + ( TSP_BYTES * 8 * input_rate ) <= self->output_counter ) + while ( self->thread_running && 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 ); @@ -506,7 +805,7 @@ else { // Otherwise output a null packet - if ( ( result = writen( self->fd, null_packet, TSP_BYTES ) ) < 0 ) + if ( ( result = self->write_tsp( self, null_packet, TSP_BYTES ) ) < 0 ) break; packets_since_pcr++; } @@ -554,88 +853,55 @@ return; } -static void *remux_thread( void *arg ) +static int remux_packet( consumer_cbrts self, uint8_t *packet ) { - 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; + uint16_t pid = PIDOF( packet ); 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 ); + write_sections( self ); - // 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); - } + // Sanity checks + if ( packet[0] != 0x47 ) + { + mlt_log_panic( service, "NOT SYNC BYTE 0x%02x\n", packet[0] ); + exit(1); + } + if ( pid == NULL_PID ) + { + mlt_log_panic( service, "NULL PACKET\n" ); + exit(1); + } - // Measure the bitrate between consecutive PCRs - if ( HASPCR( packet ) ) + // 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 ) { - if ( self->pcr_count++ % PCR_SMOOTHING == 0 ) + self->is_stuffing_set = 1; + if ( input_rate > 1.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; + result = output_cbr( self, input_rate, self->muxrate, &pcr ); + set_pcr( packet, pcr ); } } - 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++; + self->previous_pcr = pcr; + self->previous_packet_count = self->packet_count; } } - return NULL; + mlt_deque_push_back( self->tsp_packets, packet ); + self->packet_count++; + return result; } -static void start_remux_thread( consumer_cbrts self ) +static void start_output_thread( consumer_cbrts self ) { - self->is_remuxing = 1; - int rtprio = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "rtprio" ); + int rtprio = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "udp.rtprio" ); + self->thread_running = 1; if ( rtprio > 0 ) { // Use realtime priority class @@ -647,42 +913,39 @@ 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 ) + if ( pthread_create( &self->output_thread, &thread_attributes, output_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 ); + "failed to initialize output thread with realtime priority\n" ); + pthread_create( &self->output_thread, &thread_attributes, output_thread, self ); } pthread_attr_destroy( &thread_attributes ); } else { // Use normal priority class - pthread_create( &self->remux_thread, NULL, remux_thread, self ); + pthread_create( &self->output_thread, NULL, output_thread, self ); } } -static void stop_remux_thread( consumer_cbrts self ) +static void stop_output_thread( consumer_cbrts self ) { - if ( self->is_remuxing ) - { - self->is_remuxing = 0; + self->thread_running = 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 ); + // Broadcast to the condition in case it's waiting. + pthread_mutex_lock( &self->udp_deque_mutex ); + pthread_cond_broadcast( &self->udp_deque_cond ); + pthread_mutex_unlock( &self->udp_deque_mutex ); - // Join the thread - pthread_join( self->remux_thread, NULL ); + // Join the thread. + pthread_join( self->output_thread, NULL ); - n = mlt_deque_count( self->packets2 ); - while ( n-- ) - free( mlt_deque_pop_back( self->packets2 ) ); - } + // Release the buffered packets. + pthread_mutex_lock( &self->udp_deque_mutex ); + int n = mlt_deque_count( self->udp_packets ); + while ( n-- ) + free( mlt_deque_pop_back( self->udp_packets ) ); + pthread_mutex_unlock( &self->udp_deque_mutex ); } static inline int filter_packet( consumer_cbrts self, uint8_t *packet ) @@ -702,18 +965,31 @@ if ( self->is_si_pmt ) result = 1; } - + return result; } +static void filter_remux_or_write_packet( consumer_cbrts self, uint8_t *packet ) +{ + int remux = !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "noremux" ); + + // Filter out packets + if ( remux ) { + if ( !filter_packet( self, packet ) ) + remux_packet( self, packet ); + else + free( packet ); + } else { + self->write_tsp( self, packet, TSP_BYTES ); + free( packet ); + } +} + 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 ) @@ -734,9 +1010,8 @@ uint8_t *packet = NULL; int i; -// mlt_log_verbose( service, "%s: packets %d remaining %i\n", __FUNCTION__, num_packets, self->leftover_size ); +// mlt_log_verbose( MLT_CONSUMER_SERVICE(consumer), "%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 ); @@ -744,36 +1019,24 @@ 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 ); + filter_remux_or_write_packet( self, 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 ); + filter_remux_or_write_packet( self, 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 ); + if ( !self->thread_running ) + start_output_thread( self ); + mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: %p 0x%x (%d)\n", __FUNCTION__, buf, *buf, size % TSP_BYTES ); // Do direct output -// result = writen( self->fd, buf, size ); +// result = self->write_tsp( self, buf, size ); } } @@ -802,6 +1065,38 @@ mlt_properties_set( avformat, "f", "mpegts" ); self->dropped = 0; self->fd = STDOUT_FILENO; + self->write_tsp = writen; + self->muxrate = mlt_properties_get_int64( MLT_CONSUMER_PROPERTIES(&self->parent), "muxrate" ); + + if ( mlt_properties_get( properties, "udp.address" ) ) + { + if ( create_socket( self ) >= 0 ) + { + int is_rtp = 1; + if ( mlt_properties_get( properties, "udp.rtp" ) ) + is_rtp = !!mlt_properties_get_int( properties, "udp.rtp" ); + if ( is_rtp ) { + self->rtp_ssrc = mlt_properties_get_int( properties, "udp.rtp_ssrc" ); + while ( !self->rtp_ssrc ) + self->rtp_ssrc = (uint32_t) rand(); + self->rtp_counter = (uint32_t) rand(); + } + + self->udp_packet_size = mlt_properties_get_int( properties, "udp.nb_tsp" ) * TSP_BYTES; + if ( self->udp_packet_size <= 0 || self->udp_packet_size > UDP_MTU ) + self->udp_packet_size = 7 * TSP_BYTES; +#ifdef CBRTS_BSD_SOCKETS + self->nsec_per_packet = 1000000000UL * self->udp_packet_size * 8 / self->muxrate; + self->femto_per_packet = 1000000000000000ULL * self->udp_packet_size * 8 / self->muxrate - self->nsec_per_packet * 1000000; +#endif + self->udp_buffer_max = mlt_properties_get_int( properties, "udp.buffer" ); + if ( self->udp_buffer_max < UDP_BUFFER_MINIMUM ) + self->udp_buffer_max = UDP_BUFFER_DEFAULT; + + self->write_tsp = enqueue_udp; + } + } + // Load the DVB PSI/SI sections load_sections( self, properties ); @@ -832,7 +1127,7 @@ // Kill the threads and clean up self->running = 0; -#ifndef WIN32 +#ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); @@ -840,7 +1135,7 @@ if ( self->avformat ) mlt_consumer_stop( self->avformat ); - stop_remux_thread( self ); + stop_output_thread( self ); if ( self->fd > 1 ) close( self->fd ); @@ -863,9 +1158,6 @@ // 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; @@ -903,7 +1195,6 @@ } 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 ) @@ -935,8 +1226,8 @@ mlt_consumer_close( self->avformat ); // Now clean up the rest - mlt_deque_close( self->packets ); - mlt_deque_close( self->packets2 ); + mlt_deque_close( self->tsp_packets ); + mlt_deque_close( self->udp_packets ); mlt_consumer_close( parent ); // Finally clean up this diff -Nru mlt-0.9.8/src/modules/plusgpl/consumer_cbrts.yml mlt-6.0.0/src/modules/plusgpl/consumer_cbrts.yml --- mlt-0.9.8/src/modules/plusgpl/consumer_cbrts.yml 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/consumer_cbrts.yml 2016-02-17 23:43:24.000000000 +0000 @@ -1,9 +1,9 @@ -schema_version: 0.1 +schema_version: 0.3 type: consumer identifier: cbrts title: CBR MPEG2-TS -version: 1 -copyright: Copyright (C) 2010-2014 Broadcasting Center Europe S.A. http://www.bce.lu +version: 2 +copyright: Copyright (C) 2010-2015 Broadcasting Center Europe S.A. http://www.bce.lu license: GPLv2 language: en creator: Dan Dennedy @@ -35,3 +35,83 @@ 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. + +parameters: + - identifier: muxrate + type: integer + unit: bytes/second + + - identifier: udp.rtprio + title: Real-time priority + description: > + When set to a valid value, this makes the network output thread run with a + real-time policy and priority where 1 is lowest and 99 is highest. + type: integer + minimum: 1 + maximum: 99 + + - identifier: udp.address + title: UDP address + description: > + If an IP address is provided, the stream is sent over UDP instead of STDOUT. + type: string + + - identifier: udp.port + title: UDP port + type: integer + minimum: 0 + default: 1234 + + - identifier: udp.ttl + title: Multicast TTL + description: > + The multicast time-to-live value controls how many routing hops the + multicast will survive. + type: integer + minimum: 0 + maximum: 255 + + - identifier: udp.reuse + title: Reuse socket address + description: > + When not supplied, the socket is opened with the reuse address option. Set + this to 0 to disable that. + type: boolean + default: 1 + + - identifier: udp.sockbufsize + title: Socket buffer size + type: integer + minimum: 1 + unit: bytes + + - identifier: udp.nb_tsp + title: TS packets per UDP packet + type: integer + minimum: 0 + maximum: 7 + default: 7 + + - identifier: udp.buffer + title: Max buffer IP packets + type: integer + minimum: 100 + default: 1000 + + - identifier: udp.rtp + title: Use RTP + type: boolean + default: 1 + + - identifier: udp.rtp_ssrc + title: RTP synchronization source + type: integer + description: The default is a random number, but you can override it. + + - identifier: udp.interface + title: Multicast interface name + description: > + Normally the multicast interface is selected by the IP routing table + configured on the system, but this might be more convenient. It takes + a name like "eth0". + type: string diff -Nru mlt-0.9.8/src/modules/plusgpl/filter_rotoscoping.c mlt-6.0.0/src/modules/plusgpl/filter_rotoscoping.c --- mlt-0.9.8/src/modules/plusgpl/filter_rotoscoping.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/filter_rotoscoping.c 2016-02-17 23:43:24.000000000 +0000 @@ -27,8 +27,6 @@ #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 */ diff -Nru mlt-0.9.8/src/modules/plusgpl/Makefile mlt-6.0.0/src/modules/plusgpl/Makefile --- mlt-0.9.8/src/modules/plusgpl/Makefile 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/Makefile 2016-02-17 23:43:24.000000000 +0000 @@ -18,6 +18,8 @@ ifeq ($(targetos), MinGW) LDFLAGS += -lws2_32 +else ifeq ($(targetos), Linux) +LDFLAGS += -lrt endif SRCS := $(OBJS:.o=.c) diff -Nru mlt-0.9.8/src/modules/plusgpl/utils.c mlt-6.0.0/src/modules/plusgpl/utils.c --- mlt-0.9.8/src/modules/plusgpl/utils.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/utils.c 2016-02-17 23:43:24.000000000 +0000 @@ -42,7 +42,7 @@ * generates 1,2,3,0,1,2,3,0... * You should use high-order bits. */ -#ifdef __DARWIN__ +#ifdef __APPLE__ static #endif unsigned int fastrand_val; diff -Nru mlt-0.9.8/src/modules/plusgpl/utils.h mlt-6.0.0/src/modules/plusgpl/utils.h --- mlt-0.9.8/src/modules/plusgpl/utils.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/plusgpl/utils.h 2016-02-17 23:43:24.000000000 +0000 @@ -23,7 +23,7 @@ /* utils.c */ void HSItoRGB(double H, double S, double I, int *r, int *g, int *b); -#ifndef __DARWIN__ +#ifndef __APPLE__ extern unsigned int fastrand_val; #define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) #endif diff -Nru mlt-0.9.8/src/modules/qt/common.cpp mlt-6.0.0/src/modules/qt/common.cpp --- mlt-0.9.8/src/modules/qt/common.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/common.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -42,7 +42,7 @@ 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") }; + 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)); @@ -86,3 +86,29 @@ } } } + +int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + + *image_format = mlt_image_rgb24a; + + // Use the width and height suggested by the rescale filter. + if( mlt_properties_get_int( frame_properties, "rescale_width" ) > 0 ) + *width = mlt_properties_get_int( frame_properties, "rescale_width" ); + if( mlt_properties_get_int( frame_properties, "rescale_height" ) > 0 ) + *height = mlt_properties_get_int( frame_properties, "rescale_height" ); + // If no size is requested, use native size. + if( *width <=0 ) + *width = mlt_properties_get_int( frame_properties, "meta.media.width" ); + if( *height <=0 ) + *height = mlt_properties_get_int( frame_properties, "meta.media.height" ); + + int size = mlt_image_format_size( *image_format, *width, *height, NULL ); + *image = static_cast( mlt_pool_alloc( size ) ); + memset( *image, 0, size ); // Transparent + mlt_frame_set_image( frame, *image, size, mlt_pool_release ); + + return error; +} diff -Nru mlt-0.9.8/src/modules/qt/common.h mlt-6.0.0/src/modules/qt/common.h --- mlt-0.9.8/src/modules/qt/common.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/common.h 2016-02-17 23:43:24.000000000 +0000 @@ -26,5 +26,6 @@ bool createQApplicationIfNeeded(mlt_service service); void copy_qimage_to_mlt_rgba( QImage* qImg, uint8_t* mImg ); void copy_mlt_to_qimage_rgba( uint8_t* mImg, QImage* qImg ); +int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ); #endif // COMMON_H diff -Nru mlt-0.9.8/src/modules/qt/factory.c mlt-6.0.0/src/modules/qt/factory.c --- mlt-0.9.8/src/modules/qt/factory.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/factory.c 2016-02-17 23:43:24.000000000 +0000 @@ -32,6 +32,7 @@ extern mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ); #ifdef USE_FFTW +extern mlt_filter filter_audiospectrum_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lightshow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #endif @@ -52,11 +53,13 @@ MLT_REGISTER( producer_type, "qtext", producer_qtext_init ); MLT_REGISTER( producer_type, "kdenlivetitle", producer_kdenlivetitle_init ); #ifdef USE_FFTW + MLT_REGISTER( filter_type, "audiospectrum", filter_audiospectrum_init ); MLT_REGISTER( filter_type, "lightshow", filter_lightshow_init ); #endif MLT_REGISTER_METADATA( filter_type, "audiowaveform", metadata, "filter_audiowaveform.yml" ); #ifdef USE_FFTW MLT_REGISTER_METADATA( filter_type, "lightshow", metadata, "filter_lightshow.yml" ); + MLT_REGISTER_METADATA( filter_type, "audiospectrum", metadata, "filter_audiospectrum.yml" ); #endif MLT_REGISTER_METADATA( producer_type, "qimage", metadata, "producer_qimage.yml" ); MLT_REGISTER_METADATA( producer_type, "qtext", metadata, "producer_qtext.yml" ); diff -Nru mlt-0.9.8/src/modules/qt/filter_audiospectrum.cpp mlt-6.0.0/src/modules/qt/filter_audiospectrum.cpp --- mlt-0.9.8/src/modules/qt/filter_audiospectrum.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/filter_audiospectrum.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * filter_audiospectrum.cpp -- audio spectrum visualization filter + * Copyright (c) 2015 Meltytech, LLC + * 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 "graph.h" +#include +#include +#include // memset +#include +#include +#include +#include //pow + +// Private Types +typedef struct +{ + mlt_filter fft; + char* fft_prop_name; + int preprocess_warned; +} private_data; + +static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) +{ + mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); + private_data* pdata = (private_data*)filter->child; + + // Create the FFT filter the first time. + if( !pdata->fft ) + { + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); + pdata->fft = mlt_factory_filter( profile, "fft", NULL ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES( pdata->fft ), "window_size", + mlt_properties_get_int( filter_properties, "window_size" ) ); + if( !pdata->fft ) + { + mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n" ); + return 1; + } + } + + mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); + + // The service must stay locked while using the private data + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + + // Perform FFT processing on the frame + mlt_filter_process( pdata->fft, frame ); + mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); + + float* bins = (float*)mlt_properties_get_data( fft_properties, "bins", NULL ); + + if( bins ) + { + double window_level = mlt_properties_get_double( fft_properties, "window_level" ); + int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); + size_t bins_size = bin_count * sizeof(float); + float* save_bins = (float*)mlt_pool_alloc( bins_size ); + + if( window_level == 1.0 ) + { + memcpy( save_bins, bins, bins_size ); + } else { + memset( save_bins, 0, bins_size ); + } + + // Save the bin data as a property on the frame to be used in get_image() + mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), pdata->fft_prop_name, save_bins, bins_size, mlt_pool_release, NULL ); + } + + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + + return 0; +} + +static void convert_fft_to_spectrum( mlt_filter filter, mlt_frame frame, int spect_bands, float* spectrum ) +{ + private_data* pdata = (private_data*)filter->child; + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + double low_freq = mlt_properties_get_int( filter_properties, "frequency_low" ); + double hi_freq = mlt_properties_get_int( filter_properties, "frequency_high" ); + int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); + double bin_width = mlt_properties_get_double( fft_properties, "bin_width" ); + float* bins = (float*)mlt_properties_get_data( frame_properties, pdata->fft_prop_name, NULL ); + double threshold = mlt_properties_get_int( filter_properties, "threshold" ); + int reverse = mlt_properties_get_int( filter_properties, "reverse" ); + + // Map the linear fft bin frequencies to a log scale spectrum. + double band_freq_factor = pow( hi_freq / low_freq, 1.0 / (double)spect_bands ); + double band_freq_low = low_freq; + double band_freq_hi = band_freq_low * band_freq_factor; + int bin_index = 0; + int spect_index = 0; + double bin_freq = 0; + + // Skip bins that occur before the low frequency of the spectrum + while( bin_freq < band_freq_low ) + { + bin_freq += bin_width; + bin_index ++; + } + + for( spect_index = 0; spect_index < spect_bands && bin_index < bin_count; spect_index++ ) + { + float mag = 0.0; + + if( bin_freq > band_freq_hi ) + { + // There is no bin for this point. Interpolate between the two closest. + if( bin_index == 0 ) + { + mag = bins[bin_index]; + } + else + { + double y0 = bins[bin_index - 1]; + double y1 = bins[bin_index]; + double spect_center = band_freq_low + (band_freq_hi - band_freq_low) / 2; + double prev_freq = bin_freq - bin_width; + double t = bin_width / ( spect_center - prev_freq ); + mag = y0 + ( y1 - y0 ) * t; + } + } + else + { + // Find the bin frequency with the greatest magnitude in the range for + // this spectrum point. + while( bin_freq < band_freq_hi && bin_index < bin_count ) + { + if( mag < bins[bin_index] ) + { + mag = bins[bin_index]; + } + + bin_freq += bin_width; + bin_index ++; + } + } + + // Scale the magnitude to the range 0.0-1.0 based on dB + double dB = mag > 0.0 ? 20 * log10( mag ) : -1000.0; + double spect_val = 0; + if( dB >= threshold ) + { + spect_val = 1.0 - (dB / threshold); + } + + if( reverse ) + { + spectrum[spect_bands - spect_index - 1] = spect_val; + } else { + spectrum[spect_index] = spect_val; + } + + // Calculate the next spectrum point frequency range. + band_freq_low = band_freq_hi; + band_freq_hi = band_freq_hi * band_freq_factor; + } +} + +static void draw_spectrum( mlt_filter filter, mlt_frame frame, QImage* qimg ) +{ + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); + if ( strchr( mlt_properties_get( filter_properties, "rect" ), '%' ) ) { + rect.x *= qimg->width(); + rect.w *= qimg->width(); + rect.y *= qimg->height(); + rect.h *= qimg->height(); + } + char* graph_type = mlt_properties_get( filter_properties, "type" ); + int mirror = mlt_properties_get_int( filter_properties, "mirror" ); + int fill = mlt_properties_get_int( filter_properties, "fill" ); + double tension = mlt_properties_get_double( filter_properties, "tension" ); + + QRectF r( rect.x, rect.y, rect.w, rect.h ); + QPainter p( qimg ); + + if( mirror ) { + // Draw two half rectangle instead of one full rectangle. + r.setHeight( r.height() / 2.0 ); + } + + setup_graph_painter( p, r, filter_properties ); + setup_graph_pen( p, r, filter_properties ); + + int bands = mlt_properties_get_int( filter_properties, "bands" ); + if ( bands == 0 ) { + // "0" means match rectangle width + bands = r.width(); + } + float* spectrum = (float*)mlt_pool_alloc( bands * sizeof(float) ); + + convert_fft_to_spectrum( filter, frame, bands, spectrum ); + + if( graph_type && graph_type[0] == 'b' ) { + paint_bar_graph( p, r, bands, spectrum ); + } else { + paint_line_graph( p, r, bands, spectrum, tension, fill ); + } + + if( mirror ) { + // Second rectangle is mirrored. + p.translate( 0, r.y() * 2 + r.height() * 2 ); + p.scale( 1, -1 ); + + if( graph_type && graph_type[0] == 'b' ) { + paint_bar_graph( p, r, bands, spectrum ); + } else { + paint_line_graph( p, r, bands, spectrum, tension, fill ); + } + } + + mlt_pool_release( spectrum ); + + p.end(); +} +/** 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_filter)mlt_frame_pop_service( frame ); + private_data* pdata = (private_data*)filter->child; + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + + if( mlt_properties_get_data( frame_properties, pdata->fft_prop_name, NULL ) ) + { + // Get the current image + *format = mlt_image_rgb24a; + error = mlt_frame_get_image( frame, image, format, width, height, 1 ); + + // Draw the spectrum + if( !error ) { + QImage qimg( *width, *height, QImage::Format_ARGB32 ); + copy_mlt_to_qimage_rgba( *image, &qimg ); + draw_spectrum( filter, frame, &qimg ); + copy_qimage_to_mlt_rgba( &qimg, *image ); + } + } else { + if ( pdata->preprocess_warned++ == 2 ) + { + // This filter depends on the consumer processing the audio before + // the video. + mlt_log_warning( MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n" ); + } + mlt_frame_get_image( frame, image, format, width, height, writable ); + } + + return error; +} + +/** Filter processing. +*/ +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + if( mlt_frame_is_test_card( frame ) ) { + // The producer does not generate video. This filter will create an + // image on the producer's behalf. + mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); + 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 ); + // Tell the framework that there really is an image. + mlt_properties_set_int( frame_properties, "test_image", 0 ); + // Push a callback to create the image. + mlt_frame_push_get_image( frame, create_image ); + } + + mlt_frame_push_audio( frame, filter ); + mlt_frame_push_audio( frame, (void*)filter_get_audio ); + 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* pdata = (private_data*)filter->child; + + if ( pdata ) + { + mlt_filter_close( pdata->fft ); + free( pdata->fft_prop_name ); + free( pdata ); + } + filter->child = NULL; + filter->close = NULL; + filter->parent.close = NULL; + mlt_service_close( &filter->parent ); +} + +/** Constructor for the filter. +*/ + +extern "C" { + +mlt_filter filter_audiospectrum_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new(); + private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); + + if ( filter && pdata && createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) + { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_int( properties, "_filter_private", 1 ); + mlt_properties_set_int( properties, "frequency_low", 20 ); + mlt_properties_set_int( properties, "frequency_high", 20000 ); + mlt_properties_set( properties, "type", "line" ); + mlt_properties_set( properties, "bgcolor", "0x00000000" ); + mlt_properties_set( properties, "color.1", "0xffffffff" ); + mlt_properties_set( properties, "rect", "0% 0% 100% 100%" ); + mlt_properties_set( properties, "thickness", "0" ); + mlt_properties_set( properties, "fill", "0" ); + mlt_properties_set( properties, "mirror", "0" ); + mlt_properties_set( properties, "reverse", "0" ); + mlt_properties_set( properties, "tension", "0.4" ); + mlt_properties_set( properties, "angle", "0" ); + mlt_properties_set( properties, "gorient", "v" ); + mlt_properties_set_int( properties, "bands", 31 ); + mlt_properties_set_double( properties, "threshold", -60.0 ); + mlt_properties_set_int( properties, "window_size", 8192 ); + + // Create a unique ID for storing data on the frame + pdata->fft_prop_name = (char*)calloc( 1, 20 ); + snprintf( pdata->fft_prop_name, 20, "fft.%p", filter ); + pdata->fft_prop_name[20 - 1] = '\0'; + + pdata->fft = 0; + + filter->close = filter_close; + filter->process = filter_process; + filter->child = pdata; + } + else + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter audio spectrum failed\n" ); + + if( filter ) + { + mlt_filter_close( filter ); + } + + if( pdata ) + { + free( pdata ); + } + + filter = NULL; + } + return filter; +} + +} + diff -Nru mlt-0.9.8/src/modules/qt/filter_audiospectrum.yml mlt-6.0.0/src/modules/qt/filter_audiospectrum.yml --- mlt-0.9.8/src/modules/qt/filter_audiospectrum.yml 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/filter_audiospectrum.yml 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,223 @@ +schema_version: 0.3 +type: filter +identifier: audiospectrum +title: Audio Spectrum Filter +version: 1 +copyright: Meltytech, LLC +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video +description: > + An audio visualization filter that draws an audio spectrum on the image. + +parameters: + - identifier: type + title: Graph type + description: The type of graph to display the spectrum. + type: string + default: line + values: + - line + - bar + readonly: no + mutable: yes + widget: combo + + - identifier: bgcolor + title: Background Color + type: color + description: | + The background color to be applied to the entire frame. The default color + is transparent. + + 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: color.* + title: Foreground color + type: color + description: | + The color of the waveform. + + Multiple colors can be specified with incrementing suffixes to cause the + waveform to be drawn in a gradient. color.1 is the top of the waveform. + Subsequent colors will produce a gradient toward the bottom. + + By default, the filter has one color defined: + + color.1=0xffffffff" + + This results in a white waveform. + + To create a gradient, define more colors: + + color.2=green color.3=0x77777777 + + 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: thickness + title: Line Thickness + type: integer + description: > + The thickness of the line used to draw the waveform for line graph. + The thickness of the bar for bar graph. + readonly: no + default: 0 + minimum: 0 + maximum: 20 + mutable: yes + widget: spinner + unit: pixels + + - identifier: angle + title: Angle + type: float + description: > + The rotation angle to be applied to the waveform. + readonly: no + default: 0 + minimum: 0 + maximum: 360 + mutable: yes + widget: spinner + + - identifier: rect + title: Rectangle + description: > + Defines the rectangle that the waveform(s) should be drawn in. + + Format is: "X,Y,W,H". + + X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. + type: rect + default: "0,0,100%,100%" + readonly: no + mutable: yes + + - identifier: fill + title: Fill + description: > + Whether the area under the waveform should be filled in. + Only applies to line graph type. + type: boolean + default: 0 + readonly: no + mutable: yes + widget: checkbox + + - identifier: mirror + title: Mirror + description: > + Mirror the spectrum about the center of the rectangle. + type: boolean + default: 0 + readonly: no + mutable: yes + widget: checkbox + + - identifier: reverse + title: Reverse + description: > + Draw the points starting with the highest frequency first. + type: boolean + default: 0 + readonly: no + mutable: yes + widget: checkbox + + - identifier: tension + title: Line Tension + description: > + Affects the amount of curve in the line interpolating between points. + 0.0 = a straight line between points. + 1.0 = very curved lines between points. + values < 0 and > 1 will cause loops in the lines. + Only applies to line graph type. + type: float + default: 0.4 + readonly: no + mutable: yes + + - identifier: gorient + title: Gradient Orientation + description: Direction of the color gradient. + type: string + default: vertical + values: + - vertical + - horizontal + readonly: no + mutable: yes + widget: combo + + - identifier: bands + title: Points + type: integer + description: > + The number of bands to draw in the spectrum. Each band shows up as a data + point in the graph. + mutable: yes + readonly: no + default: 31 + + - identifier: frequency_low + title: Low Frequency + type: integer + description: > + The low end of the frequency range to be used for the graph. + motion. + mutable: yes + readonly: no + default: 20 + unit: Hz + + - identifier: frequency_high + title: High Frequency + type: integer + description: > + The high end of the frequency range to be used for the graph. + motion. + mutable: yes + readonly: no + default: 20000 + unit: Hz + + - identifier: threshold + title: Level Threshold + type: float + description: > + The minimum amplitude of sound that must occur within the frequency range + to cause the value to be applied. + motion. + mutable: yes + readonly: no + default: -30 + minimum: -100 + maximum: 0 + unit: dB + + - identifier: window_size + title: Window Size + type: integer + description: > + The number of samples that the FFT will be performed on. If + window_size is less than the number of samples in a frame, extra samples + will be ignored. If window_size is more than the number of samples in a + frame, samples will be buffered from previous frames to fill the window. + The buffering is performed as a sliding window so that the most recent + samples are always transformed. + mutable: no + readonly: no + default: 2048 + \ No newline at end of file diff -Nru mlt-0.9.8/src/modules/qt/filter_audiowaveform.cpp mlt-6.0.0/src/modules/qt/filter_audiowaveform.cpp --- mlt-0.9.8/src/modules/qt/filter_audiowaveform.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/filter_audiowaveform.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -19,6 +19,7 @@ */ #include "common.h" +#include "graph.h" #include #include #include // memset @@ -119,62 +120,12 @@ } } -static void setup_pen( QPainter& p, QRectF& rect, mlt_properties filter_properties ) -{ - int thickness = mlt_properties_get_int( filter_properties, "thickness" ); - QString gorient = mlt_properties_get( filter_properties, "gorient" ); - QVector colors; - bool color_found = true; - - QPen pen; - pen.setWidth( qAbs(thickness) ); - - // Find user specified colors for the gradient - while( color_found ) { - QString prop_name = QString("color.") + QString::number(colors.size() + 1); - if( mlt_properties_get(filter_properties, prop_name.toUtf8().constData() ) ) { - mlt_color mcolor = mlt_properties_get_color( filter_properties, prop_name.toUtf8().constData() ); - colors.append( QColor( mcolor.r, mcolor.g, mcolor.b, mcolor.a ) ); - } else { - color_found = false; - } - } - - if( !colors.size() ) { - // No color specified. Just use white. - pen.setBrush(Qt::white); - } else if ( colors.size() == 1 ) { - // Only use one color - pen.setBrush(colors[0]); - } else { - QLinearGradient gradient; - if( gorient.startsWith("h", Qt::CaseInsensitive) ) { - gradient.setStart ( rect.x(), rect.y() ); - gradient.setFinalStop ( rect.x() + rect.width(), rect.y() ); - } else { // Vertical - gradient.setStart ( rect.x(), rect.y() ); - gradient.setFinalStop ( rect.x(), rect.y() + rect.height() ); - } - - qreal step = 1.0 / ( colors.size() - 1 ); - for( int i = 0; i < colors.size(); i++ ) - { - gradient.setColorAt( (qreal)i * step, colors[i] ); - } - pen.setBrush(gradient); - } - - p.setPen(pen); -} - static void draw_waveforms( mlt_filter filter, mlt_frame frame, QImage* qimg, int16_t* audio, int channels, int samples ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); - mlt_color bg_color = mlt_properties_get_color( filter_properties, "bgcolor" ); int show_channel = mlt_properties_get_int( filter_properties, "show_channel" ); - double angle = mlt_properties_get_double( filter_properties, "angle" ); int fill = mlt_properties_get_int( filter_properties, "fill" ); mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); if ( strchr( mlt_properties_get( filter_properties, "rect" ), '%' ) ) { @@ -188,20 +139,7 @@ QPainter p( qimg ); - p.setRenderHint(QPainter::Antialiasing); - - // Fill background - if ( bg_color.r || bg_color.g || bg_color.g || bg_color.a ) { - QColor qbgcolor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ); - p.fillRect( 0, 0, qimg->width(), qimg->height(), qbgcolor ); - } - - // Apply rotation - if ( angle ) { - p.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 ); - p.rotate( angle ); - p.translate( -(r.x() + r.width() / 2), -(r.y() + r.height() / 2) ); - } + setup_graph_painter( p, r, filter_properties ); if ( show_channel == 0 ) // Show all channels { @@ -212,7 +150,7 @@ // Divide the rectangle into smaller rectangles for each channel. c_rect.setY( r.y() + c_height * c ); c_rect.setHeight( c_height ); - setup_pen( p, c_rect, filter_properties ); + setup_graph_pen( p, c_rect, filter_properties ); paint_waveform( p, c_rect, audio + c, samples, channels, fill ); } } else if ( show_channel > 0 ) { // Show one specific channel @@ -220,7 +158,7 @@ // Sanity show_channel = 1; } - setup_pen( p, r, filter_properties ); + setup_graph_pen( p, r, filter_properties ); paint_waveform( p, r, audio + show_channel - 1, samples, channels, fill ); } @@ -282,32 +220,6 @@ return error; } - -static int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) -{ - int error = 0; - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); - - *image_format = mlt_image_rgb24a; - - // Use the width and height suggested by the rescale filter. - if( mlt_properties_get_int( frame_properties, "rescale_width" ) > 0 ) - *width = mlt_properties_get_int( frame_properties, "rescale_width" ); - if( mlt_properties_get_int( frame_properties, "rescale_height" ) > 0 ) - *height = mlt_properties_get_int( frame_properties, "rescale_height" ); - // If no size is requested, use native size. - if( *width <=0 ) - *width = mlt_properties_get_int( frame_properties, "meta.media.width" ); - if( *height <=0 ) - *height = mlt_properties_get_int( frame_properties, "meta.media.height" ); - - int size = mlt_image_format_size( *image_format, *width, *height, NULL ); - *image = static_cast( mlt_pool_alloc( size ) ); - memset( *image, 0, size ); // Transparent - mlt_frame_set_image( frame, *image, size, mlt_pool_release ); - - return error; -} /** Filter processing. */ diff -Nru mlt-0.9.8/src/modules/qt/filter_lightshow.cpp mlt-6.0.0/src/modules/qt/filter_lightshow.cpp --- mlt-0.9.8/src/modules/qt/filter_lightshow.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/filter_lightshow.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -238,32 +238,6 @@ return error; } -static int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) -{ - int error = 0; - mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); - - *image_format = mlt_image_rgb24a; - - // Use the width and height suggested by the rescale filter. - if( mlt_properties_get_int( frame_properties, "rescale_width" ) > 0 ) - *width = mlt_properties_get_int( frame_properties, "rescale_width" ); - if( mlt_properties_get_int( frame_properties, "rescale_height" ) > 0 ) - *height = mlt_properties_get_int( frame_properties, "rescale_height" ); - // If no size is requested, use native size. - if( *width <=0 ) - *width = mlt_properties_get_int( frame_properties, "meta.media.width" ); - if( *height <=0 ) - *height = mlt_properties_get_int( frame_properties, "meta.media.height" ); - - int size = mlt_image_format_size( *image_format, *width, *height, NULL ); - *image = static_cast( mlt_pool_alloc( size ) ); - memset( *image, 0, size ); // Transparent - mlt_frame_set_image( frame, *image, size, mlt_pool_release ); - - return error; -} - /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) @@ -316,7 +290,7 @@ mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); - if ( filter && pdata ) + if ( filter && pdata && createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "_filter_private", 1 ); diff -Nru mlt-0.9.8/src/modules/qt/graph.cpp mlt-6.0.0/src/modules/qt/graph.cpp --- mlt-0.9.8/src/modules/qt/graph.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/graph.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015 Meltytech, LLC + * 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 "graph.h" +#include +#include + +/* + * Apply the "bgcolor" and "angle" parameters to the QPainter + */ +void setup_graph_painter( QPainter& p, QRectF& r, mlt_properties filter_properties ) +{ + mlt_color bg_color = mlt_properties_get_color( filter_properties, "bgcolor" ); + double angle = mlt_properties_get_double( filter_properties, "angle" ); + + p.setRenderHint(QPainter::Antialiasing); + + // Fill background + if ( bg_color.r || bg_color.g || bg_color.g || bg_color.a ) { + QColor qbgcolor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ); + p.fillRect( 0, 0, p.device()->width(), p.device()->height(), qbgcolor ); + } + + // Apply rotation + if ( angle ) { + p.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 ); + p.rotate( angle ); + p.translate( -(r.x() + r.width() / 2), -(r.y() + r.height() / 2) ); + } +} + +/* + * Apply the "thickness", "gorient" and "color.x" parameters to the QPainter + */ +void setup_graph_pen( QPainter& p, QRectF& r, mlt_properties filter_properties ) +{ + int thickness = mlt_properties_get_int( filter_properties, "thickness" ); + QString gorient = mlt_properties_get( filter_properties, "gorient" ); + QVector colors; + bool color_found = true; + + QPen pen; + pen.setWidth( qAbs(thickness) ); + + // Find user specified colors for the gradient + while( color_found ) { + QString prop_name = QString("color.") + QString::number(colors.size() + 1); + if( mlt_properties_get(filter_properties, prop_name.toUtf8().constData() ) ) { + mlt_color mcolor = mlt_properties_get_color( filter_properties, prop_name.toUtf8().constData() ); + colors.append( QColor( mcolor.r, mcolor.g, mcolor.b, mcolor.a ) ); + } else { + color_found = false; + } + } + + if( !colors.size() ) { + // No color specified. Just use white. + pen.setBrush(Qt::white); + } else if ( colors.size() == 1 ) { + // Only use one color + pen.setBrush(colors[0]); + } else { + QLinearGradient gradient; + if( gorient.startsWith("h", Qt::CaseInsensitive) ) { + gradient.setStart ( r.x(), r.y() ); + gradient.setFinalStop ( r.x() + r.width(), r.y() ); + } else { // Vertical + gradient.setStart ( r.x(), r.y() ); + gradient.setFinalStop ( r.x(), r.y() + r.height() ); + } + + qreal step = 1.0 / ( colors.size() - 1 ); + for( int i = 0; i < colors.size(); i++ ) + { + gradient.setColorAt( (qreal)i * step, colors[i] ); + } + pen.setBrush(gradient); + } + + p.setPen(pen); +} + +static inline void fix_point( QPointF& point, QRectF& rect ) +{ + if( point.x() < rect.x() ) { + point.setX( rect.x() ); + } else if ( point.x() > rect.x() + rect.width() ) { + point.setX( rect.x() + rect.width() ); + } + + if( point.y() < rect.y() ) { + point.setY( rect.y() ); + } else if ( point.y() > rect.y() + rect.height() ) { + point.setY( rect.y() + rect.height() ); + } +} + +void paint_line_graph( QPainter& p, QRectF& rect, int points, float* values, double tension, int fill ) +{ + double width = rect.width(); + double height = rect.height(); + double pixelsPerPoint = width / (double)(points - 1); + + // Calculate cubic control points + QVector controlPoints( (points - 1) * 2 ); + int cpi = 0; + // First control point is equal to first point + controlPoints[cpi++] = QPointF( rect.x(), rect.y() + height - values[0] * height ); + + // Calculate control points between data points + // Based on ideas from: + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + for( int i = 0; i < (points - 2); i++ ) { + double x0 = rect.x() + (double)i * pixelsPerPoint; + double x1 = rect.x() + (double)(i + 1) * pixelsPerPoint; + double x2 = rect.x() + (double)(i + 2) * pixelsPerPoint; + double y0 = rect.y() + height - values[i] * height; + double y1 = rect.y() + height - values[i + 1] * height; + double y2 = rect.y() + height - values[i + 2] * height; + double d01 = sqrt( pow( x1 - x0, 2 ) + pow( y1 - y0, 2) ); + double d12 = sqrt( pow( x2 - x1, 2 ) + pow( y2 - y1, 2) ); + double fa = tension * d01 / ( d01 + d12 ); + double fb = tension * d12 / ( d01 + d12 ); + double p1x = x1 - fa * ( x2 - x0 ); + double p1y = y1 - fa * ( y2 - y0 ); + QPointF c1( p1x, p1y ); + fix_point( c1, rect ); + double p2x = x1 + fb * ( x2 - x0 ); + double p2y = y1 + fb * ( y2 - y0 ); + QPointF c2( p2x, p2y ); + fix_point( c2, rect ); + controlPoints[cpi++] = c1; + controlPoints[cpi++] = c2; + } + + // Last control point is equal to last point + controlPoints[cpi++] = QPointF( rect.x() + width, rect.y() + height - values[points - 1] * height ); + + // Draw a sequence of bezier curves to interpolate between points. + QPainterPath curvePath; + + // Begin at the first data point. + curvePath.moveTo( rect.x(), rect.y() + height - values[0] * height ); + cpi = 0; + for( int i = 1; i < points; i++ ) { + QPointF c1 = controlPoints[cpi++]; + QPointF c2 = controlPoints[cpi++]; + double endX = rect.x() + (double)i * pixelsPerPoint; + double endY = rect.y() + height - values[i] * height; + QPointF end( endX, endY ); + curvePath.cubicTo( c1, c2, end ); + } + + if( fill ) { + curvePath.lineTo( rect.x() + width, rect.y() + height ); + curvePath.lineTo( rect.x(), rect.y() + height ); + curvePath.closeSubpath(); + p.fillPath( curvePath, p.pen().brush() ); + } else { + p.drawPath( curvePath ); + } +} + +void paint_bar_graph( QPainter& p, QRectF& rect, int points, float* values ) +{ + double width = rect.width(); + double height = rect.height(); + double pixelsPerPoint = width / (double)points; + QPointF bottomPoint( rect.x() + pixelsPerPoint / 2, rect.y() + height ); + QPointF topPoint( rect.x() + pixelsPerPoint / 2, rect.y() ); + + for( int i = 0; i < points; i++ ) { + topPoint.setY( rect.y() + height - values[i] * height ); + p.drawLine( bottomPoint, topPoint ); + bottomPoint.setX( bottomPoint.x() + pixelsPerPoint ); + topPoint.setX( bottomPoint.x() ); + } +} diff -Nru mlt-0.9.8/src/modules/qt/graph.h mlt-6.0.0/src/modules/qt/graph.h --- mlt-0.9.8/src/modules/qt/graph.h 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/graph.h 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015 Meltytech, LLC + * 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 + */ + +#ifndef GRAPH_H +#define GRAPH_H + +#include +#include +#include + +void setup_graph_painter( QPainter& p, QRectF& rect, mlt_properties filter_properties ); +void setup_graph_pen( QPainter& p, QRectF& rect, mlt_properties filter_properties ); +void paint_line_graph( QPainter& p, QRectF& rect, int points, float* values, double tension, int fill ); +void paint_bar_graph( QPainter& p, QRectF& rect, int points, float* values ); + + +#endif // GRAPH_H diff -Nru mlt-0.9.8/src/modules/qt/Makefile mlt-6.0.0/src/modules/qt/Makefile --- mlt-0.9.8/src/modules/qt/Makefile 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/Makefile 2016-02-17 23:43:24.000000000 +0000 @@ -9,6 +9,7 @@ OBJS = factory.o producer_qimage.o producer_kdenlivetitle.o CPPOBJS = common.o \ + graph.o \ filter_audiowaveform.o \ qimage_wrapper.o \ kdenlivetitle_wrapper.o \ @@ -25,7 +26,8 @@ endif ifdef USE_FFTW - CPPOBJS += filter_lightshow.o + CPPOBJS += filter_lightshow.o \ + filter_audiospectrum.o CFLAGS += -DUSE_FFTW endif diff -Nru mlt-0.9.8/src/modules/qt/producer_qimage.c mlt-6.0.0/src/modules/qt/producer_qimage.c --- mlt-0.9.8/src/modules/qt/producer_qimage.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/producer_qimage.c 2016-02-17 23:43:24.000000000 +0000 @@ -49,7 +49,13 @@ mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); // Initialize KDE image plugins - init_qimage(); + if ( !init_qimage( filename ) ) + { + // Reject if animation. + mlt_producer_close( producer ); + free( self ); + return NULL; + } // Callback registration producer->get_frame = producer_get_frame; diff -Nru mlt-0.9.8/src/modules/qt/qimage_wrapper.cpp mlt-6.0.0/src/modules/qt/qimage_wrapper.cpp --- mlt-0.9.8/src/modules/qt/qimage_wrapper.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/qimage_wrapper.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef USE_EXIF #include @@ -62,14 +63,19 @@ } -void init_qimage() +/// Returns false if this is animated. +int init_qimage(const char *filename) { + QImageReader reader( filename ); + if ( reader.canRead() && reader.imageCount() > 1 ) { + return 0; + } #ifdef USE_KDE4 if ( !instance ) { instance = new KComponentData( "qimage_prod" ); } #endif - + return 1; } static QImage* reorient_with_exif( producer_qimage self, int image_idx, QImage *qimage ) diff -Nru mlt-0.9.8/src/modules/qt/qimage_wrapper.h mlt-6.0.0/src/modules/qt/qimage_wrapper.h --- mlt-0.9.8/src/modules/qt/qimage_wrapper.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/qt/qimage_wrapper.h 2016-02-17 23:43:24.000000000 +0000 @@ -55,7 +55,7 @@ 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(); +extern int init_qimage(const char *filename); #ifdef __cplusplus diff -Nru mlt-0.9.8/src/modules/sdl/consumer_sdl_audio.c mlt-6.0.0/src/modules/sdl/consumer_sdl_audio.c --- mlt-0.9.8/src/modules/sdl/consumer_sdl_audio.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/sdl/consumer_sdl_audio.c 2016-02-17 23:43:24.000000000 +0000 @@ -162,6 +162,16 @@ { consumer_stop( parent ); + mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); + char *audio_driver = mlt_properties_get( properties, "audio_driver" ); + char *audio_device = mlt_properties_get( properties, "audio_device" ); + + if ( audio_driver && strcmp( audio_driver, "" ) ) + setenv( "SDL_AUDIODRIVER", audio_driver, 1 ); + + if ( audio_device && strcmp( audio_device, "" ) ) + setenv( "AUDIODEV", audio_device, 1 ); + pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); @@ -196,7 +206,7 @@ pthread_mutex_unlock( &self->refresh_mutex ); // Cleanup the main thread -#ifndef WIN32 +#ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); @@ -252,7 +262,7 @@ pthread_mutex_lock( &self->audio_mutex ); // Block until audio received -#ifdef __DARWIN__ +#ifdef __APPLE__ while ( self->running && len > self->audio_avail ) pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); #endif diff -Nru mlt-0.9.8/src/modules/sdl/consumer_sdl.c mlt-6.0.0/src/modules/sdl/consumer_sdl.c --- mlt-0.9.8/src/modules/sdl/consumer_sdl.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/sdl/consumer_sdl.c 2016-02-17 23:43:24.000000000 +0000 @@ -141,7 +141,7 @@ // Set the sdl flags self->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_DOUBLEBUF; -#if !defined(__DARWIN__) +#if !defined(__APPLE__) self->sdl_flags |= SDL_RESIZABLE; #endif // Allow thread to be started/stopped @@ -281,7 +281,7 @@ // Kill the thread and clean up self->joined = 1; self->running = 0; -#ifndef WIN32 +#ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); @@ -910,11 +910,11 @@ // Lock the display //sdl_lock_display(); -#ifndef __DARWIN__ +#ifndef __APPLE__ // Get the wm structure if ( SDL_GetWMInfo( &wm ) == 1 ) { -#ifndef WIN32 +#ifndef _WIN32 // Check that we have the X11 wm if ( wm.subsystem == SDL_SYSWM_X11 ) { diff -Nru mlt-0.9.8/src/modules/sdl/consumer_sdl_osx.h mlt-6.0.0/src/modules/sdl/consumer_sdl_osx.h --- mlt-0.9.8/src/modules/sdl/consumer_sdl_osx.h 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/sdl/consumer_sdl_osx.h 2016-02-17 23:43:24.000000000 +0000 @@ -21,7 +21,7 @@ #ifndef _CONSUMER_SDL_OSX_H_ #define _CONSUMER_SDL_OSX_H_ -#ifdef __DARWIN__ +#ifdef __APPLE__ void* mlt_cocoa_autorelease_init(); void mlt_cocoa_autorelease_close( void* ); diff -Nru mlt-0.9.8/src/modules/sdl/consumer_sdl_preview.c mlt-6.0.0/src/modules/sdl/consumer_sdl_preview.c --- mlt-0.9.8/src/modules/sdl/consumer_sdl_preview.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/sdl/consumer_sdl_preview.c 2016-02-17 23:43:24.000000000 +0000 @@ -253,7 +253,7 @@ pthread_mutex_lock( &self->refresh_mutex ); pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); -#ifndef WIN32 +#ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); diff -Nru mlt-0.9.8/src/modules/sdl/consumer_sdl_still.c mlt-6.0.0/src/modules/sdl/consumer_sdl_still.c --- mlt-0.9.8/src/modules/sdl/consumer_sdl_still.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/sdl/consumer_sdl_still.c 2016-02-17 23:43:24.000000000 +0000 @@ -598,11 +598,11 @@ // Specify the SDL Version SDL_VERSION( &wm.version ); -#ifndef __DARWIN__ +#ifndef __APPLE__ // Get the wm structure if ( SDL_GetWMInfo( &wm ) == 1 ) { -#ifndef WIN32 +#ifndef _WIN32 // Check that we have the X11 wm if ( wm.subsystem == SDL_SYSWM_X11 ) { diff -Nru mlt-0.9.8/src/modules/videostab/stab/estimate.c mlt-6.0.0/src/modules/videostab/stab/estimate.c --- mlt-0.9.8/src/modules/videostab/stab/estimate.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/videostab/stab/estimate.c 2016-02-17 23:43:24.000000000 +0000 @@ -18,7 +18,7 @@ #include #include #include -#if !defined(__DARWIN__) && !defined(__FreeBSD__) && !defined(WIN32) && !defined(__NetBSD__) +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(_WIN32) && !defined(__NetBSD__) #include #endif diff -Nru mlt-0.9.8/src/modules/videostab/transform_image.c mlt-6.0.0/src/modules/videostab/transform_image.c --- mlt-0.9.8/src/modules/videostab/transform_image.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/videostab/transform_image.c 2016-02-17 23:43:24.000000000 +0000 @@ -28,10 +28,6 @@ #include #include -#define MAX (a,b) (((a)>(b)?(a):(b))) -#define MIN (a,b) (((a)<(b)?(a):(b))) -#define CLAMP(a,x,y) MIN( MAX( (a),(x) ,y)) - #define TC_MAX(a, b) (((a) > (b)) ?(a) :(b)) #define TC_MIN(a, b) (((a) < (b)) ?(a) :(b)) /* clamp x between a and b */ diff -Nru mlt-0.9.8/src/modules/vmfx/filter_shape.c mlt-6.0.0/src/modules/vmfx/filter_shape.c --- mlt-0.9.8/src/modules/vmfx/filter_shape.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/vmfx/filter_shape.c 2016-02-17 23:43:24.000000000 +0000 @@ -115,20 +115,7 @@ // Calculate the position and length int position = mlt_filter_get_position( this, frame ); - int in = mlt_filter_get_in( this ); - int out = mlt_filter_get_out( this ); - int length; - - // Special case for attached filters - in/out come through on the frame - if ( out == 0 ) - { - in = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "in" ); - out = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "out" ); - position -= in; - } - - // Duration of the shape - length = out - in + 1; + mlt_position length = mlt_filter_get_length2( this, frame ); // If we haven't created the instance or it's changed if ( producer == NULL || strcmp( resource, last_resource ) ) diff -Nru mlt-0.9.8/src/modules/xine/filter_deinterlace.c mlt-6.0.0/src/modules/xine/filter_deinterlace.c --- mlt-0.9.8/src/modules/xine/filter_deinterlace.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/xine/filter_deinterlace.c 2016-02-17 23:43:24.000000000 +0000 @@ -118,6 +118,8 @@ if ( !previous_frame || !next_frame ) return 1; + mlt_service_lock( MLT_FILTER_SERVICE(filter) ); + // Get the preceding frame's image int error = mlt_frame_get_image( previous_frame, &previous_image, format, &previous_width, &previous_height, 0 ); int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( previous_frame ), "progressive" ); @@ -128,15 +130,17 @@ // OK, now we know we have work to do and can request the image in our format frame->convert_image( previous_frame, &previous_image, format, mlt_image_yuv422 ); + mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); + // Get the current frame's image *format = mlt_image_yuv422; - error = mlt_frame_get_image( frame, image, format, width, height, 0 ); + error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( !error && *image && *format == mlt_image_yuv422 ) { // Get the following frame's image error = mlt_frame_get_image( next_frame, &next_image, format, &next_width, &next_height, 0 ); - + if ( !error && next_image && *format == mlt_image_yuv422 ) { yadif_filter *yadif = init_yadif( *width, *height ); @@ -173,6 +177,8 @@ } else { + mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); + // Get the current frame's image error = mlt_frame_get_image( frame, image, format, width, height, 0 ); } @@ -269,7 +275,7 @@ if ( !error && *image && *format == mlt_image_yuv422 ) { // Deinterlace the image using one of the Xine deinterlacers - int image_size = *width * *height * 2; + int image_size = mlt_image_format_size( *format, *width, *height, NULL ); uint8_t *new_image = mlt_pool_alloc( image_size ); deinterlace_yuv( new_image, image, *width * 2, *height, method ); diff -Nru mlt-0.9.8/src/modules/xml/consumer_xml.c mlt-6.0.0/src/modules/xml/consumer_xml.c --- mlt-0.9.8/src/modules/xml/consumer_xml.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/xml/consumer_xml.c 2016-02-17 23:43:24.000000000 +0000 @@ -1,6 +1,6 @@ /* * consumer_xml.c -- a libxml2 serialiser of mlt service networks - * Copyright (C) 2003-2015 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,7 +25,6 @@ #include #include #include -#include #define ID_SIZE 128 #define TIME_PROPERTY "_consumer_xml" @@ -62,36 +61,6 @@ static void *consumer_thread( void *arg ); static void serialise_service( serialise_context context, mlt_service service, xmlNode *node ); -static char* filter_restricted( const char *in ) -{ - if ( !in ) return NULL; - size_t n = strlen( in ); - char *out = calloc( 1, n + 1 ); - char *p = out; - mbstate_t mbs; - memset( &mbs, 0, sizeof(mbs) ); - while ( *in ) - { - wchar_t w; - size_t c = mbrtowc( &w, in, n, &mbs ); - if ( c <= 0 || c > n ) break; - n -= c; - in += c; - if ( w == 0x9 || w == 0xA || w == 0xD || - ( w >= 0x20 && w <= 0xD7FF ) || - ( w >= 0xE000 && w <= 0xFFFD ) || - ( w >= 0x10000 && w <= 0x10FFFF ) ) - { - mbstate_t ps; - memset( &ps, 0, sizeof(ps) ); - c = wcrtomb( p, w, &ps ); - if ( c > 0 ) - p += c; - } - } - return out; -} - typedef enum { xml_existing, @@ -235,13 +204,9 @@ { char *value = NULL; if ( !strcmp( name, "length" ) ) - { - char *time = mlt_properties_get_time( properties, name, context->time_format ); - if ( time ) - value = strdup( time ); - } + value = mlt_properties_get_time( properties, name, context->time_format ); else - value = filter_restricted( mlt_properties_get_value( properties, i ) ); + value = mlt_properties_get_value( properties, i ); if ( value ) { int rootlen = strlen( context->root ); @@ -251,7 +216,6 @@ else p = xmlNewTextChild( node, NULL, _x("property"), _x(value) ); xmlNewProp( p, _x("name"), _x(name) ); - free( value ); } } } @@ -268,7 +232,7 @@ char *name = mlt_properties_get_name( properties, i ); if ( !strncmp( name, store, strlen( store ) ) ) { - char *value = filter_restricted( mlt_properties_get_value( properties, i ) ); + char *value = mlt_properties_get_value( properties, i ); if ( value ) { int rootlen = strlen( context->root ); @@ -278,7 +242,6 @@ else p = xmlNewTextChild( node, NULL, _x("property"), _x(value) ); xmlNewProp( p, _x("name"), _x(name) ); - free( value ); } } } @@ -727,7 +690,17 @@ if ( mlt_properties_get_lcnumeric( properties ) ) xmlNewProp( root, _x("LC_NUMERIC"), _x( mlt_properties_get_lcnumeric( properties ) ) ); else +#ifdef _WIN32 + { + const char* lcnumeric = setlocale( LC_NUMERIC, NULL ); + mlt_properties_set( properties, "_xml_lcnumeric_in", lcnumeric ); + mlt_properties_to_utf8( properties, "_xml_lcnumeric_in", "_xml_lcnumeric_out" ); + lcnumeric = mlt_properties_get( properties, "_xml_lcnumeric_out" ); + xmlNewProp( root, _x("LC_NUMERIC"), _x( lcnumeric ) ); + } +#else xmlNewProp( root, _x("LC_NUMERIC"), _x( setlocale( LC_NUMERIC, NULL ) ) ); +#endif // Indicate the version xmlNewProp( root, _x("version"), _x( mlt_version_get_string() ) ); @@ -861,7 +834,7 @@ int length = 0; xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" ); mlt_properties_set( properties, resource, _s(buffer) ); -#ifdef WIN32 +#ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif diff -Nru mlt-0.9.8/src/modules/xml/producer_xml.c mlt-6.0.0/src/modules/xml/producer_xml.c --- mlt-0.9.8/src/modules/xml/producer_xml.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/modules/xml/producer_xml.c 2016-02-17 23:43:24.000000000 +0000 @@ -925,7 +925,7 @@ // Associate the filter with the parent if ( parent != NULL ) { - if ( parent_type == mlt_tractor_type ) + if ( parent_type == mlt_tractor_type && mlt_properties_get( properties, "track" ) ) { mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) ); mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) ); @@ -1181,7 +1181,7 @@ // Serialise the tree to get value xmlDocDumpMemory( context->value_doc, &value, &size ); mlt_properties_set( properties, context->property, _s(value) ); -#ifdef WIN32 +#ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif @@ -1513,7 +1513,7 @@ case ':': case '=': -#ifdef WIN32 +#ifdef _WIN32 if ( url[i] == ':' && url[i + 1] != '/' ) #endif if ( is_query ) @@ -1686,6 +1686,14 @@ if ( !file_exists( filename ) ) { + // Try the un-converted text encoding as a fallback. + // Fixes launching melt as child process from Shotcut on Windows + // when there are extended characters in the path. + filename = mlt_properties_get( context->params, "_mlt_xml_resource" ); + } + + if ( !file_exists( filename ) ) + { context_close( context ); return NULL; } diff -Nru mlt-0.9.8/src/tests/test_animation/test_animation.pro mlt-6.0.0/src/tests/test_animation/test_animation.pro --- mlt-0.9.8/src/tests/test_animation/test_animation.pro 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/tests/test_animation/test_animation.pro 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,3 @@ +include(../common.pri) +TARGET = test_animation +SOURCES += test_animation.cpp diff -Nru mlt-0.9.8/src/tests/test_filter/test_filter.cpp mlt-6.0.0/src/tests/test_filter/test_filter.cpp --- mlt-0.9.8/src/tests/test_filter/test_filter.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/tests/test_filter/test_filter.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -27,14 +27,14 @@ public: TestFilter() { -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__linux__) || defined(__APPLE__) locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); #endif Factory::init(); } ~TestFilter() { -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__linux__) || defined(__APPLE__) freelocale(locale); #endif } diff -Nru mlt-0.9.8/src/tests/test_properties/test_properties.cpp mlt-6.0.0/src/tests/test_properties/test_properties.cpp --- mlt-0.9.8/src/tests/test_properties/test_properties.cpp 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/tests/test_properties/test_properties.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Dan Dennedy + * Copyright (C) 2013-2015 Dan Dennedy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,7 +23,7 @@ using namespace Mlt; extern "C" { -#define __DARWIN__ +#define __APPLE__ #include #include } @@ -36,14 +36,14 @@ public: TestProperties() { -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__linux__) || defined(__APPLE__) locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); #endif Factory::init(); } ~TestProperties() { -#if defined(__linux__) || defined(__DARWIN__) +#if defined(__linux__) || defined(__APPLE__) freelocale(locale); #endif } @@ -192,6 +192,30 @@ QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } + void SetAndGetTimeCodeNonIntFps() + { + Profile profile("atsc_720p_2398"); + Properties p; + p.set("_profile", profile.get_profile(), 0); + const char *timeString = "11:22:33:04"; + const int frames = 981894; + p.set("key", timeString); + QCOMPARE(p.get_int("key"), frames); + p.set("key", frames); + QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); + } + + void SetAndGetTimeCodeNonDropFrame() + { + Profile profile("dv_ntsc"); + Properties p; + p.set("_profile", profile.get_profile(), 0); + const char *timeString = "11:22:33:04"; + const int frames = 1228594; + p.set("key", frames); + QCOMPARE(p.get_time("key", mlt_time_smpte_ndf), timeString); + } + void SetAndGetTimeClock() { Profile profile; @@ -204,6 +228,19 @@ QCOMPARE(p.get_time("key", mlt_time_clock), timeString); } + void SetAndGetTimeClockNonIntFps() + { + Profile profile("dv_ntsc"); + Properties p; + p.set("_profile", profile.get_profile(), 0); + const char *timeString = "11:22:33.400"; + const int frames = 1227375; + p.set("key", timeString); + QCOMPARE(p.get_int("key"), frames); + p.set("key", frames); + QCOMPARE(p.get_time("key", mlt_time_clock), timeString); + } + void SetSimpleMathExpression() { Properties p; diff -Nru mlt-0.9.8/src/tests/tests.pro mlt-6.0.0/src/tests/tests.pro --- mlt-0.9.8/src/tests/tests.pro 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/tests/tests.pro 2016-02-17 23:43:24.000000000 +0000 @@ -3,4 +3,5 @@ test_frame \ test_properties \ test_repository \ - test_animation + test_animation \ + test_tractor diff -Nru mlt-0.9.8/src/tests/test_tractor/test_tractor.cpp mlt-6.0.0/src/tests/test_tractor/test_tractor.cpp --- mlt-0.9.8/src/tests/test_tractor/test_tractor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/tests/test_tractor/test_tractor.cpp 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2015 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 consumer library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +using namespace Mlt; + +class TestTractor : public QObject +{ + Q_OBJECT + Profile profile; + +public: + TestTractor() + : profile("dv_pal") + { + Factory::init(); + } + +private Q_SLOTS: + + void CreateSingleTrack() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p(profile, "noise"); + QVERIFY(p.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p, t.count()); + QCOMPARE(t.count(), 1); + } + + void FailSameProducerNewTrack() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p(profile, "noise"); + QVERIFY(p.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p, t.count()); + QCOMPARE(t.count(), 1); + int result = t.set_track(p, t.count()); + QCOMPARE(result, 3); + QCOMPARE(t.count(), 1); + } + + void CreateMultipleTracks() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + } + + void PlantTransitionWorks() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 1); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + } + + void InsertTrackBelowAdjustsTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 1); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.insert_track(p3, 0); + QCOMPARE(t.count(), 3); + QCOMPARE(trans.get_int("a_track"), 1); + QCOMPARE(trans.get_int("b_track"), 2); + } + + void InsertTrackMiddleAdjustsTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 1); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.insert_track(p3, 1); + QCOMPARE(t.count(), 3); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 2); + } + + void InsertTrackAboveDoesNotAffectTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 1); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.insert_track(p3, 2); + QCOMPARE(t.count(), 3); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + } + + void RemoveTrackBelowAdjustsTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.set_track(p3, t.count()); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 1); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + + t.remove_track(0); + QCOMPARE(t.count(), 2); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 0); + // This transition is a candidate for removal. + } + + void RemoveMiddleTrackAdjustsTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.set_track(p3, t.count()); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 0, 2); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 2); + + t.remove_track(1); + QCOMPARE(t.count(), 2); + QCOMPARE(trans.get_int("a_track"), 0); + QCOMPARE(trans.get_int("b_track"), 1); + } + + void RemoveTrackAboveAdjustsTransition() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + Producer p3(profile, "noise"); + QVERIFY(p3.is_valid()); + t.set_track(p3, t.count()); + + Transition trans(profile, "mix"); + QVERIFY(trans.is_valid()); + t.plant_transition(trans, 1, 2); + QCOMPARE(trans.get_int("a_track"), 1); + QCOMPARE(trans.get_int("b_track"), 2); + + t.remove_track(2); + QCOMPARE(t.count(), 2); + QCOMPARE(trans.get_int("a_track"), 1); + QCOMPARE(trans.get_int("b_track"), 1); + // This transition is a candidate for removal. + } + + void PlantFilterWorks() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 0); + QCOMPARE(filter.get_track(), 0); + } + + void InsertTrackBelowAdjustsFilter() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 0); + QCOMPARE(filter.get_track(), 0); + + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.insert_track(p2, 0); + QCOMPARE(t.count(), 2); + QCOMPARE(filter.get_track(), 1); + } + + void InsertTrackAboveDoesNotAffectFilter() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 0); + QCOMPARE(filter.get_track(), 0); + + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.insert_track(p2, 1); + QCOMPARE(t.count(), 2); + QCOMPARE(filter.get_track(), 0); + } + + void RemoveTrackBelowAdjustsFilter() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 1); + QCOMPARE(filter.get_track(), 1); + + t.remove_track(0); + QCOMPARE(t.count(), 1); + QCOMPARE(filter.get_track(), 0); + } + + void RemoveFilteredTrackAdjustsFilter() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 1); + QCOMPARE(filter.get_track(), 1); + + t.remove_track(1); + QCOMPARE(t.count(), 1); + QCOMPARE(filter.get_track(), 0); + // This filter is a candidate for removal. + } + + void RemoveTrackAboveDoesNotAffectFilter() + { + Tractor t(profile); + QVERIFY(t.is_valid()); + Producer p1(profile, "noise"); + QVERIFY(p1.is_valid()); + QCOMPARE(t.count(), 0); + t.set_track(p1, t.count()); + QCOMPARE(t.count(), 1); + Producer p2(profile, "noise"); + QVERIFY(p2.is_valid()); + t.set_track(p2, t.count()); + QCOMPARE(t.count(), 2); + + Filter filter(profile, "crop"); + QVERIFY(filter.is_valid()); + t.plant_filter(filter, 0); + QCOMPARE(filter.get_track(), 0); + + t.remove_track(1); + QCOMPARE(t.count(), 1); + QCOMPARE(filter.get_track(), 0); + } +}; + +QTEST_APPLESS_MAIN(TestTractor) + +#include "test_tractor.moc" diff -Nru mlt-0.9.8/src/tests/test_tractor/test_tractor.pro mlt-6.0.0/src/tests/test_tractor/test_tractor.pro --- mlt-0.9.8/src/tests/test_tractor/test_tractor.pro 1970-01-01 00:00:00.000000000 +0000 +++ mlt-6.0.0/src/tests/test_tractor/test_tractor.pro 2016-02-17 23:43:24.000000000 +0000 @@ -0,0 +1,3 @@ +include(../common.pri) +TARGET = test_tractor +SOURCES += test_tractor.cpp diff -Nru mlt-0.9.8/src/win32/win32.c mlt-6.0.0/src/win32/win32.c --- mlt-0.9.8/src/win32/win32.c 2015-07-29 23:04:39.000000000 +0000 +++ mlt-6.0.0/src/win32/win32.c 2016-02-17 23:43:24.000000000 +0000 @@ -2,7 +2,7 @@ * \file win32.c * \brief Miscellaneous utility functions for Windows. * - * Copyright (C) 2003-2014 Meltytech, LLC + * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +28,7 @@ #include #include #include "../framework/mlt_properties.h" +#include "../framework/mlt_log.h" int usleep(unsigned int useconds) { @@ -68,7 +69,7 @@ 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 ); + char *text = mlt_properties_get( properties, prop_name ); int result = -1; iconv_t cd = iconv_open( encoding, "UTF-8" ); @@ -88,33 +89,74 @@ mlt_pool_release( outbuf ); result = 0; } - iconv_close( cd ); + if ( cd != (iconv_t) -1 ) + iconv_close( cd ); + return result; +} + +static int iconv_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) +{ + char *text = mlt_properties_get( properties, prop_name ); + int result = -1; + + iconv_t cd = iconv_open( "UTF-8", encoding ); + 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; + } + if ( cd != (iconv_t) -1 ) + 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 ); - } + UINT codepage = GetACP(); + + if ( codepage > 0 ) { + // numeric code page + char codepage_str[10]; + snprintf( codepage_str, sizeof(codepage_str), "CP%u", codepage ); + codepage_str[sizeof(codepage_str) - 1] = '\0'; + result = iconv_from_utf8( properties, prop_name, prop_name_out, codepage_str ); + } + if ( result < 0 ) { + result = mlt_properties_set( properties, prop_name_out, + mlt_properties_get( properties, prop_name ) ); + mlt_log_warning( NULL, "iconv failed to convert \"%s\" from UTF-8 to code page %u\n", prop_name, codepage ); + } + return result; +} + +int mlt_properties_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) +{ + int result = -1; + UINT codepage = GetACP(); + + if ( codepage > 0 ) { + // numeric code page + char codepage_str[10]; + snprintf( codepage_str, sizeof(codepage_str), "CP%u", codepage ); + codepage_str[sizeof(codepage_str) - 1] = '\0'; + result = iconv_to_utf8( properties, prop_name, prop_name_out, codepage_str ); } if ( result < 0 ) { result = mlt_properties_set( properties, prop_name_out, mlt_properties_get( properties, prop_name ) ); + mlt_log_warning( NULL, "iconv failed to convert \"%s\" from code page %u to UTF-8\n", prop_name, codepage ); } return result; }