]> git.sesse.net Git - mlt/commitdiff
Merge ../mlt++
authorDan Dennedy <dan@dennedy.org>
Fri, 8 May 2009 02:47:17 +0000 (19:47 -0700)
committerDan Dennedy <dan@dennedy.org>
Fri, 8 May 2009 02:47:17 +0000 (19:47 -0700)
472 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Doxyfile [new file with mode: 0644]
GPL [new file with mode: 0644]
Makefile [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
configure [new file with mode: 0755]
demo/README [new file with mode: 0644]
demo/circle.png [new file with mode: 0644]
demo/circle.svg [new file with mode: 0644]
demo/consumers.ini [new file with mode: 0644]
demo/demo [new file with mode: 0755]
demo/demo.ini [new file with mode: 0644]
demo/demo.kino [new file with mode: 0644]
demo/entity.westley [new file with mode: 0644]
demo/luma1.pgm [new file with mode: 0644]
demo/mlt_all [new file with mode: 0644]
demo/mlt_attributes [new file with mode: 0644]
demo/mlt_audio_stuff [new file with mode: 0644]
demo/mlt_avantika_title [new file with mode: 0644]
demo/mlt_bouncy [new file with mode: 0644]
demo/mlt_bouncy_ball [new file with mode: 0644]
demo/mlt_clock_in_and_out [new file with mode: 0644]
demo/mlt_composite_transition [new file with mode: 0644]
demo/mlt_effect_in_middle [new file with mode: 0644]
demo/mlt_fade_black [new file with mode: 0644]
demo/mlt_fade_in_and_out [new file with mode: 0644]
demo/mlt_intro [new file with mode: 0644]
demo/mlt_jcut [new file with mode: 0644]
demo/mlt_lcut [new file with mode: 0644]
demo/mlt_levels [new file with mode: 0644]
demo/mlt_my_name_is [new file with mode: 0644]
demo/mlt_news [new file with mode: 0644]
demo/mlt_obscure [new file with mode: 0644]
demo/mlt_push [new file with mode: 0644]
demo/mlt_slideshow [new file with mode: 0644]
demo/mlt_slideshow_black [new file with mode: 0644]
demo/mlt_squeeze [new file with mode: 0644]
demo/mlt_squeeze_box [new file with mode: 0644]
demo/mlt_ticker [new file with mode: 0644]
demo/mlt_title_over_gfx [new file with mode: 0644]
demo/mlt_titleshadow_watermark [new file with mode: 0644]
demo/mlt_voiceover [new file with mode: 0644]
demo/mlt_watermark [new file with mode: 0644]
demo/new.westley [new file with mode: 0644]
demo/pango.westley [new file with mode: 0644]
demo/svg.westley [new file with mode: 0644]
demo/watermark1.png [new file with mode: 0644]
docs/TODO [new file with mode: 0644]
docs/dvcp.txt [new file with mode: 0644]
docs/framework.txt [new file with mode: 0644]
docs/inigo.txt [new file with mode: 0644]
docs/install.txt [new file with mode: 0644]
docs/policies.txt [new file with mode: 0644]
docs/services.txt [new file with mode: 0644]
docs/testing-20040110.txt [new file with mode: 0644]
docs/testing.txt [new file with mode: 0644]
docs/valerie.txt [new file with mode: 0644]
docs/westley.txt [new file with mode: 0644]
mlt-config-template [new file with mode: 0644]
mlt-framework.pc.in [new file with mode: 0644]
mlt-miracle.pc.in [new file with mode: 0644]
mlt-valerie.pc.in [new file with mode: 0644]
profiles/Makefile [new file with mode: 0644]
profiles/atsc_1080i_50 [new file with mode: 0644]
profiles/atsc_1080i_60 [new file with mode: 0644]
profiles/atsc_1080p_2398 [new file with mode: 0644]
profiles/atsc_1080p_24 [new file with mode: 0644]
profiles/atsc_1080p_25 [new file with mode: 0644]
profiles/atsc_1080p_2997 [new file with mode: 0644]
profiles/atsc_1080p_30 [new file with mode: 0644]
profiles/atsc_720p_30 [new file with mode: 0644]
profiles/cif_15 [new file with mode: 0644]
profiles/cif_ntsc [new file with mode: 0644]
profiles/cif_pal [new file with mode: 0644]
profiles/cvd_ntsc [new file with mode: 0644]
profiles/cvd_pal [new file with mode: 0644]
profiles/dv_ntsc [new file with mode: 0644]
profiles/dv_ntsc_wide [new file with mode: 0644]
profiles/dv_pal [new file with mode: 0644]
profiles/dv_pal_wide [new file with mode: 0644]
profiles/hdv_1080_25p [new file with mode: 0644]
profiles/hdv_1080_30p [new file with mode: 0644]
profiles/hdv_1080_50i [new file with mode: 0644]
profiles/hdv_1080_60i [new file with mode: 0644]
profiles/hdv_720_25p [new file with mode: 0644]
profiles/hdv_720_30p [new file with mode: 0644]
profiles/hdv_720_50p [new file with mode: 0644]
profiles/hdv_720_60p [new file with mode: 0644]
profiles/qcif_15 [new file with mode: 0644]
profiles/qcif_ntsc [new file with mode: 0644]
profiles/qcif_pal [new file with mode: 0644]
profiles/quarter_15 [new file with mode: 0644]
profiles/quarter_ntsc [new file with mode: 0644]
profiles/quarter_ntsc_wide [new file with mode: 0644]
profiles/quarter_pal [new file with mode: 0644]
profiles/quarter_pal_wide [new file with mode: 0644]
profiles/square_ntsc [new file with mode: 0644]
profiles/square_ntsc_wide [new file with mode: 0644]
profiles/square_pal [new file with mode: 0644]
profiles/square_pal_wide [new file with mode: 0644]
profiles/svcd_ntsc [new file with mode: 0644]
profiles/svcd_ntsc_wide [new file with mode: 0644]
profiles/svcd_pal [new file with mode: 0644]
profiles/svcd_pal_wide [new file with mode: 0644]
profiles/vcd_ntsc [new file with mode: 0644]
profiles/vcd_pal [new file with mode: 0644]
setenv [new file with mode: 0644]
setenv_mc [new file with mode: 0644]
src/albino/Makefile [new file with mode: 0644]
src/albino/albino.c [new file with mode: 0644]
src/framework/Makefile [new file with mode: 0644]
src/framework/configure [new file with mode: 0755]
src/framework/metaschema.yaml [new file with mode: 0644]
src/framework/mlt.h [new file with mode: 0644]
src/framework/mlt_cache.c [new file with mode: 0644]
src/framework/mlt_cache.h [new file with mode: 0644]
src/framework/mlt_consumer.c [new file with mode: 0644]
src/framework/mlt_consumer.h [new file with mode: 0644]
src/framework/mlt_deque.c [new file with mode: 0644]
src/framework/mlt_deque.h [new file with mode: 0644]
src/framework/mlt_events.c [new file with mode: 0644]
src/framework/mlt_events.h [new file with mode: 0644]
src/framework/mlt_factory.c [new file with mode: 0644]
src/framework/mlt_factory.h [new file with mode: 0644]
src/framework/mlt_field.c [new file with mode: 0644]
src/framework/mlt_field.h [new file with mode: 0644]
src/framework/mlt_filter.c [new file with mode: 0644]
src/framework/mlt_filter.h [new file with mode: 0644]
src/framework/mlt_frame.c [new file with mode: 0644]
src/framework/mlt_frame.h [new file with mode: 0644]
src/framework/mlt_geometry.c [new file with mode: 0644]
src/framework/mlt_geometry.h [new file with mode: 0644]
src/framework/mlt_log.c [new file with mode: 0644]
src/framework/mlt_log.h [new file with mode: 0644]
src/framework/mlt_multitrack.c [new file with mode: 0644]
src/framework/mlt_multitrack.h [new file with mode: 0644]
src/framework/mlt_parser.c [new file with mode: 0644]
src/framework/mlt_parser.h [new file with mode: 0644]
src/framework/mlt_playlist.c [new file with mode: 0644]
src/framework/mlt_playlist.h [new file with mode: 0644]
src/framework/mlt_pool.c [new file with mode: 0644]
src/framework/mlt_pool.h [new file with mode: 0644]
src/framework/mlt_producer.c [new file with mode: 0644]
src/framework/mlt_producer.h [new file with mode: 0644]
src/framework/mlt_profile.c [new file with mode: 0644]
src/framework/mlt_profile.h [new file with mode: 0644]
src/framework/mlt_properties.c [new file with mode: 0644]
src/framework/mlt_properties.h [new file with mode: 0644]
src/framework/mlt_property.c [new file with mode: 0644]
src/framework/mlt_property.h [new file with mode: 0644]
src/framework/mlt_repository.c [new file with mode: 0644]
src/framework/mlt_repository.h [new file with mode: 0644]
src/framework/mlt_service.c [new file with mode: 0644]
src/framework/mlt_service.h [new file with mode: 0644]
src/framework/mlt_tokeniser.c [new file with mode: 0644]
src/framework/mlt_tokeniser.h [new file with mode: 0644]
src/framework/mlt_tractor.c [new file with mode: 0644]
src/framework/mlt_tractor.h [new file with mode: 0644]
src/framework/mlt_transition.c [new file with mode: 0644]
src/framework/mlt_transition.h [new file with mode: 0644]
src/framework/mlt_types.h [new file with mode: 0644]
src/humperdink/Makefile [new file with mode: 0644]
src/humperdink/client.c [new file with mode: 0644]
src/humperdink/client.h [new file with mode: 0644]
src/humperdink/io.c [new file with mode: 0644]
src/humperdink/io.h [new file with mode: 0644]
src/humperdink/remote.c [new file with mode: 0644]
src/inigo/Makefile [new file with mode: 0644]
src/inigo/configure [new file with mode: 0755]
src/inigo/inigo.c [new file with mode: 0644]
src/inigo/io.c [new file with mode: 0644]
src/inigo/io.h [new file with mode: 0644]
src/miracle/Makefile [new file with mode: 0644]
src/miracle/configure [new file with mode: 0755]
src/miracle/miracle.c [new file with mode: 0644]
src/miracle/miracle_commands.c [new file with mode: 0644]
src/miracle/miracle_commands.h [new file with mode: 0644]
src/miracle/miracle_connection.c [new file with mode: 0644]
src/miracle/miracle_connection.h [new file with mode: 0644]
src/miracle/miracle_local.c [new file with mode: 0644]
src/miracle/miracle_local.h [new file with mode: 0644]
src/miracle/miracle_log.c [new file with mode: 0644]
src/miracle/miracle_log.h [new file with mode: 0644]
src/miracle/miracle_server.c [new file with mode: 0644]
src/miracle/miracle_server.h [new file with mode: 0644]
src/miracle/miracle_unit.c [new file with mode: 0644]
src/miracle/miracle_unit.h [new file with mode: 0644]
src/miracle/miracle_unit_commands.c [new file with mode: 0644]
src/miracle/miracle_unit_commands.h [new file with mode: 0644]
src/modules/Makefile [new file with mode: 0644]
src/modules/avformat/Makefile [new file with mode: 0644]
src/modules/avformat/audioconvert.h [new file with mode: 0644]
src/modules/avformat/configure [new file with mode: 0755]
src/modules/avformat/consumer_avformat.c [new file with mode: 0644]
src/modules/avformat/factory.c [new file with mode: 0644]
src/modules/avformat/filter_avcolour_space.c [new file with mode: 0644]
src/modules/avformat/filter_avdeinterlace.c [new file with mode: 0644]
src/modules/avformat/filter_avresample.c [new file with mode: 0644]
src/modules/avformat/filter_swscale.c [new file with mode: 0644]
src/modules/avformat/mmx.h [new file with mode: 0644]
src/modules/avformat/producer_avformat.c [new file with mode: 0644]
src/modules/avformat/producer_avformat.yml [new file with mode: 0644]
src/modules/configure [new file with mode: 0755]
src/modules/core/Makefile [new file with mode: 0644]
src/modules/core/composite_line_yuv_mmx.S [new file with mode: 0644]
src/modules/core/consumer_null.c [new file with mode: 0644]
src/modules/core/factory.c [new file with mode: 0644]
src/modules/core/filter_brightness.c [new file with mode: 0644]
src/modules/core/filter_channelcopy.c [new file with mode: 0644]
src/modules/core/filter_crop.c [new file with mode: 0644]
src/modules/core/filter_data_feed.c [new file with mode: 0644]
src/modules/core/filter_data_show.c [new file with mode: 0644]
src/modules/core/filter_gamma.c [new file with mode: 0644]
src/modules/core/filter_greyscale.c [new file with mode: 0644]
src/modules/core/filter_luma.c [new file with mode: 0644]
src/modules/core/filter_mirror.c [new file with mode: 0644]
src/modules/core/filter_mono.c [new file with mode: 0644]
src/modules/core/filter_obscure.c [new file with mode: 0644]
src/modules/core/filter_region.c [new file with mode: 0644]
src/modules/core/filter_rescale.c [new file with mode: 0644]
src/modules/core/filter_resize.c [new file with mode: 0644]
src/modules/core/filter_transition.c [new file with mode: 0644]
src/modules/core/filter_watermark.c [new file with mode: 0644]
src/modules/core/producer_colour.c [new file with mode: 0644]
src/modules/core/producer_consumer.c [new file with mode: 0644]
src/modules/core/producer_noise.c [new file with mode: 0644]
src/modules/core/producer_ppm.c [new file with mode: 0644]
src/modules/core/transition_composite.c [new file with mode: 0644]
src/modules/core/transition_composite.h [new file with mode: 0644]
src/modules/core/transition_luma.c [new file with mode: 0644]
src/modules/core/transition_mix.c [new file with mode: 0644]
src/modules/core/transition_region.c [new file with mode: 0644]
src/modules/core/transition_region.h [new file with mode: 0644]
src/modules/data_fx.properties [new file with mode: 0644]
src/modules/dgraft/Makefile [new file with mode: 0644]
src/modules/dgraft/factory.c [new file with mode: 0644]
src/modules/dgraft/filter_telecide.c [new file with mode: 0644]
src/modules/dgraft/gpl [new file with mode: 0644]
src/modules/dv/Makefile [new file with mode: 0644]
src/modules/dv/configure [new file with mode: 0755]
src/modules/dv/consumer_libdv.c [new file with mode: 0644]
src/modules/dv/factory.c [new file with mode: 0644]
src/modules/dv/producer_libdv.c [new file with mode: 0644]
src/modules/effectv/Makefile [new file with mode: 0644]
src/modules/effectv/factory.c [new file with mode: 0644]
src/modules/effectv/filter_burn.c [new file with mode: 0644]
src/modules/effectv/gpl [new file with mode: 0644]
src/modules/effectv/image.c [new file with mode: 0644]
src/modules/effectv/utils.c [new file with mode: 0644]
src/modules/effectv/utils.h [new file with mode: 0644]
src/modules/feeds/Makefile [new file with mode: 0644]
src/modules/feeds/NTSC/data_fx.properties [new file with mode: 0644]
src/modules/feeds/NTSC/obscure.properties [new file with mode: 0644]
src/modules/feeds/PAL/border.properties [new file with mode: 0644]
src/modules/feeds/PAL/data_fx.properties [new file with mode: 0644]
src/modules/feeds/PAL/etv.properties [new file with mode: 0644]
src/modules/feeds/PAL/example.properties [new file with mode: 0644]
src/modules/feeds/PAL/obscure.properties [new file with mode: 0644]
src/modules/fezzik.dict [new file with mode: 0644]
src/modules/fezzik.ini [new file with mode: 0644]
src/modules/fezzik/Makefile [new file with mode: 0644]
src/modules/fezzik/factory.c [new file with mode: 0644]
src/modules/fezzik/producer_fezzik.c [new file with mode: 0644]
src/modules/fezzik/producer_hold.c [new file with mode: 0644]
src/modules/frei0r/Makefile [new file with mode: 0644]
src/modules/frei0r/configure [new file with mode: 0755]
src/modules/frei0r/factory.c [new file with mode: 0644]
src/modules/frei0r/filter_frei0r.c [new file with mode: 0644]
src/modules/frei0r/frei0r_helper.c [new file with mode: 0644]
src/modules/frei0r/frei0r_helper.h [new file with mode: 0644]
src/modules/frei0r/producer_frei0r.c [new file with mode: 0644]
src/modules/frei0r/transition_frei0r.c [new file with mode: 0644]
src/modules/gtk2/Makefile [new file with mode: 0644]
src/modules/gtk2/configure [new file with mode: 0755]
src/modules/gtk2/consumer_gtk2.c [new file with mode: 0644]
src/modules/gtk2/factory.c [new file with mode: 0644]
src/modules/gtk2/filter_rescale.c [new file with mode: 0644]
src/modules/gtk2/have_mmx.S [new file with mode: 0644]
src/modules/gtk2/pixops.c [new file with mode: 0644]
src/modules/gtk2/pixops.h [new file with mode: 0644]
src/modules/gtk2/producer_pango.c [new file with mode: 0644]
src/modules/gtk2/producer_pixbuf.c [new file with mode: 0644]
src/modules/gtk2/scale_line_22_yuv_mmx.S [new file with mode: 0644]
src/modules/inigo/Makefile [new file with mode: 0644]
src/modules/inigo/factory.c [new file with mode: 0644]
src/modules/inigo/producer_inigo.c [new file with mode: 0644]
src/modules/jackrack/Makefile [new file with mode: 0644]
src/modules/jackrack/configure [new file with mode: 0755]
src/modules/jackrack/factory.c [new file with mode: 0644]
src/modules/jackrack/filter_jackrack.c [new file with mode: 0644]
src/modules/jackrack/filter_ladspa.c [new file with mode: 0644]
src/modules/jackrack/gpl [new file with mode: 0644]
src/modules/jackrack/jack_rack.c [new file with mode: 0644]
src/modules/jackrack/jack_rack.h [new file with mode: 0644]
src/modules/jackrack/lock_free_fifo.c [new file with mode: 0644]
src/modules/jackrack/lock_free_fifo.h [new file with mode: 0644]
src/modules/jackrack/plugin.c [new file with mode: 0644]
src/modules/jackrack/plugin.h [new file with mode: 0644]
src/modules/jackrack/plugin_desc.c [new file with mode: 0644]
src/modules/jackrack/plugin_desc.h [new file with mode: 0644]
src/modules/jackrack/plugin_mgr.c [new file with mode: 0644]
src/modules/jackrack/plugin_mgr.h [new file with mode: 0644]
src/modules/jackrack/plugin_settings.c [new file with mode: 0644]
src/modules/jackrack/plugin_settings.h [new file with mode: 0644]
src/modules/jackrack/process.c [new file with mode: 0644]
src/modules/jackrack/process.h [new file with mode: 0644]
src/modules/kdenlive/Makefile [new file with mode: 0644]
src/modules/kdenlive/factory.c [new file with mode: 0644]
src/modules/kdenlive/filter_boxblur.c [new file with mode: 0644]
src/modules/kdenlive/filter_freeze.c [new file with mode: 0644]
src/modules/kdenlive/filter_wave.c [new file with mode: 0644]
src/modules/kdenlive/producer_framebuffer.c [new file with mode: 0644]
src/modules/kino/Makefile [new file with mode: 0644]
src/modules/kino/avi.cc [new file with mode: 0644]
src/modules/kino/avi.h [new file with mode: 0644]
src/modules/kino/configure [new file with mode: 0755]
src/modules/kino/endian_types.h [new file with mode: 0644]
src/modules/kino/error.cc [new file with mode: 0644]
src/modules/kino/error.h [new file with mode: 0644]
src/modules/kino/factory.c [new file with mode: 0644]
src/modules/kino/filehandler.cc [new file with mode: 0644]
src/modules/kino/filehandler.h [new file with mode: 0644]
src/modules/kino/gpl [new file with mode: 0644]
src/modules/kino/kino_wrapper.cc [new file with mode: 0644]
src/modules/kino/kino_wrapper.h [new file with mode: 0644]
src/modules/kino/producer_kino.c [new file with mode: 0644]
src/modules/kino/riff.cc [new file with mode: 0644]
src/modules/kino/riff.h [new file with mode: 0644]
src/modules/lumas/Makefile [new file with mode: 0644]
src/modules/lumas/configure [new file with mode: 0755]
src/modules/lumas/create_lumas [new file with mode: 0755]
src/modules/lumas/luma.c [new file with mode: 0644]
src/modules/motion_est/Makefile [new file with mode: 0644]
src/modules/motion_est/README [new file with mode: 0644]
src/modules/motion_est/arrow_code.c [new file with mode: 0644]
src/modules/motion_est/arrow_code.h [new file with mode: 0644]
src/modules/motion_est/factory.c [new file with mode: 0644]
src/modules/motion_est/filter_autotrack_rectangle.c [new file with mode: 0644]
src/modules/motion_est/filter_crop_detect.c [new file with mode: 0644]
src/modules/motion_est/filter_motion_est.c [new file with mode: 0644]
src/modules/motion_est/filter_motion_est.h [new file with mode: 0644]
src/modules/motion_est/filter_vismv.c [new file with mode: 0644]
src/modules/motion_est/gpl [new file with mode: 0644]
src/modules/motion_est/producer_slowmotion.c [new file with mode: 0644]
src/modules/motion_est/sad_sse.h [new file with mode: 0644]
src/modules/normalize/Makefile [new file with mode: 0644]
src/modules/normalize/factory.c [new file with mode: 0644]
src/modules/normalize/filter_volume.c [new file with mode: 0644]
src/modules/normalize/gpl [new file with mode: 0644]
src/modules/oldfilm/Makefile [new file with mode: 0644]
src/modules/oldfilm/dust1.svg [new file with mode: 0644]
src/modules/oldfilm/dust2.svg [new file with mode: 0644]
src/modules/oldfilm/dust3.svg [new file with mode: 0644]
src/modules/oldfilm/dust4.svg [new file with mode: 0644]
src/modules/oldfilm/dust5.svg [new file with mode: 0644]
src/modules/oldfilm/factory.c [new file with mode: 0644]
src/modules/oldfilm/fdust.svg [new file with mode: 0644]
src/modules/oldfilm/filter_dust.c [new file with mode: 0644]
src/modules/oldfilm/filter_dust.yml [new file with mode: 0644]
src/modules/oldfilm/filter_grain.c [new file with mode: 0644]
src/modules/oldfilm/filter_grain.yml [new file with mode: 0644]
src/modules/oldfilm/filter_lines.c [new file with mode: 0644]
src/modules/oldfilm/filter_lines.yml [new file with mode: 0644]
src/modules/oldfilm/filter_oldfilm.c [new file with mode: 0644]
src/modules/oldfilm/filter_oldfilm.yml [new file with mode: 0644]
src/modules/oldfilm/filter_tcolor.c [new file with mode: 0644]
src/modules/oldfilm/filter_tcolor.yml [new file with mode: 0644]
src/modules/oldfilm/filter_vignette.c [new file with mode: 0644]
src/modules/oldfilm/filter_vignette.yml [new file with mode: 0644]
src/modules/oldfilm/grain.svg [new file with mode: 0644]
src/modules/oldfilm/lines.svg [new file with mode: 0644]
src/modules/oldfilm/oldfilm.svg [new file with mode: 0644]
src/modules/oldfilm/tcolor.svg [new file with mode: 0644]
src/modules/oldfilm/vignette.svg [new file with mode: 0644]
src/modules/plus/Makefile [new file with mode: 0644]
src/modules/plus/factory.c [new file with mode: 0644]
src/modules/plus/filter_affine.c [new file with mode: 0644]
src/modules/plus/filter_charcoal.c [new file with mode: 0644]
src/modules/plus/filter_invert.c [new file with mode: 0644]
src/modules/plus/filter_sepia.c [new file with mode: 0644]
src/modules/plus/transition_affine.c [new file with mode: 0644]
src/modules/qimage/Makefile [new file with mode: 0644]
src/modules/qimage/configure [new file with mode: 0755]
src/modules/qimage/factory.c [new file with mode: 0644]
src/modules/qimage/gpl [new file with mode: 0644]
src/modules/qimage/producer_qimage.c [new file with mode: 0644]
src/modules/qimage/qimage_wrapper.cpp [new file with mode: 0644]
src/modules/qimage/qimage_wrapper.h [new file with mode: 0644]
src/modules/resample/Makefile [new file with mode: 0644]
src/modules/resample/configure [new file with mode: 0755]
src/modules/resample/factory.c [new file with mode: 0644]
src/modules/resample/filter_resample.c [new file with mode: 0644]
src/modules/resample/gpl [new file with mode: 0644]
src/modules/sdl/Makefile [new file with mode: 0644]
src/modules/sdl/configure [new file with mode: 0755]
src/modules/sdl/consumer_sdl.c [new file with mode: 0644]
src/modules/sdl/consumer_sdl_osx_hack.h [new file with mode: 0644]
src/modules/sdl/consumer_sdl_preview.c [new file with mode: 0644]
src/modules/sdl/consumer_sdl_still.c [new file with mode: 0644]
src/modules/sdl/factory.c [new file with mode: 0644]
src/modules/sdl/producer_sdl_image.c [new file with mode: 0644]
src/modules/sox/Makefile [new file with mode: 0644]
src/modules/sox/configure [new file with mode: 0755]
src/modules/sox/factory.c [new file with mode: 0644]
src/modules/sox/filter_sox.c [new file with mode: 0644]
src/modules/valerie/Makefile [new file with mode: 0644]
src/modules/valerie/consumer_valerie.c [new file with mode: 0644]
src/modules/valerie/factory.c [new file with mode: 0644]
src/modules/vmfx/Makefile [new file with mode: 0644]
src/modules/vmfx/factory.c [new file with mode: 0644]
src/modules/vmfx/filter_chroma.c [new file with mode: 0644]
src/modules/vmfx/filter_chroma_hold.c [new file with mode: 0644]
src/modules/vmfx/filter_mono.c [new file with mode: 0644]
src/modules/vmfx/filter_shape.c [new file with mode: 0644]
src/modules/vmfx/producer_pgm.c [new file with mode: 0644]
src/modules/vorbis/Makefile [new file with mode: 0644]
src/modules/vorbis/configure [new file with mode: 0755]
src/modules/vorbis/factory.c [new file with mode: 0644]
src/modules/vorbis/producer_vorbis.c [new file with mode: 0644]
src/modules/westley/Makefile [new file with mode: 0644]
src/modules/westley/configure [new file with mode: 0755]
src/modules/westley/consumer_westley.c [new file with mode: 0644]
src/modules/westley/factory.c [new file with mode: 0644]
src/modules/westley/producer_westley.c [new file with mode: 0644]
src/modules/westley/westley.dtd [new file with mode: 0644]
src/modules/xine/Makefile [new file with mode: 0644]
src/modules/xine/attributes.h [new file with mode: 0644]
src/modules/xine/cpu_accel.c [new file with mode: 0644]
src/modules/xine/deinterlace.c [new file with mode: 0644]
src/modules/xine/deinterlace.h [new file with mode: 0644]
src/modules/xine/factory.c [new file with mode: 0644]
src/modules/xine/filter_deinterlace.c [new file with mode: 0644]
src/modules/xine/gpl [new file with mode: 0644]
src/modules/xine/xineutils.h [new file with mode: 0644]
src/tests/Makefile [new file with mode: 0644]
src/tests/charlie.c [new file with mode: 0644]
src/tests/clock16ntsc.pgm [new file with mode: 0644]
src/tests/clock16pal.pgm [new file with mode: 0644]
src/tests/dan.c [new file with mode: 0644]
src/tests/dissolve.c [new file with mode: 0644]
src/tests/hello.c [new file with mode: 0644]
src/tests/io.c [new file with mode: 0644]
src/tests/io.h [new file with mode: 0644]
src/tests/luma.c [new file with mode: 0644]
src/tests/pango.c [new file with mode: 0644]
src/tests/pixbuf.c [new file with mode: 0644]
src/tests/setenv [new file with mode: 0644]
src/tests/test.png [new file with mode: 0644]
src/valerie/Makefile [new file with mode: 0644]
src/valerie/configure [new file with mode: 0755]
src/valerie/valerie.c [new file with mode: 0644]
src/valerie/valerie.h [new file with mode: 0644]
src/valerie/valerie_notifier.c [new file with mode: 0644]
src/valerie/valerie_notifier.h [new file with mode: 0644]
src/valerie/valerie_parser.c [new file with mode: 0644]
src/valerie/valerie_parser.h [new file with mode: 0644]
src/valerie/valerie_remote.c [new file with mode: 0644]
src/valerie/valerie_remote.h [new file with mode: 0644]
src/valerie/valerie_response.c [new file with mode: 0644]
src/valerie/valerie_response.h [new file with mode: 0644]
src/valerie/valerie_socket.c [new file with mode: 0644]
src/valerie/valerie_socket.h [new file with mode: 0644]
src/valerie/valerie_status.c [new file with mode: 0644]
src/valerie/valerie_status.h [new file with mode: 0644]
src/valerie/valerie_tokeniser.c [new file with mode: 0644]
src/valerie/valerie_tokeniser.h [new file with mode: 0644]
src/valerie/valerie_util.c [new file with mode: 0644]
src/valerie/valerie_util.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..63f6b59
--- /dev/null
@@ -0,0 +1,6 @@
+*.o
+*.so
+*.so.*
+config.mak
+config.h
+.depend
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..89e7d76
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,14 @@
+MLT framework was developed by:
+Charles Yates <charles.yates@pandora.be>
+
+MLT framework is maintained by:
+Dan Dennedy <dan@dennedy.org>
+
+MLT module authors and maintainers:
+
+Charles Yates <charles.yates@pandora.be>
+Dan Dennedy <dan@dennedy.org>
+Stephane Fillod (effectv)
+Marco Gittler <g.marco@freenet.de> (frei0r, oldfilm)
+Jean-Baptiste Mardelle <jb@ader.ch> (kdenlive, qimage)
+Zachary Drew (motion_est)
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..223ede7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e8e459f
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,6664 @@
+2009-04-14  Dan Dennedy <dan@dennedy.org>
+
+  * src/modules/avformat/configure: Fix build for --avformat-svn to use FFmpeg
+  v0.5 and HEAD build to not use --enable-swscale.
+
+2009-04-15  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, Makefile: Update ChangeLog and remove svn log from the make
+  install target. 
+
+  * NEWS, configure, src/framework/mlt.h, src/modules/avformat/configure: bump
+  to version 0.3.8  
+
+2009-04-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.ini: fezzik.ini: workaround scaling resolution
+  limitation with swscale filter by making it the lowest priority 
+
+  * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c:
+  interpret negative speed as reverse 
+
+2009-04-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  building on some older versions. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat: bugfix
+  (kdenlive-677) to make interlaced coding automatic if profile is not
+  progressive and coding not explicit by ildct and ilme properties. 
+
+2009-04-07  Ray Lehtiniemi <rayl@mail.com>
+
+  * src/modules/kdenlive/filter_boxblur.c: Fix a 64-bit segfault in kdenlive 
+  To reproduce: - create a new project - create a color clip - add clip to
+  timeline - set an in point on the clip - add the box blur effect  The
+  segfault happens because we take the negative of an unsigned integer. This
+  works out to a signed 32 bit value on a 64 bit platform, which causes the rgb
+  array bounds to be exceeded. 
+
+  * src/framework/mlt_consumer.c, src/miracle/miracle_connection.c,
+  src/modules/kino/riff.cc: Fix up a few ignored return values 
+
+  * src/framework/mlt_pool.c: Fix warning: pointer of type â€˜void *’ used in
+  arithmetic 
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c, src/modules/westley/producer_westley.c:
+  Constness changes 
+
+  * src/framework/mlt_properties.c, src/humperdink/client.c,
+  src/miracle/miracle_connection.c, src/modules/avformat/consumer_avformat.c,
+  src/modules/core/filter_data_show.c, src/modules/kino/filehandler.cc,
+  src/valerie/valerie_response.c, src/valerie/valerie_response.h: Constness
+  changes 
+
+  * src/framework/mlt_tokeniser.c, src/framework/mlt_tokeniser.h,
+  src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+  src/valerie/valerie.c, src/valerie/valerie.h: Constness changes 
+
+  * src/humperdink/io.c, src/humperdink/io.h,
+  src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c,
+  src/modules/westley/consumer_westley.c, src/valerie/valerie.c,
+  src/valerie/valerie.h, src/valerie/valerie_parser.c,
+  src/valerie/valerie_parser.h, src/valerie/valerie_socket.c,
+  src/valerie/valerie_socket.h: Constness changes 
+
+  * src/framework/mlt_events.c, src/framework/mlt_events.h, src/inigo/inigo.c,
+  src/modules/avformat/factory.c, src/modules/plus/transition_affine.c,
+  src/modules/westley/producer_westley.c, src/modules/xine/deinterlace.c,
+  src/modules/xine/deinterlace.h: Constness changes 
+
+  * src/miracle/miracle_local.c, src/valerie/valerie.c, src/valerie/valerie.h,
+  src/valerie/valerie_status.c, src/valerie/valerie_tokeniser.c,
+  src/valerie/valerie_tokeniser.h: Constness changes 
+
+  * src/humperdink/client.c, src/humperdink/io.c, src/humperdink/io.h,
+  src/miracle/miracle_log.c, src/miracle/miracle_log.h, src/valerie/valerie.c,
+  src/valerie/valerie.h, src/valerie/valerie_response.c,
+  src/valerie/valerie_response.h: Constness changes 
+
+  * src/framework/mlt_multitrack.c, src/modules/effectv/image.c,
+  src/modules/gtk2/producer_pango.c, src/modules/jackrack/jack_rack.c,
+  src/modules/motion_est/filter_motion_est.c, src/modules/xine/xineutils.h:
+  Constness changes 
+
+2009-03-31  Ray Lehtiniemi <rayl@mail.com>
+
+  * src/framework/mlt_properties.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Constness changes 
+
+2009-03-04  Ray Lehtiniemi <rayl@mail.com>
+
+  * src/framework/mlt_events.c, src/framework/mlt_events.h,
+  src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+  src/valerie/valerie_response.c, src/valerie/valerie_response.h: Constness
+  changes 
+
+  * .gitignore: Add a .gitignore file 
+
+2009-04-05  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: producer_westley.c: Don't prepend
+  westley document root to empty properties 
+
+2009-04-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_crop.c: filter_crop.c: bugfix chroma alignment 
+
+2009-03-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: add /usr/lib64 to the
+  default frei0r plugin path 
+
+2009-03-15  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: transition_composite.c: allow
+  removing of luma file by passing an empty name 
+
+2009-03-14  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: transition_composite.c: make luma
+  and luma_invert properties mutable 
+
+2009-03-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: add
+  backwards compatibility macro for PIX_FMT_YUYV422 
+
+  * 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: avformat: fix compilation due to
+  recent PIX_FMT changes in libavutil v50. 
+
+2009-03-08  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix
+  producer out position 
+
+2009-03-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_log.h: mlt_log.h: add convenience macros 
+
+2009-03-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/riff.cc: kino/riff.cc: suppress compiler warning 
+
+  * src/modules/frei0r/factory.c, src/modules/frei0r/producer_frei0r.c:
+  frei0r/factory.c, producer_frei0r.c: suppress compiler warnings 
+
+  * src/framework/mlt_property.c: mlt_property.c: suppress compiler warning 
+
+2009-02-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/producer_colour.c: producer_colour.c: improve previous
+  patch 
+
+  * src/modules/core/producer_colour.c: producer_colour.c: bugfix reading color
+  value after westley has prepended the document path to the resource property 
+
+2009-02-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: consumer_sdl*.c: apply patch from
+  Jean-Baptiste Mardelle to add window_background property 
+
+2009-02-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c:
+  filter_chroma.c: update to use new property-based color value 
+
+  * src/modules/vmfx/filter_chroma_hold.c: filter_chroma_hold.c: update to use
+  new property-based color value 
+
+  * src/modules/core/producer_colour.c: producer_colour.c: update to use new
+  property-based color parsing. 
+
+  * src/framework/mlt_property.c: mlt_property.c: interpret hex int as unsigned
+
+  * src/modules/frei0r/frei0r_helper.c: frei0r_helper.c: cleanup color parser
+  to use new code in mlt_property.c 
+
+  * src/framework/mlt_property.c: mlt_property.c: added parsing for color
+  values beginning with # 
+
+2009-02-20  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/producer_frei0r.c: modules/frei0r: added missing
+  producer_frei0r.c -This line, and those below, will be ignored--  A   
+  producer_frei0r.c  
+
+  * src/modules/frei0r/Makefile, src/modules/frei0r/factory.c,
+  src/modules/frei0r/frei0r_helper.c: added frei0r producers (patch from jb)
+  thx to jb  
+
+2009-02-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/Makefile, src/humperdink/Makefile, src/inigo/Makefile,
+  src/miracle/Makefile: albino/Makefile, inigo/Makefile, humperdink/Makefile,
+  miracle/Makefile: apply patch from Alberto Villa to fix underlinking on
+  FreeBSD  
+
+2009-02-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c:
+  frei0r/factory.c, frei0r_helper.c: add support for color parameter type with
+  whitespace cleanup courtesy of eclipse. 
+
+2009-02-14  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_affine.c: filter_affine.c: remove silly default
+  rotate animation for new kdenlive pan and zoom effect (kdenlive-565) 
+
+  * src/modules/core/Makefile, src/modules/core/factory.c,
+  src/modules/core/filter_crop.c, src/modules/fezzik.ini: filter_crop.c: add
+  cropping filter (kdenlive-509) 
+
+  * configure: configure: relax optimization level slightly to improve debugger
+  backtraces in bug reports 
+
+  * src/modules/plus/transition_affine.c: transition_affine.c: bugfix chroma
+  alignment 
+
+2009-02-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_brightness.c: filter_brightness.c: fix the
+  wonkiness by filtering chroma as well. 
+
+2009-02-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * profiles/cif_15, profiles/qcif_15, profiles/quarter_15: profiles/*_15: add
+  some 15fps profiles 
+
+  * src/modules/qimage/configure: qimage/configure: let qimage first attempt to
+  use Qt4 through pkg-config (canonical) without having to specify directories
+  or QTDIR 
+
+  * src/modules/sox/configure: sox/configure: give pkg-config priority over
+  libst-config 
+
+2009-02-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure: qimage/configure: fix build on OS X 
+
+  * src/modules/avformat/filter_avdeinterlace.c: filter_avdeinterlace.c: bugfix
+  (kdenlive-672) deinterlace only works on left half of image 
+
+  * src/modules/qimage/producer_qimage.c,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+  producer_qimage.c, qimage_wrapper.{h,cpp}: enhance qimage producer to use the
+  new mlt_cache (kdenlive-575) 
+
+  * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: enhance pixbuf
+  producer to use new mlt_cache (kdenlive-575) 
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/vorbis/producer_vorbis.c: producer_vorbis.c, producer_avformat.c,
+  consumer_avformat.c: update headers in services for framework changes with
+  addition of mlt_cache 
+
+  * configure, src/framework/Makefile, src/framework/mlt.h,
+  src/framework/mlt_cache.c, src/framework/mlt_cache.h,
+  src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_types.h: mlt_cache.[hc], mlt_types.h, mlt_service.[hc],
+  mlt_factory.[hc], mlt.h: add mlt_cache and related service functions
+  (kdenlive-575) 
+
+  * Doxyfile: Doxyfile: set tab width to 4 spaces 
+
+  * src/framework/mlt_properties.c: mlt_properties.c: update doxygen comments
+  for some out params 
+
+  * src/framework/mlt_property.c: mlt_property.c: update a doxygen comment to
+  label param as out 
+
+2009-02-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * debian/changelog, debian/control, debian/copyright, debian/rules: remove
+  the debian package subdirectory (they provide their own) 
+
+2009-02-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/framework/mlt.h, src/modules/avformat/configure: bump to
+  version 0.3.6 
+
+  * NEWS: NEWS: add release notes for 0.3.6 
+
+2009-02-01  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: add more default locations
+  for locating plugins including one for MacPorts 
+
+2009-01-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: inigo.c: make usage fit in 80 columns 
+
+2009-01-29  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/filter_swscale.c: filter_swscale.c: Fix compilation
+  (typo introduced in rev. 1330) 
+
+2009-01-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik/producer_fezzik.c: producer_fezzik.c: do not use the
+  swscale filter on images wider than 2048 loaded by the sdl_image producer. 
+
+  * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c,
+  src/modules/sdl/producer_sdl_image.c: producer_pixbuf.c, producer_qimage.c,
+  producer_sdl_image.c: bugfix (kdenlive-575) large memory consumption loading
+  many pictures. 
+
+  * src/modules/avformat/filter_swscale.c: filter_swscale.c: throw assert if
+  creation of swscale context fails. 
+
+  * src/modules/avformat/factory.c: avformat/factory.c: set ffmpeg logging to
+  the same level as MLT's 
+
+2009-01-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix
+  (kdenlive-575) memory leak 
+
+  * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix
+  (kdenlive-575) memory leak 
+
+2009-01-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: add version
+  check to use AVCodec->long_name 
+
+2009-01-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: bugfix segfault on
+  unchecked pointer 
+
+  * src/modules/inigo/producer_inigo.c: producer_inigo.c: bugfix segfault on
+  unchecked pointer 
+
+2009-01-21  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/inigo.txt: inigo.txt: update usage info 
+
+  * docs/policies.txt: policies.txt: add instruction to update version in
+  mlt.h\! 
+
+  * src/framework/mlt.h, src/framework/mlt_consumer.c,
+  src/framework/mlt_consumer.h, src/framework/mlt_deque.c,
+  src/framework/mlt_deque.h, src/framework/mlt_events.c,
+  src/framework/mlt_events.h, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_field.c,
+  src/framework/mlt_field.h, src/framework/mlt_filter.c,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_multitrack.h, src/framework/mlt_parser.c,
+  src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_profile.c,
+  src/framework/mlt_profile.h, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_property.c,
+  src/framework/mlt_property.h, src/framework/mlt_repository.c,
+  src/framework/mlt_repository.h, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_tokeniser.c,
+  src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h, src/framework/mlt_types.h: Add doxygen
+  documentation for mlt_profile, mlt_pool, mlt_repository, and mlt_factory.
+  Update copyrights to 2009. Add cross references from files to data structures
+  in doxygen. 
+
+2009-01-14  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: inigo/inigo.c: add -debug and -verbose options to turn
+  on additional logging. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: set consumer
+  buffer prefill to 1 by default. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  (kdenlive-450) bad timestamps in MPEG-2 Transport Stream and possibly quite a
+  few other formats. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: add support
+  for an=1, vn=1, acodec=none, and vcodec=none options (kdenlive-533) 
+
+2009-01-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/policies.txt: docs/policies.txt: Add policy about not using stdout,
+  messages, and recommending the new log API. 
+
+  * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_consumer.c,
+  src/framework/mlt_events.c, src/framework/mlt_log.c, src/framework/mlt_log.h,
+  src/framework/mlt_pool.c, src/framework/mlt_producer.c,
+  src/framework/mlt_properties.c, src/framework/mlt_repository.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c: mlt_log.[hc],
+  mlt_transition.c, mlt_tractor.c, mlt_repository.c, mlt_properties.c,
+  mlt_producer.c, mlt_pool.c, mlt_events.c, mlt_consumer.c, mlt.h, Makefile:
+  add logging system based on FFmpeg's. 
+
+  * configure: configure: separate -march (suitable on x86) and -mcpu (suitable
+  on ppc, arm, and sparc) 
+
+2009-01-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Doxyfile: Doxyfile: strip the path to the source files 
+
+  * src/modules/core/producer_consumer.c: producer_consumer.c: bugfix setting
+  in point 
+
+  * src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_multitrack.h, src/framework/mlt_playlist.h,
+  src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_tractor.h: mlt_tractor.[ch], mlt_multitrack.[ch]: improve
+  doxygen documentation for the tractor and mulitrack classes 
+
+2009-01-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c,
+  src/modules/avformat/producer_avformat.yml: producer_avformat.{c,yml}:
+  support special constructor argument values to list available demuxers and
+  decoders: f-list[[,]acodec-list][[,]vcodec-list] 
+
+  * src/inigo/inigo.c: inigo/inigo.c: fix the usage help within 80 characters
+  wide. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: report list
+  of muxers when f=list and codecs when acodec=list or vcodec=list. 
+
+  * src/framework/mlt_repository.c: mlt_repository.c: report reason when dlopen
+  fails. 
+
+2009-01-05  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_frame.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_multitrack.h, src/framework/mlt_producer.c,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h:
+  mlt_filter.[ch], mlt_transition.[ch], mlt_consumer.[ch]: improve doxygen for
+  filter, transition, and consumer 
+
+2009-01-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/configure: add -lbz2 automatically
+  for --avformat-svn 
+
+2008-12-31  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/modules/avformat/producer_avformat.c: producer_avformat.c:
+  fix build on older versions of ffmpeg; whitespace cleanup by eclipse.  
+
+2008-12-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Doxyfile: Doxyfile: bump version 
+
+2008-12-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * NEWS, configure: NEWS, configure: set version to 0.3.4 and add release
+  notes 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: further
+  analysis and testing reveals the DV encoder does not need the special aspect
+  ratio overrides. It expects a generic input. Only the DV decoder produces the
+  special, proper aspect ratios for which MLT is not yet prepared. 
+
+2008-12-28  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/filter_sox.c: filter_sox.c: fix crash when trying to create
+  a sox filter with wrong name 
+
+2008-12-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/configure: update the recommended
+  ffmpeg revision 
+
+  * src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_rescale.c, src/modules/core/producer_consumer.c,
+  src/modules/dv/producer_libdv.c: filter_rescale.c, producer_avformat.c,
+  producer_libdv.c, producer_consumer.c: coerce a deinterlace when scaling an
+  interlaced source. 
+
+2008-12-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt-config-template: mlt-config-template: send deprecation warning to
+  stderr to prevent breaking legacy scripts and makefiles 
+
+  * src/modules/core/filter_luma.c: filter_luma.c: prevent the first
+  application of the nested luma transition from being applied to a test card
+  image. This makes slideshows start without a transition at the beginning,
+  which is nicer and more expected. 
+
+  * src/modules/core/transition_luma.c: transition_luma.c: bugfix
+  (kdenlive-496) floating point exception when a slideshow using filter luma is
+  added to a multitrack. 
+
+2008-12-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: producer_westley.c: silence
+  compilation warning on uninitialized variable. 
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: producer_avformat.c,
+  consumer_avformat.c: use av_set_string3 where available (gets rid of
+  deprecation warning). 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  rendering to widescreen PAL DV. 
+
+2008-12-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c: add meta.media.
+  properties. 
+
+  * src/modules/dv/producer_libdv.c: producer_libdv.c: add meta.media.
+  properties. 
+
+  * src/modules/avformat/Makefile: avformat/Makefile: suppress error on
+  uninstall target 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c: avformat/configure, avformat/Makfile,
+  avformat/factory.c: Add a --avformat-no-filters configure option to
+  facilitate building a codecs and muxers only module. Change the module
+  filename for a no-codecs build to libmltffmpeg.so to prevent a clash with a
+  no-filters module (libmltavformat.so).  
+
+2008-12-21  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: add a bunch
+  of metadata about the media under the properties key prefix "meta.media." 
+
+2008-12-21  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/producer_sdl_image.c: producer_sdl_image.c: Fix crash when
+  attempting to play a folder without images 
+
+2008-12-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: let it work without
+  filter_avcolour_space 
+
+  * src/modules/core/producer_consumer.c: producer_consumer.c: use parent
+  profile if none specified; accept real_time properties from parent producer. 
+
+  * src/modules/core/producer_consumer.c: producer_consumer.c: set our length
+  from the length of the nested producer so we can terminate at the end of
+  rendering. 
+
+  * src/framework/mlt_properties.c: mlt_properties.c: fix some documentation 
+
+  * src/modules/core/Makefile, src/modules/core/factory.c,
+  src/modules/core/producer_consumer.c: core/Makefile, core/factory.c,
+  core/producer_consumer.c: add new producer_consumer that will consume from an
+  encapsulated producer under a different profile that the parent producer
+  (kdenlive-323). 
+
+  * src/modules/core/transition_region.c: transition_region.c: bugfix
+  regression with in built circle region 
+
+  * src/modules/avformat/filter_swscale.c: avformat/filter_swscale.c: add
+  support for scaling the alpha channel (needs further testing) 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+  src/modules/avformat/filter_swscale.c, src/modules/fezzik.ini:
+  avformat/Makefile, avformat/factory.c, avformat/filter_swscale.c: add new
+  image scaler using FFmpeg libswcale. fezzik.ini: add swscale at higher
+  priority than gtk2/rescale.  
+
+2008-12-19  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: fezzik.dict: let qimage be a producer for svg 
+
+2008-12-18  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c: avformat/configure, avformat/Makefile,
+  avformat/factory.c: add configure option --avformat-no-codecs, which will
+  build the avformat module without the producer and consumer - useful to
+  people who want to make a version entirely without including FFmpeg's codecs,
+  which present patent royalty licensing issues. 
+
+  * src/modules/avformat/configure: avformat/configure: checkout
+  (--avformat-svn) or recommend (--help) a specific FFmpeg revision if this is
+  a release version of MLT (last field of version is even). 
+
+  * configure: configure: --disable-mmx implies --disable-sse 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+  src/modules/avformat/filter_avdeinterlace.c: avformat/Makefile,
+  avformat/factory.c, avformat/filter_avdeinterlace.c: Fix and enable the
+  avdeinterlace filter for a non-MMX configuration. 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: add support
+  for AVOptions as properties. 
+
+2008-12-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_events.c, src/framework/mlt_field.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_tractor.c: mlt_producer.c,
+  mlt_playlist.h, mlt_field.h, mlt_playlist.c, mlt_tractor.c, mlt_events.c: add
+  doxygen docs for events, field, and playlist. 
+
+2008-12-14  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  (kdenlive-432) segfault when reusing previous AVFrame (paused or idling on
+  last frame) but the previos AVFrame was invalid (not got_picture before
+  erroring out). 
+
+2008-12-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/filter_motion_est.c: motion_est/filter_motion_est.c:
+  the sse compilation flag logic was inverted 
+
+  * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c: gtk2/pixops.c,
+  gtk2/Makefile: prevent MMX on all x86_64, not just OS X 
+
+  * configure: configure: add make flag and define for ARCH_X86_64 for all OSes
+
+  * configure: configure: fix mmx/sse detection on OS X and add detection of
+  x86_64 to define ARCH_X86_64 
+
+  * src/modules/xine/Makefile, src/modules/xine/configure,
+  src/modules/xine/deinterlace.c, src/modules/xine/xineutils.h: xine/Makefile,
+  xine/xineutils.h, xine/deinterlace.c: respect mmx compilation flag instead of
+  using own detection xine/configure: remove, no longer necessary 
+
+  * src/modules/motion_est/filter_motion_est.c: filtedr_motion_est.c: respect
+  new --disable-sse configure flag and whitespace cleanup 
+
+  * src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+  src/modules/gtk2/pixops.c: gtk2/Makefile, gtk2/configure, gtk2/pixops.c:
+  disable MMX parts on OS X - does not build 
+
+  * src/modules/kino/configure: kino/configure: automatically disable on OS X -
+  does not build due to missing headers 
+
+  * configure: configure: add --disable-sse and add mmx/sse detection for OS X 
+
+2008-12-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * profiles/atsc_1080i_50: profiles/atsc_1080i_50: added new profile for
+  square pixel 1080i at a PAL-like rate 
+
+  * Doxyfile: Doxyfile: add doxygen config file 
+
+  * src/valerie/valerie_status.h: valerie_status.h: take stdio.h header from
+  system include path 
+
+  * docs/install.txt: docs/install.txt: fix license info on humperdink and
+  valerie 
+
+  * configure: configure: bump the version 
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+  src/framework/mlt_events.c, src/framework/mlt_events.h,
+  src/framework/mlt_field.c, src/framework/mlt_field.h,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+  src/framework/mlt_parser.c, src/framework/mlt_parser.h,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_pool.c, src/framework/mlt_pool.h,
+  src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+  src/framework/mlt_profile.c, src/framework/mlt_profile.h,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.c, src/framework/mlt_property.h,
+  src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_tokeniser.c, src/framework/mlt_tokeniser.h,
+  src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+  src/framework/mlt_types.h: src/framework/*: improve the doxygen documentation
+  (work in progress). This also includes removal of superfluous white space. 
+
+2008-12-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c,
+  src/modules/sdl/producer_sdl_image.c: producer_pixbuf.c, producer_qimage.c,
+  producer_sdl_image.c: bugfix (kdenlive-422) not validating input file for
+  image producers. 
+
+  * src/modules/inigo/producer_inigo.c: producer_inigo.c: display a warning
+  when failed to load a file. 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: clean up the
+  dual pass log at the end of the second pass. 
+
+2008-11-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix r1242
+  segfault due to improper audio decoder memory allocation. Also fix logical
+  bug with resampling on channels > 2 
+
+  * src/modules/avformat/audioconvert.h,
+  src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  (kdenlive-297) audio distortion with audio formats other than signed 16-bit. 
+
+2008-11-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: fezzik.dict: added support for .tif equivalent to
+  .tiff 
+
+2008-11-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/albino.c: albino.c: fix playout with SDL on Mac OS X 
+
+  * src/modules/sox/filter_sox.c: filter_sox.c: bugfix (2263114) build on sox
+  14.2.0. 
+
+2008-11-13  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: fix detection of
+  current frame position in a playlist 
+
+2008-11-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  (kdenlive-347) segfault when resolution is not known until after first frame
+  is decoded. Also, bugfix segfault when video_index or audio_index are -1
+  (invalid). 
+
+2008-11-13  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/filter_freeze.c: filter_freeze.c: update frozen frame
+  if freeze position is changed on the fly 
+
+2008-11-13  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/oldfilm/filter_vignette.c,
+  src/modules/oldfilm/filter_vignette.yml: filter_vignette.{c,yml}: better
+  standard values and correct start param name  
+
+2008-11-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * NEWS, configure: configure, NEWS: bump to version 0.3.2 and update release
+  notes 
+
+2008-11-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_affine.c: filter_affine.c: bugfix (kdenlive-235)
+  rendering when used inside a multitrack. 
+
+2008-11-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * profiles/hdv_720_50p: profiles/hdv_720_50p: fix frame rate in description 
+
+  * profiles/atsc_1080p_2398, profiles/atsc_1080p_24, profiles/atsc_1080p_25,
+  profiles/atsc_1080p_2997, profiles/atsc_1080p_30, profiles/hdv_1080_25p,
+  profiles/hdv_1080_30p, profiles/hdv_720_50p, profiles/hdv_720_60p:
+  profiles/hdv_*, profiles/atsc_*: added more HD progressive mode profiles 
+
+  * src/modules/oldfilm/filter_dust.yml: filter_dust.yml: apply description fix
+  patch from Mads Dydensborg. 
+
+  * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: bugfix
+  segfault in construction with null argument. 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  detection of aspect ratio for DV AVI (applies to raw and quicktime files as
+  well). 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: rework the
+  aspect ratio detection to try to fetch it from the codec and/or the stream in
+  newer versions of ffmpeg. This fixes aspect handling for raw DV but still not
+  yet for DV AVIs without the vprp chunk. 
+
+2008-11-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: fix build on BSD 
+
+  * src/modules/core/transition_mix.c: transition_mix.c: prevent serializing
+  previous_mix and reset previous_mix on seeking. 
+
+  * src/modules/normalize/filter_volume.c: filter_volume.c: prevent serializing
+  previous_gain and reset previous_gain on seeking. 
+
+2008-11-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt: services.txt: minor corrections to documentation for
+  producer_avformat 
+
+2008-11-05  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/Makefile, src/modules/kdenlive/factory.c,
+  src/modules/kdenlive/filter_freeze.c: kdenlive/filter_freeze.c: added simple
+  freeze filter 
+
+2008-10-30  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/oldfilm/filter_vignette.c,
+  src/modules/oldfilm/filter_vignette.yml: oldfilm/filter_vignette*: filter is
+  now usable with keyframes  
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: set min/max values in
+  metadata to defined min/max from frei0r.h  
+
+  * src/modules/frei0r/frei0r_helper.c: frei0r/frei0r_helper.c: frei0r double
+  and bool params are now useable with keyframes (mlt_geometry)  
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: yml files can be used now
+  for critical plugins  
+
+2008-10-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * profiles/Makefile: profiles/Makefile: fix removal of turd (*~) files on
+  install. 
+
+  * docs/TODO: docs/TODO: refer to wiki page 
+
+  * Makefile: Makefile: suppress warning on ldconfig failure. 
+
+2008-10-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/albino.c, src/inigo/inigo.c: albino.c, inigo.c: disable realtime
+  scheduling (kdenlive-180).  
+
+2008-10-27  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: Fix crash /
+  corruption when changing audio or video index 
+
+2008-10-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: optimize
+  slightly the stream index bugfix and update the video informational
+  properties on the producer when the video index changes. 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  regression with audio_index and video_index in last release when I added the
+  feature to close file on init with re-open on first use. Also, added some
+  exception handling around index values. 
+
+  * src/modules/vmfx/filter_mono.c: filter_mono.c: cleanup code to made it more
+  consistent between cases (use_alpha). 
+
+  * src/modules/vmfx/filter_mono.c: filter_mono.c: bugfix (kdenlive-234)
+  threshold filter inverting image and add invert property to revert to old
+  behavior. 
+
+2008-10-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/modules/kino/endian_types.h, src/modules/kino/riff.cc,
+  src/modules/sox/configure: configure, kino/enadian_types.h, kino/riff.c,
+  sox/configure: apply patch from Alberto Villa to fix build on FreeBSD and to
+  fix a sh expression bug in sox/configure. 
+
+2008-10-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c:
+  improve delimiter parsing to allow '?' in filename argument 
+
+  * mlt-config-template: mlt-config-template: add deprecation warning 
+
+  * src/modules/sox/filter_sox.c: filter_sox.c: bugfix recent build regression
+  on older versions of sox 
+
+2008-10-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: inigo.c: improve the usage help and add -silent and
+  -progress options 
+
+  * src/modules/inigo/producer_inigo.c: producer_inigo.c: bugfix (2164436)
+  processing unknown command line options causes infinite loop 
+
+  * src/inigo/Makefile, src/inigo/inigo.c: inigo.c: added -version option 
+
+  * src/modules/sox/filter_sox.c: filter_sox.c: bugfix (2040035) segfault with
+  libsox 14.1.0 
+
+  * configure: configure: -O3 is the maximum optimization level, not -O4 
+
+2008-10-21  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix
+  deprecated warning on av_set_string 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix build on
+  older libavformat versions 
+
+2008-10-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix Ogg
+  Vorbis files and possibly others that report invalid pts on some packets 
+
+  * src/modules/xine/configure: xine/configure: disable module on ppc64 
+
+  * src/modules/xine/configure: xine/configure: disable module on ppc64 
+
+2008-10-08  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: Fix crash
+  introduced by FFmpeg revision 15367 (check that muxer and encoder have same
+  aspect ratio) 
+
+2008-10-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  reading file over http.  
+
+2008-09-22  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp:
+  producer_pixbuf.c, qimage_wrapper.c: Add "force_reload" option to force image
+  reloading in the image producers 
+
+2008-09-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  (2106941) compilation against recent ffmpeg changes 
+
+2008-09-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/filehandler.cc: modules/kino/filehandler.cc: compilation
+  fix  
+
+2008-08-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/configure, src/modules/sox/filter_sox.c: sox/configure,
+  filter_sox.c: fix building against sox 14.1.0.  
+
+2008-08-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/modules/sdl/consumer_sdl.c: consumer_sdl.c: added support
+  for fullscreen with no mouse through the "fullscreen" property. 
+
+2008-08-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure: bump versions for 0.3.0 release 
+
+  * Makefile: improve make dist target 
+
+  * AUTHORS: add AUTHORS file 
+
+  * NEWS: Add release notes file 
+
+  * demo/mlt_news: demo/mlt_news: small typo 
+
+2008-08-05  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c:
+  kdenlive/producer_framebuffer.c: keep resource file in producer and use '?'
+  instead of ':' to separate filename from speed, because it caused some
+  problems with other MLT functions 
+
+2008-08-03  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c: framework/mlt_playlist.c: check length before
+  inserting blank, which fixes one frame blanks that were sometimes inserted
+  where not needed. 
+
+2008-07-31  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: use float values for
+  "double vars" in frei0r   
+
+2008-07-28  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/configure: frei0r/configure: removed unneeded newlines  
+
+2008-07-27  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c: producer_framebuffer.c: Fix
+  aspect ratio with slowmotion producer 
+
+2008-07-24  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure: qimage/configure: Fix Qt3 detection and
+  compilation 
+
+2008-07-22  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/producer_qimage.c,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+  qimage module: add mutex, fix caching and use alpha only if necessary (mostly
+  borrowed from producer_pixbuf) 
+
+2008-07-14  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure: qimage/configure: Fix Qt4 detection 
+
+2008-07-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  recent regression with setting aspect ratio. Now it takes it from the profile
+  by default using the quotient properties for best accuracy. Now, one can also
+  override the aspect ratio using the same property name as the ffmpeg command
+  line utility ("aspect") for even greater symmetry. 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  segfault when fail to open or read file in init. 
+
+2008-07-10  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure, src/modules/qimage/producer_qimage.c,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+  qimage module: add support for Qt4 (you can force compile against Qt3 with
+  --force-qt3) 
+
+2008-07-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c: bugfix regression
+  with introduction of mlt_profile causing length of vorbis producer to always
+  yield zero. 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  regression playing audio-only files. 
+
+2008-07-01  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/oldfilm/filter_vignette.c,
+  src/modules/oldfilm/filter_vignette.yml: oldfilm/filter_vignette.{c,yml}:
+  change format for parameters, to avoid converting problems with different
+  locales  
+
+2008-06-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/framework/mlt_service.c:
+  mlt_properties.c, mlt_service.c: bugfix to make reference counting and
+  service closure truly thread-safe. As it was, reference count increment and
+  decrement operations were not atomic and not protected comprehensively. 
+
+  * src/framework/mlt_consumer.c: mlt_consumer.c: added ability to set priority
+  of the read-ahead thread through a new "priority" property. This only works
+  if you have permission; fails to execute properly otherwise - not sure how to
+  make it fail over gracefully. Do not set this property if you do not have
+  permission. 
+
+2008-06-25  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_field.c: mlt_field.c: correctly reconnect transitions
+  after a service disconnect 
+
+  * src/framework/mlt_service.c: mlt_service.c: fix bad identification for some
+  services (eg. transitions) 
+
+2008-06-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  segfault up the call stack when no image could be decoded for a frame by
+  producing the "test card" image. 
+
+2008-06-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c: mlt_playlist.c: return error on
+  mlt_playlist_get_clip_info if producer is null. 
+
+2008-06-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik/producer_fezzik.c: producer_fezzik.c: let other services
+  prevent Fezzik from attaching filters by passing the "fezzik_normalised"
+  property. 
+
+  * src/framework/mlt_repository.c: mlt_repository.c: bugfix memory leak on
+  getting directory list of MLT_REPOSITORY. 
+
+2008-06-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c: mlt_consumer.c: make the realtime
+  frame-dropping heuristic based on actual frame rate instead of 25fps 
+
+2008-06-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  segfault in unprotected libavcodec call that is clearly marked not thread
+  safe! 
+
+2008-06-15  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: transition_composite.c: slightly
+  more accurate positioning when using crop panning and horizontal position is
+  adjusted to align chroma channels. 
+
+2008-06-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: close the
+  file at the end of object creation, then re-open the file on-demand. This
+  presented a file descriptor limit issue when loading very large playlists. 
+
+2008-06-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c: mlt_playlist.c: remove some unncessary and
+  inefficient accounting code. 
+
+2008-06-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: transition_composite.c: add repeat
+  processing to crop property. 
+
+  * src/modules/core/transition_composite.c: transition_composite.c: add
+  animatable geometry "pan" property. This suppresses implicit scaling of the
+  b-frame and makes the compositing rectangle crop. Then, it uses the x and y
+  geometry information to pan the b-frame within the composite rectangle. For
+  example, a negative x pans the image to the left and that portion of the b
+  frame left of the composite rectangle is cropped. w and h of the pan geometry
+  is not implemented at this time, but the plan is to implement scaling of the
+  b-frame. In the end, this can provide a Ken Burns effect for still images - a
+  much requested feature. 
+
+2008-06-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: remove
+  multi-threaded audio decoding option. It does not provide any advantage at
+  this time and just wastes resources. 
+
+  * src/framework/mlt_playlist.c: mlt_playlist.c: added an "autoclose" property
+  for sequential processing of very large playlists. If set, it automatically
+  closes previous producers to reduce resources (file handles and threads if
+  using producer_avformat with threads). 
+
+2008-06-01  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pango.c: producer_pango.c: make the size property
+  an absolute height in pixels for greater compositing and quality control. 
+
+  * src/modules/core/filter_resize.c, src/modules/core/transition_composite.c:
+  filter_resize,c, filter_composite.c: bugfix redundant rounding. 
+
+  * src/modules/core/filter_watermark.c: filter_watermark.c: bugfix propogation
+  of output_ratio as a double - was causing incorrect calculations in
+  transition_composite.c with non-square-pixel watermark sources. 
+
+  * src/framework/mlt_properties.c: mlt_properties.c: make arithmetic processor
+  use floating point instead of integer so that '/' is meaningful. I am not
+  totally certain of the consequences of this change because I am not aware of
+  where the feature is used. However, I am using it to specify the aspect ratio
+  of certain things like bitmap graphics that were not designed for square
+  pixels. And being able to specify a fraction allows for accurate detection of
+  equivalent aspect ratios between different sources, particularly compositing.
+
+  * src/framework/mlt_profile.c: mlt_profile.c: make fallback default sample
+  aspect ratio the same as the revised profile's sample aspect ratio 
+
+2008-05-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: apply the in point
+  to the position in the image sequence 
+
+2008-05-15  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/configure: fix compilation against
+  shared ffmpeg for a headers configuration that has appeared. 
+
+  * profiles/dv_ntsc, profiles/dv_ntsc_wide, profiles/dv_pal,
+  profiles/dv_pal_wide, src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: profiles/dv_*, consumer_avformat.c,
+  producer_avformat.c: bugfix (1912796) to override FFmpeg notion of sample
+  aspect for DV. The values it uses might be more proper in certain contexts,
+  but not in the way MLT currently operates. This change improves performance
+  and quality when outputting to one of the "dv" profiles when using DV or
+  other ITU-R 601-based video sources such as MPEG-2 for DVD Video and
+  broadcast. 
+
+2008-05-12  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_luma.c: Correctly update the luma file if the
+  resource was modified 
+
+2008-05-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c:
+  avformat/configure: fix compiling against shared ffmpeg due to changes in
+  ffmpeg pkg-config 
+
+2008-05-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_resize.c: filter_resize.c: fix field order
+  correction 
+
+  * src/modules/xine/configure: xine/configure: relax restrictions to let
+  OSX/Intel use xine deinterlace 
+
+  * src/framework/mlt_field.c, src/framework/mlt_field.h: mlt_field.[hc]: added
+  mlt_field_disconnect_service 
+
+  * src/modules/dgraft/Makefile, src/modules/dgraft/factory.c,
+  src/modules/dgraft/filter_telecide.c: modules/dgraft: added module for ports
+  of Donald Graft's GPL filters. 
+
+  * src/modules/sox/configure: sox/configure: make inclusion of libsfx dynamic 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  reporting of top_field_first on frame. 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure:
+  avformat/Makefile, configure: fix --avformat-swscale and the removal of the
+  ffmpeg 'lib' make target.  
+
+  * src/modules/core/filter_data_show.c: filter_data_show.c: bugfix
+  interpreting timecode, due to invalid fps on mlt_profiles API changes 
+
+2008-04-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c: filter_watermark.c,
+  filter_composite.c: support explicit deinterlace of composited image.  
+
+2008-04-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/lumas/create_lumas: lumas/create_lumas: bugfix (1940387)
+  bash-ism in script 
+
+  * configure, src/modules/motion_est/configure: configure,
+  motion_est/configure: remove module-specific crud from top-level configure
+  script, and enable motion_est now by default.  
+
+  * src/modules/kino/avi.cc, src/modules/kino/filehandler.cc,
+  src/modules/kino/kino_wrapper.cc: kino/kino_wrapper.cc, kino/filehandler.cc,
+  kino/avi.cc: bugfix (1936991) compilation with gcc 4.3.  
+
+2008-04-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c: producer_pixbuf.c: bugfix image
+  sequences 
+
+2008-03-22  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/transition_frei0r.c:
+  frei0r/{frei0r_helper,transition_frei0r}.c: fixed wrong scaling and memory
+  leak  
+
+2008-03-18  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/configure: improve chances of
+  successful linking with -svn and -static options 
+
+2008-03-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/riff.cc: kino/riff.c: fix failure to dlopen due to symbol
+  signature mismatch on make_fourcc 
+
+  * src/modules/frei0r/configure, src/modules/frei0r/factory.c:
+  frei0r/configure: use CFLAGS so I can tell the test where to find frei0r.h
+  frei0r/factory.c: add metadata_schema value to metadata 
+
+  * src/framework/mlt_repository.c: mlt_repository.c: clear up warning due to
+  const return from getenv_locale() 
+
+2008-03-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_repository.c: mlt_repository.c: fix to previous string
+  const fix in mlt_repository_languages 
+
+  * src/framework/mlt_repository.c: mlt_repository.[hc]: fix modifying const
+  string in mlt_repository_languages 
+
+  * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+  mlt_repository.[hc]: add mlt_repository_languages helper function for
+  localizing metadata 
+
+2008-03-05  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/valerie/Makefile: src/valerie/Makefile: fix overwriting libmlt.0.dylib
+  on libvalerie install on OS X 
+
+  * src/modules/sox/configure: sox/configure: add OS X and Debian (future?)
+  pkg-config support to sox configuration 
+
+2008-03-04  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/factory.c: src/modules/frei0r/factory.c: load metadata
+  on request (thx for patch from Dan Dennedy) added "tags" metadata with type
+  "Video" for frei0r plugins  
+
+  * src/modules/frei0r/factory.c: modules/frei0r/factory.c: also register
+  transitions, added "tags" to metadata  
+
+  * src/modules/oldfilm/filter_vignette.c: oldfilm/filter_vignette.c: speedup  
+
+  * src/modules/oldfilm/fdust.svg, src/modules/oldfilm/filter_dust.yml,
+  src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_lines.yml,
+  src/modules/oldfilm/filter_oldfilm.yml,
+  src/modules/oldfilm/filter_tcolor.yml,
+  src/modules/oldfilm/filter_vignette.yml, src/modules/oldfilm/grain.svg,
+  src/modules/oldfilm/lines.svg, src/modules/oldfilm/oldfilm.svg,
+  src/modules/oldfilm/tcolor.svg, src/modules/oldfilm/vignette.svg:
+  modules/oldfilm: yml files without icon, icon as separate file  
+
+2008-03-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/Makefile, src/modules/sox/configure: sox/configure,
+  Makefile: try to make sox build smarter about library dependencies (pending
+  Darwin compatibilty) 
+
+  * src/framework/metaschema.yaml, src/modules/avformat/producer_avformat.yml:
+  metaschema.yaml, producer_avformat.yml: reset schema_version to 0.1 since we
+  have not release anything yet with schema let alone metadata 
+
+  * src/modules/frei0r/factory.c: frei0r/factory.c: apply destructors and
+  serialiser to metadata mlt_properties 
+
+  * src/inigo/inigo.c: inigo.c: fix querying on specific filter or transition 
+
+2008-03-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_resize.c: filter_rescale.c: if input width or
+  height are zero, infer them from the profile 
+
+2008-02-28  blendamedt <blendamedt@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/frei0r/configure: test not only if header frei0r.h exists, also
+  use an item  
+
+  * src/modules/frei0r/Makefile, src/modules/frei0r/configure,
+  src/modules/frei0r/factory.c, src/modules/frei0r/filter_frei0r.c,
+  src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h,
+  src/modules/frei0r/transition_frei0r.c: initial frei0r support  
+
+  * src/modules/oldfilm/Makefile, src/modules/oldfilm/dust1.svg,
+  src/modules/oldfilm/dust2.svg, src/modules/oldfilm/dust3.svg,
+  src/modules/oldfilm/dust4.svg, src/modules/oldfilm/dust5.svg,
+  src/modules/oldfilm/factory.c, src/modules/oldfilm/filter_dust.c,
+  src/modules/oldfilm/filter_dust.yml, src/modules/oldfilm/filter_grain.c,
+  src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_lines.c,
+  src/modules/oldfilm/filter_lines.yml, src/modules/oldfilm/filter_oldfilm.c,
+  src/modules/oldfilm/filter_oldfilm.yml, src/modules/oldfilm/filter_tcolor.c,
+  src/modules/oldfilm/filter_tcolor.yml, src/modules/oldfilm/filter_vignette.c,
+  src/modules/oldfilm/filter_vignette.yml: updated oldfilm module + 2 new
+  filters  
+
+2008-02-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/Makefile, src/framework/metaschema.yaml,
+  src/modules/avformat/producer_avformat.yml: framework/Makefile,
+  metaschema.yaml: add a Kwalify schema for metadata producer_avformat.yml:
+  update to schema  
+
+2008-02-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/Makefile: compilation fix for
+  latest FFmpeg update 
+
+2008-02-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: inigo.c: add -query option to inigo for service and
+  metadata lookup. 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/factory.c,
+  src/modules/avformat/producer_avformat.yml: avformat/factory.c,
+  producer_avformat.yml, avformat/Makefile: add metadata for producer:avformat.
+
+  * src/tests/Makefile, src/tests/dan.c: dan.c: example showing how to use the
+  new yaml parsing and serialisation and the new registry metadata system 
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h:
+  mlt_properties.[hc]: added really simply YAML Tiny parser and serialiser,
+  mainly to support the registry metadata system. 
+
+  * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+  mlt_repository.[hc]: implement the metadata registration and lookup interface
+
+2008-02-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/Makefile: sox/Makefile: helpful note for Ubuntu (and
+  Debian?)  
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure:
+  avformat/configure, avformat/Makefile: add libavdevice for newer versions of
+  ffmpeg when using --avformat-svn or --avformat-static   
+
+  * src/framework/mlt_repository.c: mlt_repository.c: throw warning on failure
+  to load module  
+
+2008-02-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_repository.c, src/framework/mlt_repository.h:
+  mlt_consumer.[hc]: added new functions mlt_repository_consumers,
+  mlt_repository_filters, mlt_repository_producers, mlt_repository_transitions,
+  mlt_repository_register_metadata, and mlt_repository_metadata  
+
+2008-02-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: avformat/configure: use pkg-config with
+  --avformat-shared 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: - Convert to
+  ffmpeg and AVOptions exclusively. This makes ALL AVOptions as seen from
+  \'ffmpeg -h\' available to MLT. Instead of ffmpeg\'s \'-option value\' use
+  inigo\'s property syntax \'option=value\" - Add dual pass encoding. - Use
+  multi-threading even with non-threaded codecs by separating producer and
+  consumer threads. - Whitespace cleanup. 
+
+  * src/framework/mlt_consumer.c: mlt_consumer.c: let consumers use read-ahead
+  processing thread without frame dropping with real_time=-1 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: cleanup
+  whitespace 
+
+2008-02-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: consumer_avformat.c,
+  producer_avformat.c: add FFmpeg multi-thread support via "threads" property
+  or MLT_AVFORMAT_THREADS environment variable   
+
+2008-02-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/producer_libdv.c: producer_libdv.c: fix test for framerate
+  matching profile 
+
+2008-02-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/framework/Makefile, src/framework/mlt.h,
+  src/miracle/Makefile, src/valerie/Makefile: configure: add soversion
+  variable, move version variables to top for easier access framework/Makefile,
+  miracle/Makefile, valerie/Makefile: improve library versioning by linking on
+  interface version (soversion) mlt.h: add version info to header so apps can
+  have build time adaptations 
+
+  * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_repository.c, src/framework/mlt_repository.h: cleanup some
+  names since we are changing the interface mlt_repository.[hc]: change
+  mlt_repository_fetch to mlt_repository_create mlt_factory.[hc]: change
+  mlt_factory_prefix to mlt_factory_directory 
+
+2008-02-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt.h, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_properties.c,
+  src/framework/mlt_repository.c, src/framework/mlt_repository.h,
+  src/modules/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c, src/modules/configure,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/dv/configure, src/modules/dv/factory.c,
+  src/modules/effectv/configure, src/modules/effectv/factory.c,
+  src/modules/fezzik/configure, src/modules/fezzik/factory.c,
+  src/modules/gtk2/configure, src/modules/gtk2/factory.c,
+  src/modules/inigo/configure, src/modules/inigo/factory.c,
+  src/modules/inigo/producer_inigo.c, src/modules/jackrack/configure,
+  src/modules/jackrack/factory.c, src/modules/kdenlive/configure,
+  src/modules/kdenlive/factory.c, src/modules/kino/configure,
+  src/modules/kino/factory.c, src/modules/motion_est/configure,
+  src/modules/motion_est/factory.c, src/modules/normalize/configure,
+  src/modules/normalize/factory.c, src/modules/oldfilm/configure,
+  src/modules/oldfilm/factory.c, src/modules/plus/configure,
+  src/modules/plus/factory.c, src/modules/qimage/configure,
+  src/modules/qimage/factory.c, src/modules/resample/configure,
+  src/modules/resample/factory.c, src/modules/sdl/configure,
+  src/modules/sdl/factory.c, src/modules/sox/configure,
+  src/modules/sox/factory.c, src/modules/valerie/configure,
+  src/modules/valerie/factory.c, src/modules/vmfx/configure,
+  src/modules/vmfx/factory.c, src/modules/vorbis/configure,
+  src/modules/vorbis/factory.c, src/modules/westley/configure,
+  src/modules/westley/factory.c, src/modules/xine/configure,
+  src/modules/xine/factory.c: mlt_repository.[hc]: - dynamically locate and
+  register modules instead of reading .dat files - added
+  mlt_repository_register() and macros for modules and apps(!) to register
+  their service factory functions mlt_factory.[hc]: change mlt_factory_init()
+  to return mlt_repository to app mlt_properties.c: let
+  mlt_properties_dir_list() take a NULL filter pattern src/modules/*: - adapt
+  to new module registration system - much simpler! - remove unncessary
+  configure scripts (now optional!)   
+
+2008-02-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/config.h: remove config.h 
+
+  * Makefile, setenv, src/framework/Makefile, src/framework/mlt_consumer.c,
+  src/framework/mlt_factory.c, src/framework/mlt_filter.c,
+  src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_parser.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/framework/mlt_property.c, src/framework/mlt_service.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+  src/modules/Makefile, src/modules/avformat/Makefile,
+  src/modules/avformat/configure, src/modules/core/Makefile,
+  src/modules/core/filter_data_show.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/dv/Makefile,
+  src/modules/effectv/Makefile, src/modules/feeds/Makefile,
+  src/modules/fezzik/Makefile, src/modules/fezzik/producer_fezzik.c,
+  src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+  src/modules/jackrack/Makefile, src/modules/kdenlive/Makefile,
+  src/modules/kino/Makefile, src/modules/lumas/Makefile,
+  src/modules/motion_est/Makefile, src/modules/normalize/Makefile,
+  src/modules/oldfilm/Makefile, src/modules/plus/Makefile,
+  src/modules/qimage/Makefile, src/modules/resample/Makefile,
+  src/modules/sdl/Makefile, src/modules/sox/Makefile,
+  src/modules/sox/configure, src/modules/valerie/Makefile,
+  src/modules/vmfx/Makefile, src/modules/vmfx/filter_shape.c,
+  src/modules/vorbis/Makefile, src/modules/westley/Makefile,
+  src/modules/xine/Makefile: move binary modules to libdir - affects
+  MLT_REPOSITORY added MLT_DATA environment variable to refer to share dir
+  remove need for config.h   
+
+2008-02-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_factory.c:
+  guard against accessing mlt_environment before it is ready mlt_profile.c: fix
+  setting legacy MLT_NORMALISATION on mlt_environment 
+
+  * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_factory.c:
+  guard against setting mlt_environment before it is available mlt_profile.c:
+  use getenv instead of mlt_environment in case profile is created before
+  factory 
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_filter.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_geometry.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_profile.c,
+  src/framework/mlt_profile.h, src/framework/mlt_repository.c,
+  src/framework/mlt_repository.h, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+  src/miracle/miracle_connection.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit_commands.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+  src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avcolour_space.h,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/filter_avdeinterlace.h,
+  src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/filter_avresample.h,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/avformat/producer_avformat.h, src/modules/core/consumer_null.c,
+  src/modules/core/consumer_null.h, src/modules/core/factory.c,
+  src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.h,
+  src/modules/core/filter_channelcopy.c, src/modules/core/filter_channelcopy.h,
+  src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+  src/modules/core/filter_data_show.c, src/modules/core/filter_gamma.c,
+  src/modules/core/filter_gamma.h, src/modules/core/filter_greyscale.c,
+  src/modules/core/filter_greyscale.h, src/modules/core/filter_luma.c,
+  src/modules/core/filter_luma.h, src/modules/core/filter_mirror.c,
+  src/modules/core/filter_mirror.h, src/modules/core/filter_mono.c,
+  src/modules/core/filter_mono.h, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_obscure.h, src/modules/core/filter_region.c,
+  src/modules/core/filter_region.h, src/modules/core/filter_rescale.c,
+  src/modules/core/filter_rescale.h, src/modules/core/filter_resize.c,
+  src/modules/core/filter_resize.h, src/modules/core/filter_transition.c,
+  src/modules/core/filter_transition.h, src/modules/core/filter_watermark.c,
+  src/modules/core/filter_watermark.h, src/modules/core/producer_colour.c,
+  src/modules/core/producer_colour.h, src/modules/core/producer_noise.c,
+  src/modules/core/producer_noise.h, src/modules/core/producer_ppm.c,
+  src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+  src/modules/core/transition_luma.h, src/modules/core/transition_mix.c,
+  src/modules/core/transition_mix.h, src/modules/core/transition_region.c,
+  src/modules/core/transition_region.h, src/modules/dv/consumer_libdv.c,
+  src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+  src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.h,
+  src/modules/effectv/factory.c, src/modules/effectv/filter_burn.c,
+  src/modules/effectv/filter_burn.h, src/modules/fezzik/factory.c,
+  src/modules/fezzik/producer_fezzik.c, src/modules/fezzik/producer_fezzik.h,
+  src/modules/fezzik/producer_hold.c, src/modules/fezzik/producer_hold.h,
+  src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/consumer_gtk2.h,
+  src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/filter_rescale.h, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/gtk2/producer_pixbuf.h, src/modules/inigo/factory.c,
+  src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+  src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c,
+  src/modules/jackrack/filter_jackrack.h, src/modules/jackrack/filter_ladspa.c,
+  src/modules/jackrack/filter_ladspa.h, src/modules/kdenlive/factory.c,
+  src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.h,
+  src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/filter_wave.h,
+  src/modules/kdenlive/producer_framebuffer.c,
+  src/modules/kdenlive/producer_framebuffer.h, src/modules/kino/factory.c,
+  src/modules/kino/producer_kino.c, src/modules/kino/producer_kino.h,
+  src/modules/motion_est/factory.c,
+  .../motion_est/filter_autotrack_rectangle.c,
+  src/modules/motion_est/filter_crop_detect.c,
+  src/modules/motion_est/filter_motion_est.c,
+  src/modules/motion_est/filter_motion_est.h,
+  src/modules/motion_est/filter_vismv.c,
+  src/modules/motion_est/producer_slowmotion.c,
+  src/modules/normalize/factory.c, src/modules/normalize/filter_volume.c,
+  src/modules/normalize/filter_volume.h, src/modules/oldfilm/factory.c,
+  src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_dust.h,
+  src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_grain.h,
+  src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_lines.h,
+  src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_oldfilm.h,
+  src/modules/plus/factory.c, src/modules/plus/filter_affine.c,
+  src/modules/plus/filter_affine.h, src/modules/plus/filter_charcoal.c,
+  src/modules/plus/filter_charcoal.h, src/modules/plus/filter_invert.c,
+  src/modules/plus/filter_invert.h, src/modules/plus/filter_sepia.c,
+  src/modules/plus/filter_sepia.h, src/modules/plus/transition_affine.c,
+  src/modules/plus/transition_affine.h, src/modules/qimage/factory.c,
+  src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.h,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/resample/factory.c,
+  src/modules/resample/filter_resample.c,
+  src/modules/resample/filter_resample.h, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl.h, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c, src/modules/sdl/factory.c,
+  src/modules/sdl/producer_sdl_image.c, src/modules/sdl/producer_sdl_image.h,
+  src/modules/sox/configure, src/modules/sox/factory.c,
+  src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.h,
+  src/modules/valerie/consumer_valerie.c,
+  src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+  src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+  src/modules/vmfx/filter_chroma.h, src/modules/vmfx/filter_chroma_hold.c,
+  src/modules/vmfx/filter_chroma_hold.h, src/modules/vmfx/filter_mono.c,
+  src/modules/vmfx/filter_mono.h, src/modules/vmfx/filter_shape.c,
+  src/modules/vmfx/filter_shape.h, src/modules/vmfx/producer_pgm.c,
+  src/modules/vmfx/producer_pgm.h, src/modules/vorbis/factory.c,
+  src/modules/vorbis/producer_vorbis.c, src/modules/vorbis/producer_vorbis.h,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+  src/modules/westley/producer_westley.c,
+  src/modules/westley/producer_westley.h, src/modules/xine/factory.c,
+  src/modules/xine/filter_deinterlace.c, src/modules/xine/filter_deinterlace.h,
+  src/valerie/valerie_remote.c: framework: remove global profile, rather share
+  one mlt_profile across a service network and make it available from anywhere
+  through mlt_service_profile(). miracle, valerie: profile changes inigo: added
+  -profile and progress=1 to mimic kdenlive_renderer modules: profile changes.
+  Since nearly every file was touched, remove superfluous headers and prepare
+  for coming mlt_repository change. 
+
+2008-01-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  (kdenlive-28) a/v sync on non-whole frame rate. 
+
+2008-01-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: do not free
+  AVPacket if av_read_frame fails. 
+
+2008-01-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/oldfilm/Makefile, src/modules/oldfilm/configure,
+  src/modules/oldfilm/factory.c, src/modules/oldfilm/filter_dust.c,
+  src/modules/oldfilm/filter_dust.h, src/modules/oldfilm/filter_grain.c,
+  src/modules/oldfilm/filter_grain.h, src/modules/oldfilm/filter_lines.c,
+  src/modules/oldfilm/filter_lines.h, src/modules/oldfilm/filter_oldfilm.c,
+  src/modules/oldfilm/filter_oldfilm.h: src/modules/oldfilm/*: add oldfilm
+  module contributed by Marco Gittler 
+
+  * docs/services.txt: minor typo fix 
+
+  * src/framework/mlt_playlist.c: mlt_playlist.c: fix some blank-handling bugs
+  in mlt_playlist_insert_at() 
+
+2007-12-18  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: fezzik.dict: prioritize avformat over vorbis
+  module for .ogg, at least until better track type detection is in place. 
+
+2007-12-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: make
+  compilation fix on url_fclose version sensitive to support older ffmpeg 
+
+2007-12-08  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * .../motion_est/filter_autotrack_rectangle.c: Autotrack rectangle can now be
+  defined using geometry="x,y:wxh" instead of having to pass it in the filter
+  name 
+
+2007-12-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure, src/modules/sox/configure: sox/configure:
+  remove libsamplerate from linking by default 
+
+2007-12-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_data_show.c, src/modules/dv/producer_libdv.c,
+  src/modules/inigo/producer_inigo.c, src/modules/vorbis/producer_vorbis.c,
+  src/modules/westley/producer_westley.c: mlt_consumer.c, mlt_frame.c,
+  mlt_multitrack.c, mlt_playlist.c, mlt_producer.c, producer_avformat.c,
+  filter_data_show.c, producer_libdv.c, producer_inigo.c, producer_vorbis.c,
+  producer_westley.c: remove statefulness of frame rate through framework and
+  modules, and allow consumer properties to override profile settings. 
+
+  * src/modules/sdl/producer_sdl_image.c: producer_sdl_image.c: fix compilation
+  warning with respect to const pointer 
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: fix pointer
+  passed to url_fclose() 
+
+  * src/modules/kino/riff.h: kino/riff.h: fix compiler warnings on missing
+  const for char* 
+
+2007-11-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/Makefile, src/modules/sox/configure,
+  src/modules/sox/filter_sox.c: filter_sox.c, src/modules/sox/Makefile,
+  src/modules/sox/configure: add support for sox v14.0.0. 
+
+2007-10-19  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_server.c, src/miracle/miracle_unit.c,
+  src/modules/avformat/factory.c, src/modules/gtk2/pixops.c,
+  src/modules/gtk2/producer_pango.c, src/modules/jackrack/jack_rack.c,
+  src/modules/jackrack/plugin_settings.c, src/modules/kdenlive/filter_wave.c,
+  src/modules/plus/transition_affine.c, src/modules/vmfx/filter_chroma.c,
+  src/modules/vorbis/producer_vorbis.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: avformat/factory.c,
+  jackrack/jack_rack.c, jackrack/plugin_settings.c, vmfx/filter_chroma.c,
+  plus/transition_affine.c, westley/producer_westley.c,
+  westley/consumer_westley.c, kdenlive/filter_wave.c, vorbis/producer_vorbis.c,
+  gtk2/producer_pango.c, gtk2/pixops.c, miracle_server.c, miracle_unit.c:
+  cleanup a whole bunch of compiler warnings 
+
+  * src/modules/jackrack/configure: jackrack/configure: add detection for
+  ladspa and disable if not detected 
+
+  * src/modules/core/filter_luma.c: filter_luma.c: bugfix testing b_frame's
+  dimensions 
+
+  * src/modules/core/filter_resize.c: filter_resize.c: bugfix overriding
+  top_field_first property 
+
+  * src/modules/motion_est/filter_motion_est.c,
+  src/modules/motion_est/filter_vismv.c: filter_vismv.c: bugfix pointer to
+  array of motion vectors 
+
+  * src/modules/avformat/configure: avformat/configure: fix detect shared
+  install of libavformat due to link to versioned .so. 
+
+2007-10-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_profile.c: mlt_profile.c,
+  mlt_factory.c: bugfix loading profile by file specification and remove a
+  small memory leak  
+
+  * setenv, src/framework/mlt_profile.c: mlt_profle.c: add support for
+  MLT_PROFILES_DIR environment variable  
+
+  * src/modules/sdl/consumer_sdl.c: consumer_sdl.c: fix specifying window size
+  on constructor arg 
+
+  * src/modules/effectv/utils.c, src/modules/effectv/utils.h: effectv/utils.*:
+  fix compilation on OS X 
+
+2007-08-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_still.c: consumer_sdl_still.c: bugfix segfault
+
+2007-08-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_still.c: consumer_sdl_still.c: bugfix
+  initialisation of window dimensions due to recent profiles addition 
+
+2007-07-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, docs/policies.txt: configure: fix broken variables in pkg-config
+  files policies.txt: add bug reporting procedure  
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: align some
+  defaults with ffmpeg for more reliable output 
+
+2007-07-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_profile.c: mlt_profile.c:
+  bugfix string allocation length mlt_consumer.c: bugfix removal of
+  property-changed listener   
+
+2007-07-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * profiles/Makefile: profiles/Makefile: cleanup profiles dir on (un)install 
+
+  * profiles/atsc_1080i_60, profiles/atsc_720p_30, profiles/atsc_wide_1080i,
+  profiles/atsc_wide_720p: profiles/atsc_*: rename and change descriptions 
+
+  * profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_720_25p,
+  profiles/hdv_720_30p: 
+
+  * profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_1080_ntsc,
+  profiles/hdv_1080_pal, profiles/hdv_720_30p, profiles/hdv_720_60i: 
+
+  * profiles/hdv_720_25p, profiles/hdv_720_50p: 
+
+  * profiles/hdv_720_50p, profiles/hdv_720_60i, profiles/hdv_720_ntsc,
+  profiles/hdv_720_pal: 
+
+  * src/framework/mlt_profile.c: mlt_profile.c: revise substrings for legacy
+  setting of MLT_NORMALISATION 
+
+  * profiles/atsc_wide_1080i, profiles/atsc_wide_720p, profiles/cif_ntsc,
+  profiles/cif_pal, profiles/cvd_ntsc, profiles/cvd_pal, profiles/dv_ntsc,
+  profiles/dv_ntsc_wide, profiles/dv_pal, profiles/dv_pal_wide,
+  profiles/hdv_1080_ntsc, profiles/hdv_1080_pal, profiles/hdv_720_ntsc,
+  profiles/hdv_720_pal, profiles/qcif_ntsc, profiles/qcif_pal,
+  profiles/quarter_ntsc, profiles/quarter_ntsc_wide, profiles/quarter_pal,
+  profiles/quarter_pal_wide, profiles/square_ntsc, profiles/square_ntsc_wide,
+  profiles/square_pal, profiles/square_pal_wide, profiles/svcd_ntsc,
+  profiles/svcd_ntsc_wide, profiles/svcd_pal, profiles/svcd_pal_wide,
+  profiles/vcd_ntsc, profiles/vcd_pal, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_profile.c,
+  src/framework/mlt_profile.h: profiles/*: name->description
+  mlt_factory.{h,cc}: added mlt_environment_set() mlt_profile.{h,cc}: fix
+  setting legacy MLT_NORMALISATION, set MLT_PROFILE, and change "name" to
+  "description" for clarity   
+
+2007-07-15  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure:
+  avformat/configure: add --avformat-svn-extra avformat/Makefile: rebuild
+  module when local ffmpeg changes 
+
+  * profiles/Makefile, profiles/square_pal_wide: profiles/Makefile: do not
+  install Makefile profiles/square_pal_wide: fix display aspect 
+
+  * ChangeLog, Makefile, configure, profiles/Makefile,
+  profiles/atsc_wide_1080i, profiles/atsc_wide_720p, profiles/cif_ntsc,
+  profiles/cif_pal, profiles/cvd_ntsc, profiles/cvd_pal, profiles/dv_ntsc,
+  profiles/dv_ntsc_wide, profiles/dv_pal, profiles/dv_pal_wide,
+  profiles/hdv_1080_ntsc, profiles/hdv_1080_pal, profiles/hdv_720_ntsc,
+  profiles/hdv_720_pal, profiles/qcif_ntsc, profiles/qcif_pal,
+  profiles/quarter_ntsc, profiles/quarter_ntsc_wide, profiles/quarter_pal,
+  profiles/quarter_pal_wide, profiles/square_ntsc, profiles/square_ntsc_wide,
+  profiles/square_pal, profiles/square_pal_wide, profiles/svcd_ntsc,
+  profiles/svcd_ntsc_wide, profiles/svcd_pal, profiles/svcd_pal_wide,
+  profiles/vcd_ntsc, profiles/vcd_pal, src/framework/Makefile,
+  src/framework/mlt.h, src/framework/mlt_consumer.c,
+  src/framework/mlt_factory.c, src/framework/mlt_frame.c,
+  src/framework/mlt_geometry.c, src/framework/mlt_producer.c,
+  src/framework/mlt_profile.c, src/framework/mlt_profile.h,
+  src/framework/mlt_types.h, src/modules/dv/consumer_libdv.c,
+  src/modules/sdl/consumer_sdl.c: Added new profiles system: mlt_profile,
+  MLT_PROFILE, and profiles documents. 
+
+2007-07-14  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: consumer_avformat.c: save disabled,
+  experimental flushing code 
+
+2007-07-07  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure: Fix build based on patch from Ryan Hodge 
+
+2007-07-01  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict, src/modules/qimage/Makefile,
+  src/modules/qimage/configure, src/modules/qimage/producer_qimage.c,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h:
+  Add support for psd, xcf and exr images (KDE libraries needed for these
+  formats). Make pcx and tiff images load correctly 
+
+  * src/modules/gtk2/producer_pixbuf.c: Fix for rgba images (based on the code
+  from qimage_producer) 
+
+  * src/modules/kdenlive/producer_framebuffer.c: Fix get image for formats
+  different from yuv422 
+
+2007-07-01  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: improve
+  frame accuracy 
+
+2007-06-30  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c: Better fix for aspect_ratio
+  problem in framebuffer producer 
+
+  * src/modules/kdenlive/producer_framebuffer.c: Fix aspect ratio for
+  slowmotion / freeze effect 
+
+  * src/modules/kdenlive/configure: Fix typo which prevented wave filter to be
+  available 
+
+2007-06-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/circle.png: demo/circle.png: fix broken image 
+
+  * demo/watermark1.png: watermark1.png: fix broken image 
+
+  * demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark,
+  demo/mlt_voiceover: demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark,
+  demo/mlt_voiceover: fix broken demos due to recent hidden track handling
+  change in mlt_transition.c 
+
+2007-06-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: consumer_avformat.c: bugfix
+  initial buffer size to prevent high quantization at beginning 
+
+2007-06-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix audio
+  sync with some codecs and revert unnecessary precautions that introduce
+  inefficiency 
+
+2007-06-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile: added dist make targets 
+
+  * Makefile, src/albino/Makefile, src/framework/Makefile,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/modules/Makefile, src/valerie/Makefile: added uninstall make targets 
+
+2007-06-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/effectv/Makefile, src/modules/effectv/configure,
+  src/modules/effectv/factory.c, src/modules/effectv/filter_burn.c,
+  src/modules/effectv/filter_burn.h, src/modules/effectv/image.c,
+  src/modules/effectv/utils.c, src/modules/effectv/utils.h: added effectv
+  module with BurningTV filter provided by Stephane Fillod 
+
+  * src/framework/mlt_frame.c: mlt_frame.c: let image conversions accept NULL
+  for the alpha parameter 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: bugfix
+  segfault when paused after seeking but no picture available to duplicate 
+
+  * docs/westley.txt, src/modules/fezzik.dict: fezzik.dict: prioritize avformat
+  higher than libdv for better quality 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: make better
+  test for existence for avcodec_decode_audio2 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: fix setting
+  int property as double 
+
+  * src/modules/avformat/producer_avformat.c: producer_avformat.c: - remove
+  seeking immediately after opening file improves compatibility (in particular,
+  ogg theora) - use non-deprecated avcodec_decode_audio2 if available - changes
+  to adhere to warnings on ffmpeg decode api docs ought to improve stability
+  and compatibility  
+
+  * src/modules/avformat/consumer_avformat.c: added support for ilme=1 and
+  ildct=1 properties to consumer_avformat 
+
+2007-06-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure: --avformat-swscale with --avformat-svn is
+  only permitted with --enable-gpl 
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure: change
+  --avformat-svn configure option to do a static build of ffmpeg libs only and
+  statically link to mlt module. Also, make --avformat-svn aware of
+  --avformat-swscale and --enable-gpl 
+
+2007-06-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_rescale.c: bugfix core/filter_rescale segfault on
+  scaling alpha that was already to correct scale (e.g. mlt_bouncy_ball) 
+
+2007-06-01  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_rescale.c: bugfix segfault in core/filter_rescale
+  scaling alpha already scaled in gtk2/filter_rescale 
+
+  * src/framework/mlt_tractor.c: bugfix tractor not propogating resize_alpha
+  frame property 
+
+  * src/framework/mlt_transition.c: bugfix transition processing hidden track 
+
+2007-05-31  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/producer_framebuffer.c: Fix framebuffer crash & clip
+  duration error 
+
+2007-05-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/consumer_libdv.c: per jb's suggestion, enable
+  terminate_on_pause by default 
+
+  * demo/README, demo/mlt_attributes, demo/mlt_intro, demo/mlt_jcut,
+  demo/mlt_lcut, docs/inigo.txt: fix some demos broken by old changes 
+
+2007-05-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_data_show.c: fix dynamic attribute value parsing
+  and memory management in data_show 
+
+2007-05-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_producer.c,
+  src/modules/fezzik.ini: the framework may not depend upon specific
+  modules--data_feed/show in this case 
+
+  * src/modules/core/filter_rescale.c: Only scale the alpha when also scaling
+  the image.  
+
+2007-04-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kdenlive/filter_wave.c: compilation fix  
+
+  * src/modules/avformat/configure: fix compilation without swscale  
+
+  * ChangeLog, docs/policies.txt, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_boxblur.c, src/modules/core/filter_boxblur.h,
+  src/modules/core/filter_wave.c, src/modules/core/filter_wave.h,
+  src/modules/core/producer_framebuffer.c,
+  src/modules/core/producer_framebuffer.h, src/modules/core/transition_luma.c,
+  src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h,
+  src/modules/jackrack/jack_rack.c, src/modules/jackrack/jack_rack.h,
+  src/modules/jackrack/lock_free_fifo.c, src/modules/jackrack/lock_free_fifo.h,
+  src/modules/jackrack/plugin.c, src/modules/jackrack/plugin.h,
+  src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_desc.h,
+  src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/plugin_mgr.h,
+  src/modules/jackrack/plugin_settings.c,
+  src/modules/jackrack/plugin_settings.h, src/modules/jackrack/process.c,
+  src/modules/jackrack/process.h, src/modules/kdenlive/Makefile,
+  src/modules/kdenlive/configure, src/modules/kdenlive/factory.c,
+  src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.h,
+  src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/filter_wave.h,
+  src/modules/kdenlive/producer_framebuffer.c,
+  src/modules/kdenlive/producer_framebuffer.h,
+  src/modules/normalize/filter_volume.c, src/modules/xine/filter_deinterlace.c:
+  Cleanup copyrights and attributions, and move Jean-Baptiste's services to a
+  new kdenlive module.  
+
+2007-03-31  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/producer_framebuffer.c: Fixed crash in slowmotion producer
+   
+
+2007-03-31  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/sox/filter_sox.c: add sox 13.0.0 support  
+
+2007-03-31  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/producer_framebuffer.c: Fix slowmotion producer (no more
+  variable speed, but at least it works now).  
+
+2007-03-30  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/core/filter_boxblur.c,
+  src/modules/core/filter_boxblur.h, src/modules/core/filter_wave.c,
+  src/modules/core/filter_wave.h: Update ChangeLog and fix license for blur and
+  wave filters  
+
+2007-03-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/vmfx/configure, src/modules/vmfx/factory.c: Change
+  registration of vmfx/mono to threshold to disambiguate with core/mono.  
+
+  * ChangeLog, GPL, README, configure, docs/install.txt, docs/policies.txt,
+  docs/services.txt, docs/testing-20040110.txt, src/albino/albino.c,
+  src/framework/mlt.h, src/framework/mlt_consumer.c,
+  src/framework/mlt_consumer.h, src/framework/mlt_deque.c,
+  src/framework/mlt_deque.h, src/framework/mlt_events.c,
+  src/framework/mlt_events.h, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_field.c,
+  src/framework/mlt_field.h, src/framework/mlt_filter.c,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_geometry.c,
+  src/framework/mlt_geometry.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_multitrack.h, src/framework/mlt_parser.c,
+  src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_property.c,
+  src/framework/mlt_property.h, src/framework/mlt_repository.c,
+  src/framework/mlt_repository.h, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h, src/framework/mlt_types.h,
+  src/humperdink/client.c, src/humperdink/client.h, src/humperdink/io.c,
+  src/humperdink/io.h, src/humperdink/remote.c, src/inigo/inigo.c,
+  src/inigo/io.c, src/inigo/io.h, src/miracle/miracle.c,
+  src/miracle/miracle_local.h, src/miracle/miracle_server.c,
+  src/miracle/miracle_server.h, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+  src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avcolour_space.h,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/filter_avdeinterlace.h,
+  src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/filter_avresample.h,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/avformat/producer_avformat.h, src/modules/core/consumer_null.c,
+  src/modules/core/consumer_null.h, src/modules/core/factory.c,
+  src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.h,
+  src/modules/core/filter_channelcopy.c, src/modules/core/filter_channelcopy.h,
+  src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+  src/modules/core/filter_data_show.c, src/modules/core/filter_gamma.c,
+  src/modules/core/filter_gamma.h, src/modules/core/filter_greyscale.c,
+  src/modules/core/filter_greyscale.h, src/modules/core/filter_luma.c,
+  src/modules/core/filter_luma.h, src/modules/core/filter_mirror.c,
+  src/modules/core/filter_mirror.h, src/modules/core/filter_mono.c,
+  src/modules/core/filter_mono.h, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_obscure.h, src/modules/core/filter_region.c,
+  src/modules/core/filter_region.h, src/modules/core/filter_rescale.c,
+  src/modules/core/filter_rescale.h, src/modules/core/filter_resize.c,
+  src/modules/core/filter_resize.h, src/modules/core/filter_transition.c,
+  src/modules/core/filter_transition.h, src/modules/core/filter_watermark.c,
+  src/modules/core/filter_watermark.h, src/modules/core/producer_colour.c,
+  src/modules/core/producer_colour.h, src/modules/core/producer_noise.c,
+  src/modules/core/producer_noise.h, src/modules/core/producer_ppm.c,
+  src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+  src/modules/core/transition_luma.h, src/modules/core/transition_mix.c,
+  src/modules/core/transition_mix.h, src/modules/core/transition_region.c,
+  src/modules/core/transition_region.h, src/modules/dv/consumer_libdv.c,
+  src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+  src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.h,
+  src/modules/fezzik/factory.c, src/modules/fezzik/producer_fezzik.c,
+  src/modules/fezzik/producer_fezzik.h, src/modules/fezzik/producer_hold.c,
+  src/modules/fezzik/producer_hold.h, src/modules/gtk2/consumer_gtk2.c,
+  src/modules/gtk2/consumer_gtk2.h, src/modules/gtk2/factory.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/gtk2/filter_rescale.h,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.h,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+  src/modules/gtk2/scale_line_22_yuv_mmx.S, src/modules/inigo/factory.c,
+  src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+  src/modules/lumas/luma.c, src/modules/plus/factory.c,
+  src/modules/plus/filter_affine.c, src/modules/plus/filter_affine.h,
+  src/modules/plus/filter_charcoal.c, src/modules/plus/filter_charcoal.h,
+  src/modules/plus/filter_invert.c, src/modules/plus/filter_invert.h,
+  src/modules/plus/filter_sepia.c, src/modules/plus/filter_sepia.h,
+  src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.h,
+  src/modules/qimage/producer_qimage.c, src/modules/qimage/qimage_wrapper.cpp,
+  src/modules/qimage/qimage_wrapper.h, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl.h, src/modules/sdl/consumer_sdl_osx_hack.h,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+  src/modules/sdl/factory.c, src/modules/sdl/producer_sdl_image.c,
+  src/modules/sdl/producer_sdl_image.h, src/modules/sox/factory.c,
+  src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.h,
+  src/modules/valerie/consumer_valerie.c,
+  src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+  src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c,
+  src/modules/vorbis/producer_vorbis.h, src/modules/westley/consumer_westley.c,
+  src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+  src/modules/westley/producer_westley.c,
+  src/modules/westley/producer_westley.h, src/valerie/valerie.h: Cleanup
+  license declarations and remove dv1394d references.  
+
+2007-03-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/avformat/Makefile, src/modules/avformat/configure:
+  fixup some swscale integration  
+
+2007-03-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, docs/TODO, docs/policies.txt: added docs/policies.txt  
+
+2007-03-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/producer_avformat.c: add support for ffmpeg libswscale  
+
+  * demo/README, demo/consumers.ini: change default dv1394 device file  
+
+  * configure: remove bashisms  
+
+2007-03-02  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c: Allow user to choose video driver
+  and output display  
+
+2007-02-19  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_boxblur.c, src/modules/core/filter_boxblur.h,
+  src/modules/core/filter_wave.c, src/modules/core/filter_wave.h: Fix typo,
+  credits and make functions static, (patch from stephane fillod - thanks)  
+
+2007-02-18  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_boxblur.c,
+  src/modules/core/filter_boxblur.h, src/modules/core/filter_wave.c,
+  src/modules/core/filter_wave.h: Add blur and wave filters from Leny Grisel  
+
+2007-02-01  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c: Allow user to set alsa device  
+
+2007-01-23  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_data_show.c: Allow display of metadata and timecode
+   
+
+2007-01-22  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: Write metadata if there is any  
+
+2007-01-19  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: Fix my terribly broken YUV to RGB conversion  
+
+2007-01-13  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c: Allow changing volume in
+  sdl_preview consumer  
+
+2007-01-02  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: Change default value for
+  libavformat's qscale, preventing some crashes  
+
+2006-12-31  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c,
+  src/modules/vorbis/producer_vorbis.c: Read metadata from avformat and vorbis
+  producers, using basic structure like:
+  meta.attr.metadata_name.markup=metadata_value  
+
+  * src/modules/vorbis/producer_vorbis.c: Vorbis should set correct values in
+  frame for audio channels and frequency.  
+
+2006-12-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, configure, src/framework/mlt_consumer.h,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+  src/framework/mlt_geometry.h, src/framework/mlt_multitrack.h,
+  src/framework/mlt_producer.h, src/framework/mlt_service.h,
+  src/framework/mlt_transition.h: Applied patch from Stephane Fillod to make
+  configure run with bash since it uses bash-specific features. Also, patches
+  headers to comments for pedantic compilation.  
+
+2006-11-20  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/producer_framebuffer.c: remove debug msg  
+
+2006-11-18  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/producer_framebuffer.c,
+  src/modules/core/producer_framebuffer.h: Fix header + add freeze feature  
+
+  * src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/producer_framebuffer.c,
+  src/modules/core/producer_framebuffer.h: New framebuffer producer. Provides
+  slowmotion, reverse playing and stroboscope effect  
+
+2006-11-05  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: Kdenlive project files are now westley compatible 
+
+  * src/modules/core/transition_luma.c: Luma get_image produces yuv only, so
+  announce it. Fix problem when requesting rgb image of a luma transition.  
+
+2006-10-26  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_rescale.c: Fix rescaling of rgb images when not
+  using gtk2  
+
+2006-10-16  j-b-m <j-b-m@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_obscure.c: Position for the effect was not
+  calculated right if the clip was in the middle of a playlist  
+
+2006-10-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: + General improved media support 
+
+2006-10-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: + Correction to previous patch -
+  fixes pause behaviour with rawvideo  
+
+  * src/modules/avformat/producer_avformat.c: + Corrections for uncompressed
+  video sources  
+
+2006-09-28  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog: Following Dan's example.  Applied an amd64 compilation patch to
+  motion_est module and patch to correctly initialize audio frequency and
+  channels.  
+
+  * src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c:
+  Patch supplied by Jean-Baptiste. 
+  
+   
+
+  * src/modules/motion_est/filter_motion_est.c,
+  src/modules/motion_est/sad_sse.h: Zypher's amd64 patch. 
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/media-libs/mlt/files/  
+
+2006-09-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog, src/modules/sdl/Makefile: fix SDL compilation on some systems
+  using modular x.org  
+
+2006-08-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/filter_mono.h: + Header file for mono filter  
+
+  * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+  src/modules/vmfx/factory.c, src/modules/vmfx/filter_mono.c: + A mono filter
+  for mask generation (not v. useful)  
+
+  * src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c: +
+  Correction to uneven chroma samples  
+
+  * src/modules/qimage/qimage_wrapper.cpp: + Image caching for the qimage
+  producer  
+
+  * src/modules/gtk2/producer_pixbuf.c: + Image caching for the gtk2 pixbuf
+  producer  
+
+2006-08-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * ChangeLog: *** empty log message ***  
+
+  * src/modules/westley/producer_westley.c: enhance producer_westley to parse
+  Kino 0.9.1 SMIL (clock) time values.  
+
+  * ChangeLog: *** empty log message ***  
+
+  * src/modules/avformat/configure: convert --avformat-cvs to svn and rename
+  option as --avformat-svn (--avformat-cvs is an undocumented alias).  
+
+2006-05-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure: bump version  
+
+2006-05-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/producer_qimage.c: apply patch from Jean-Baptiste
+  <jb@ader.ch> to add rgb24a support to producer_qimage  
+
+2006-05-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: apply patch from Jean Baptiste
+  <jb@ader.ch> to fix fill-type rescaling when aspect ratio is equal to
+  normalised ratio  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/gtk2/producer_pixbuf.c: apply patch from Jean Baptiste to add
+  rgb24a support to producer_pixbuf  
+
+2006-05-20  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/configure: let QTDIR also define location of qt include
+  dir  
+
+  * src/modules/kino/filehandler.cc: fix compilation on latest version of
+  libquicktime (0.9.8)  
+
+2006-05-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/filter_avcolour_space.c: + Big endian patch courtesy
+  of Goncalo Carvalho (glslang at gmail dot com) - specifically, corrects
+  colour space conversions on the Intel Mac  
+
+2006-04-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_resize.c: + Field order control reworked
+  (meta.top_field_first has priority over source)  
+
+2006-04-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_resize.c: + Top field first correction (original
+  approach would not have worked [mea culpa], and this is only a partial
+  solution since the consumers have no say in field order)  
+
+  * src/modules/qimage/qimage_wrapper.cpp: + Fix for byte order as spotted by
+  Goncalo Carvhalo (many thanks :-))  
+
+  * src/modules/core/filter_resize.c: + Meta override for field order
+  misreporting/errors in encoders  
+
+2006-03-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/qimage/Makefile, src/modules/qimage/configure,
+  src/modules/qimage/qimage_wrapper.cpp: + And a fix for the PPC darwin  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Sigh - big endian
+  issues on ppc based macs  
+
+  * src/modules/fezzik.dict, src/modules/qimage/Makefile,
+  src/modules/qimage/configure, src/modules/qimage/factory.c,
+  src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.h,
+  src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h: +
+  QImage module added - default is still GTK2 when available  
+
+  * src/modules/gtk2/producer_pixbuf.c: + Bug Fix: Removes a memory leak on
+  last alpha channel  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Preparation for a
+  QT image loader (to allow optional and functionally equivalent qt or gtk2
+  usage for image loading)  
+
+2006-03-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pixbuf.c: + Usage of mlt_properties_dir_list  
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h: + Adds a
+  utility function for listing files in a directory (aids with cross platform
+  support)  
+
+2006-03-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/framework/mlt_manager.h, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_mono.c, src/modules/core/filter_mono.h: added mono
+  audio filter  
+
+  * src/modules/kino/Makefile: libquicktime prefers pkg-config now and latest
+  lqt-config is broken with respect to --cflags  
+
+  * configure: log configuration history to config.log  
+
+2006-02-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/filter_shape.c: + Activates the mixdown in the combine to
+  allow audio sync'd with wipe (smooth ramping not implemented yet)  
+
+  * src/modules/core/transition_mix.c: + Alternative mixing mechanism
+  introduced (specify a property of combine=1 on the mix transition to
+  activate)  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h: + Alternative between
+  track mixing mechanism (using a low pass filter)  
+
+2006-02-15  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/dvcp.txt, docs/inigo.txt: minor fixes  
+
+  * src/miracle/miracle_commands.c: add proper response to uadd command  
+
+2006-01-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/Makefile: fix compilation error  
+
+  * src/modules/dv/producer_libdv.c: Make libdv producer return some image even
+  if unable to handle specific image type request.  
+
+  * Makefile: dist-clean target is more familiar - alias it  
+
+  * src/modules/feeds/NTSC/data_fx.properties,
+  src/modules/feeds/NTSC/obscure.properties: fix comment/docu typo  
+
+2005-12-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * debian/control, debian/rules, src/miracle/Makefile: + Fix for libmiracle
+  and alternative deb packaging  
+
+  * src/framework/Makefile, src/miracle/Makefile,
+  src/modules/avformat/configure, src/valerie/Makefile: + Fix for Darwin and
+  soname logic  
+
+  * debian/changelog, debian/control, debian/copyright, debian/rules: +
+  Functional debian build rules  
+
+  * Makefile, configure, src/albino/Makefile, src/framework/Makefile,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/modules/Makefile, src/modules/avformat/Makefile,
+  src/modules/core/Makefile, src/modules/dv/Makefile,
+  src/modules/feeds/Makefile, src/modules/fezzik/Makefile,
+  src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+  src/modules/jackrack/Makefile, src/modules/kino/Makefile,
+  src/modules/lumas/Makefile, src/modules/motion_est/Makefile,
+  src/modules/normalize/Makefile, src/modules/plus/Makefile,
+  src/modules/resample/Makefile, src/modules/sdl/Makefile,
+  src/modules/sox/Makefile, src/modules/valerie/Makefile,
+  src/modules/vmfx/Makefile, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/modules/xine/Makefile,
+  src/valerie/Makefile: + Final updates for 0.2.1 - distclean corrected, soname
+  usage in linking, version bump  
+
+2005-11-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/configure, src/miracle/configure, src/valerie/configure: +
+  More fixes for lib64  
+
+  * src/modules/avformat/Makefile: + Uses libdir in private build of ffmpeg too
+   
+
+  * src/modules/avformat/configure: + 64 bit fix for ffmpeg built externally
+  (should switch to pkg-config here)  
+
+  * configure: - Removed a diagnostic  
+
+  * Makefile, configure, src/framework/Makefile, src/miracle/Makefile,
+  src/valerie/Makefile: + Added a --libdir switch to the configure and build  
+
+2005-11-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: + Correction to alpha mask
+  generation  
+
+2005-11-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, src/albino/Makefile, src/framework/Makefile,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/modules/Makefile, src/modules/avformat/Makefile,
+  src/modules/core/Makefile, src/modules/dv/Makefile,
+  src/modules/feeds/Makefile, src/modules/fezzik/Makefile,
+  src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+  src/modules/jackrack/Makefile, src/modules/kino/Makefile,
+  src/modules/lumas/Makefile, src/modules/motion_est/Makefile,
+  src/modules/normalize/Makefile, src/modules/plus/Makefile,
+  src/modules/resample/Makefile, src/modules/sdl/Makefile,
+  src/modules/sox/Makefile, src/modules/valerie/Makefile,
+  src/modules/vmfx/Makefile, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/modules/xine/Makefile,
+  src/valerie/Makefile: + DESTDIR patch from Anthony Green (green at redhat dot
+  com) - many thanks :-)  
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: Allows aac output, corrects ntsc
+  sample collection, and picks up known info streams  
+
+2005-10-28  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/filter_crop_detect.c: Correct bug introduced by
+  revision 1.3  
+
+  * src/modules/motion_est/filter_motion_est.c: x86 doesn't play well with ppc 
+
+  * src/modules/motion_est/Makefile: Fix shared lib flags in Makefile for
+  Darwin  
+
+2005-10-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/configure: + Enabled Zach's new slowmotion producer 
+
+  * src/modules/core/consumer_null.c,
+  .../motion_est/filter_autotrack_rectangle.c, src/modules/sdl/consumer_sdl.c:
+  src/modules/core/consumer_null.c src/modules/sdl/consumer_sdl.c + Terminate
+  on pause functionality  src/modules/motion_est/filter_autotrack_rectangle.c +
+  Ensures that tracked area remains valid (out of bounds was causing core
+  dumps) ? Currently, width/height is preserved on boundaries, but maybe it
+  should shrink/grow?  
+
+2005-10-24  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/README: Added a producer slowmotion example.  
+
+  * src/modules/motion_est/Makefile, src/modules/motion_est/factory.c,
+  src/modules/motion_est/filter_motion_est.c,
+  src/modules/motion_est/filter_motion_est.h,
+  src/modules/motion_est/producer_slowmotion.c: Import the proof of concept
+  slow motion producer. It provides basic slow motion through frame repeats and
+  a more advanced interpolation.  
+
+2005-10-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/filter_shape.c: + Correction for non-zero in point on the
+  associated cut  
+
+2005-10-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pango.c: + Moved ~ to LF hack to pango processing
+   
+
+  * src/modules/sdl/consumer_sdl_still.c: + Rounding errors corrected for last
+  gasp scaling  
+
+2005-10-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: + Deadlock resolution  
+
+2005-10-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+  src/modules/core/filter_luma.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c: + Added an option to override alignment
+  and transparent borders for compositing  
+
+2005-10-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/filter_shape.c: + Corrections, optimisations and a hack
+  for loading lumas from the mlt luma collection  
+
+2005-10-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/configure, src/modules/sdl/factory.c: + Correction for
+  uninstalled sdl image lib  
+
+  * configure: + OS/X Tiger patch  
+
+  * src/framework/mlt_events.h, src/framework/mlt_types.h: gcc/g++ 4.x fix  
+
+  * src/humperdink/client.c, src/humperdink/io.c, src/humperdink/io.h,
+  src/humperdink/remote.c, src/inigo/io.c: Remove OS/X warning re: get_string  
+
+  * src/framework/mlt.h, src/inigo/inigo.c: + Whoops - removed dependency on
+  sdl in the framework for darwin  
+
+  * src/modules/sdl/producer_sdl_image.c: + Surface conversion  
+
+  * src/modules/sdl/producer_sdl_image.h: + Added producer_sdl_image as an
+  alternative image and image sequence producer  
+
+  * src/modules/fezzik.dict, src/modules/sdl/Makefile,
+  src/modules/sdl/configure, src/modules/sdl/factory.c,
+  src/modules/sdl/producer_sdl_image.c: + Added producer_sdl_image as an
+  alternative image and image sequence producer  
+
+2005-10-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: + Clean ups and corrections  
+
+2005-09-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/filter_avcolour_space.c: + Extracts alpha from rgb24a
+  images  
+
+2005-09-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+  src/modules/core/filter_rescale.c, src/modules/core/filter_resize.c,
+  src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+  src/modules/core/transition_composite.c,
+  src/modules/feeds/PAL/etv.properties: src/framework/mlt_frame.c + Corrections
+  for resizing images and alpha (uneven widths)  src/framework/mlt_tractor.c +
+  Added an output aspect ratio (being the aspect ratio of the background) 
+  src/modules/core/filter_rescale.c + Force a rescale of the alpha in parallel
+  with image  src/modules/core/filter_resize.c + Rounding errors corrections 
+  src/modules/core/filter_watermark.c + Propogation of output aspect ratio in
+  reverse case  src/modules/core/producer_colour.c + Reassign aspect ratio
+  after get_image  src/modules/core/transition_composite.c + More uneven width
+  corrections + Use of output aspect ratio when available 
+  src/modules/feeds/PAL/etv.properties + Temporary work around to keep
+  composites correct  
+
+2005-09-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: + Correction and a minor
+  optimisation  
+
+  * src/modules/gtk2/producer_pixbuf.c: + Changed incorrect global variable to
+  static  
+
+  * src/modules/avformat/consumer_avformat.c:
+  src/modules/avformat/consumer_avformat.c + User specified pixel format
+  property (pix_fmt) + Corrections to aspect ratio + Alpha channel added to
+  RGBA32 conversions - Removed an historical/erroneous attempt to hack aspect
+  ratio  
+
+2005-09-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: + Indicator for missing media
+  replacement in case pango doesn't exist  
+
+  * src/modules/plus/filter_charcoal.c: + Bounds checking on chroma samples  
+
+  * src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/producer_avformat.c: filter_avcolour_space.c +
+  Correction for uneven width  filter_avdeinterlace.c + Correction for cases
+  were the interlace state of frame is only known after rendering 
+  producer_avformat.c + Corrections for uneven width + Corrections for state
+  propogation of top field first and interlaced state  
+
+  * src/modules/xine/filter_deinterlace.c: + Correction for cases where the
+  interlaced state is determined after the image is rendered  
+
+2005-09-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_producer.c, src/modules/avformat/configure,
+  src/modules/avformat/producer_avformat.c, src/modules/core/filter_mirror.c,
+  src/modules/core/producer_colour.c, src/modules/core/transition_composite.c,
+  src/modules/plus/filter_sepia.c, src/modules/plus/transition_affine.c,
+  src/modules/sdl/consumer_sdl.c: src/framework/mlt_frame.c + Removed
+  unecessary even pixel position and width dependency + Rewrote resize methods
+  to accomodate uneven widths  src/framework/mlt_frame.h + Correct RGB2YUV -
+  now 2^10 based and range checks removed (not needed) 
+  src/framework/mlt_producer.c + Check for unspecified eof property 
+  src/modules/avformat/producer_avformat.c + Provide forced aspect ratio
+  property  src/modules/core/filter_mirror.c + Correction for uneven width 
+  src/modules/core/producer_colour.c + Corrections for aspect ratio (default to
+  0) and allow override + Corrections for uneven width 
+  src/modules/core/transition_composite.c + Corrections for uneven pixel
+  position and width + Removed deprecated operator code 
+  src/modules/plus/filter_sepia.c + Corrections for uneven width 
+  src/modules/plus/transition_affine.c + Corrections for uneven width 
+  src/modules/sdl/consumer_sdl.c + Corrections for uneven width  
+
+2005-09-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_types.h, src/modules/avformat/filter_avcolour_space.c,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_luma.c, src/modules/core/transition_composite.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+  src/framework/mlt_consumer.c + Added capabilities to allow the application to
+  handle images via the consumer-frame-show event + Added cabilities to allow
+  the application to control the image format  src/framework/mlt_frame.c + Long
+  standing discrepancy resolved - image format is now stored on the frame
+  object  src/framework/mlt_tractor.c src/framework/mlt_types.h + Added
+  mlt_image_opengl which is supposed to provide an rgb image swapped around for
+  the platform  src/framework/mlt_frame.h + Added a basic YUV2RGB macro 
+  src/modules/avformat/filter_avcolour_space.c + Added a converter for the
+  opengl swapped RGB image + Corrected support for rgb24a requests 
+  src/modules/core/configure src/modules/core/factory.c + Added an alias for
+  color (since it seems to trouble so many people) 
+  src/modules/core/filter_luma.c + Added the format property to the generated
+  frame  src/modules/core/transition_composite.c + Added the format property to
+  the generated frame  src/modules/gtk2/producer_pixbuf.c + Swapped some
+  properties to hidden from the serialiser  src/modules/sdl/consumer_sdl.c +
+  Support for application provided previews and colour space conversion 
+  src/modules/sdl/consumer_sdl_preview.c + Partial switch to
+  mlt_properties_pass_list + Application provided preview support added 
+  src/modules/sdl/consumer_sdl_still.c + Application provided preview support
+  added  
+
+2005-09-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_invert.c: + Small mod to allow better use of invert
+  as a gui item selector (alpha property)  
+
+2005-09-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c:
+  consumer_sdl.c consumer_sdl_still.c + Corrections to silly mistake regarding
+  initialisation from previous checkin  
+
+  * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+  src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+  src/modules/vmfx/filter_chroma.h, src/modules/vmfx/filter_chroma_hold.c,
+  src/modules/vmfx/filter_chroma_hold.h, src/modules/vmfx/filter_shape.c,
+  src/modules/vmfx/filter_shape.h, src/modules/vmfx/producer_pgm.c,
+  src/modules/vmfx/producer_pgm.h: + Changed license of plugins to LGPL + Added
+  a chroma hold filter + Small optimisation/correction to chroma filter  
+
+2005-08-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: + Keyboard handling events on Darwin  
+
+  * src/modules/lumas/Makefile, src/modules/sdl/consumer_sdl.c: lumas/Makefile
+  + Correction for non-gui app build on darwin  lumas/luma.c + Handle sdl
+  events  sdl/consumer_sdl.c + Audio on Darwin  
+
+  * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: src/modules/sdl/consumer_sdl.c
+  src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+  Corrections to preview mode switching  
+
+  * src/modules/sdl/consumer_sdl_preview.c:
+  src/modules/sdl/consumer_sdl_preview.c + Temporary rollback for linux  
+
+  * configure, src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: configure + Correction to ldflags for
+  Darwin  src/modules/avformat/Makefile src/modules/avformat/configure +
+  Correction for avformat on Darwin  src/modules/sdl/consumer_sdl.c
+  src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+  Forgot to create the surface on the start (doh)  
+
+  * configure, src/framework/mlt.h, src/inigo/inigo.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: configure + Darwin sdl linking and
+  cflags on all use of mlt (annoying, but looks unavoidable) 
+  src/framework/mlt.h + Include sdl header on Darwin  src/inigo/inigo.c +
+  Correction for Darwin key reading from terminal 
+  src/modules/sdl/consumer_sdl.c src/modules/sdl/consumer_sdl_preview.c
+  src/modules/sdl/consumer_sdl_still.c + Moved initialisation of sdl components
+  to the start/stop methods (Darwin requirement)  
+
+  * src/modules/motion_est/configure: + Correction to the disabled case (should
+  be disable-motion_est and plugins should not be registered)  
+
+2005-08-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/vmfx/Makefile, src/modules/vmfx/configure,
+  src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c,
+  src/modules/vmfx/filter_chroma.h: + Added rudimentary chroma to alpha filter
+  (optimised on green by default)  
+
+2005-08-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.h: src/framework/mlt_properties.c
+  src/framework/mlt_properties.h + Added get and set for int64_t 
+  src/framework/mlt_property.h + Corrected int64_t  
+
+2005-08-26  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/README, .../motion_est/filter_autotrack_rectangle.c:
+  Add the obscure=1 option to filter_autotrack_rectangle and update the README
+  with an example.  
+
+2005-08-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict, src/modules/vmfx/Makefile,
+  src/modules/vmfx/configure, src/modules/vmfx/factory.c,
+  src/modules/vmfx/filter_shape.c, src/modules/vmfx/filter_shape.h,
+  src/modules/vmfx/producer_pgm.c, src/modules/vmfx/producer_pgm.h: + Added
+  VMFX module + New filter (shape) which provides alpha manipulations and an
+  alternative wipe mechanism + New producer (pgm) which provides basic
+  functionality for portable grey maps  
+
+  * src/modules/core/transition_composite.c: + SMP fix - geometry modifications
+  need explicit locking  
+
+2005-08-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.h: + Replaced this with self in new pass
+  functions for C++ compilation  
+
+2005-08-21  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.c, src/framework/mlt_property.h: Introduce some
+  more civilized ways to copy properties. See code comments for usage.  
+
+2005-08-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_tractor.c: + Attempt to ensure that the aspect ratio of
+  the background is the reported ar of the output frame  
+
+  * src/modules/core/transition_composite.c: + Yet another aspect ratio
+  correction for the filter transition (not 100% correct yet...) + Correction
+  for aspect_ratio == 0 case (should honour consumer)  
+
+  * src/modules/avformat/consumer_avformat.c: + Correction for aspect ratio  
+
+  * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+  producer_pango.c producer_pixbuf.c + More efficient use of pixbuf objects and
+  sequences/mlt pango lists  
+
+2005-08-15  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/consumer_westley.c: Fix build errors caused by the
+  (hypothetical) conversion of mlt_position from an int to a float, preserving
+  original behavior.  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_property.c, src/framework/mlt_types.h: Fix build errors
+  caused by the (hypothetical) conversion of mlt_position from an int to a
+  float, preserving original behavior.  
+
+  * src/inigo/inigo.c, src/modules/core/filter_luma.c,
+  src/modules/motion_est/filter_crop_detect.c, src/modules/sdl/consumer_sdl.c:
+  Fix build errors caused by the (hypothetical) conversion of mlt_position from
+  an int to a float, preserving original behavior.  
+
+2005-08-07  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/filter_vismv.c: Misc changes. May remove this file
+  completely soon.  
+
+  * src/modules/motion_est/filter_motion_est.c: This is a significant rewrite.
+  -Cleared up as many conceptualy sticky points as possible. -Removed chroma
+  comparison code pending a better rewrite. -Added show_residual=1 and
+  show_reconstruction=1 debug modes. See README. -Renamed many variables and
+  functions. -Revamped geometry handling. -Lots more I'm forgeting.  
+
+  * src/modules/motion_est/README: Added some more examples.  
+
+  * src/inigo/inigo.c: Prevent a frame from being skipped when inigo is first
+  paused.  
+
+  * src/modules/motion_est/filter_crop_detect.c: Corrected geometry handling.
+  Removed redundant arrow drawing code. Modified thresholding.  
+
+2005-08-04  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure: ffmpeg split
+  of the libavutil library.  
+
+2005-07-30  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/README, src/modules/motion_est/filter_motion_est.c:
+  Added a README file with lots of juicy info. Added a denoise motion vectors
+  function, enabled by default; the results seem very good. Removed some unused
+  development code.  
+
+2005-07-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/Makefile: + Allow header dependency checks  
+
+  * src/modules/avformat/configure: + Added an additional help message (for
+  ffmpeg suffix)  
+
+  * Makefile: + Force dependency checks on header files  
+
+2005-07-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_types.h: Do not break ABI to workaround a problem in
+  swig.  
+
+2005-07-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/producer_kino.c: + Stores the resource correctly (to allow
+  serialisation via westley)  
+
+2005-07-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_types.h: Add names to enums to make newer versions of
+  swig (noticed on 1.3.24) happy.  
+
+2005-07-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_watermark.c: + Correction to long outstanding
+  oddity regarding composite.out - not needed in many cases now  
+
+2005-07-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/riff.cc: + Minor correction for entry length being less
+  than the data length  
+
+  * src/modules/kino/avi.cc, src/modules/kino/avi.h, src/modules/kino/riff.cc,
+  src/modules/kino/riff.h: + fixes for opendml dv avi  
+
+2005-07-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: - Removed 'resize' property logic and
+  width/height confusion  
+
+  * src/modules/core/filter_resize.c: + Correction for rounding errors  
+
+2005-07-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/producer_libdv.c: - Removed unused aspect ratio property  
+
+  * src/modules/avformat/producer_avformat.c: + Hide internal properties via
+  the _ convention  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_service.c: - Remove
+  warnings  
+
+2005-07-21  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/filter_motion_est.c: autotrack_rectangle and
+  motion_est now convert pixel units to macroblock (whole) units the same way. 
+
+  * .../motion_est/filter_autotrack_rectangle.c: Fixed several accuracy issues.
+  Cleaned up code. Corrected pause behavior.  
+
+2005-07-20  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * .../motion_est/filter_autotrack_rectangle.c: use shared arrow drawing code.
+  improve tracking accuracy.  
+
+2005-07-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_filter.c, src/framework/mlt_service.c: mlt_filter.c
+  mlt_service.c + Filter disable property  
+
+2005-07-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pango.c: producer_pango.c + Correction of
+  oversight - allow serialisation of mpl usage  
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: consumer_avformat.c
+  producer_avformat.c + Sync with current ffmpeg CVS - PLEASE UPDATE FFMPEG
+  FIRST  
+
+2005-07-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pango.c: + Mutex protection (temporary work
+  around of SMP systems) + Corrected aspect ratio (should be 1, not 0)  
+
+  * src/modules/core/producer_colour.c: + Accepts modifiable colour property
+  (via resource) + Hides non-public properties  
+
+  * src/modules/fezzik.dict: + Added convenience lookup for MLT Pango List
+  files  
+
+  * src/modules/core/filter_mirror.c: + Alpha handling in silly filter :-)  
+
+  * src/modules/core/transition_composite.c: + Inherits deinterlace method from
+  the consumer + Sanity check on scaled size for compositing  
+
+  * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+  producer_pango.c + Added cloning + Added the very silly .mpl (MLT Pango List)
+  format [details to follow] + Corrected invalid content  producer_pixbuf.c +
+  Corrected invalid content  
+
+  * src/modules/gtk2/producer_pixbuf.c: + Bug fixes to test card handling +
+  Alpha channel cloning + Minor tidy up  
+
+2005-07-16  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_service.c,
+  src/framework/mlt_tractor.c, src/modules/core/filter_resize.c,
+  src/modules/core/filter_transition.c,
+  src/modules/core/transition_composite.c, src/modules/dv/producer_libdv.c:
+  rc/framework/mlt_frame.c + image_count added to assist the 'transition
+  filter' in knowing when to act...  src/framework/mlt_playlist.c + Complete
+  rework of fx cuts - now only the fx are output on a frame 
+  src/framework/mlt_producer.c + Aspect ratio of cuts inherited from parent 
+  src/framework/mlt_service.c + Get frame reworked and cleaned up 
+  src/framework/mlt_tractor.c - Removed erroneous width/height pass down prior
+  to image fetching + Corrected types on other properties for pass down +
+  Complete rework of fx cuts - they're now received as producer-less frames
+  from a track + Added image_count logic for transition filter assistance 
+  src/modules/core/filter_resize.c + Added state retention of aspect ratio (may
+  withdraw this later - it assumes producer knows a/r on frame creation/prior
+  to image fetch)  src/modules/core/filter_transition.c + Checks that two
+  images are available before processing + Checks test image/audio cases 
+  src/modules/core/transition_composite.c + Major correction in aspect ratio
+  handling (the b frame image is 'distorted' to the consumers aspect ratio) +
+  Minor clean up of silly and/or/xor - now have 'operator=[and/or/xor]' (more
+  clean up to follow)  src/modules/dv/producer_libdv.c + Frame stored width and
+  height are no longer assumed to be 'safe' here (investigating)  
+
+2005-07-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_repository.c: mlt_repository.c + VERY temporary hack to
+  avoid global symbol clashes (RTLD_GLOBAL needed by kino/libquicktime only so
+  far)  
+
+2005-07-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/filehandler.cc: filehandler.cc + FOURCC for DVCPRO
+  quicktime  
+
+  * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c + Oops - the frame
+  position is relative to the in point (the internal position is absolute)  
+
+  * src/modules/vorbis/producer_vorbis.c: producer_vorbis.c + Fix for non-zero
+  in point  
+
+2005-07-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: consumer_sdl_preview.c
+  consumer_sdl_still.c + Fixes a deadlock condition  
+
+  * src/modules/kino/filehandler.cc: src/modules/kino/filehandler.cc + Added
+  missing fourccs to allow compilation  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_repository.c, src/modules/kino/filehandler.cc,
+  src/modules/kino/filehandler.h: framework/mlt_frame.c framework/mlt_frame.h +
+  Added sample calculator (samples to current frame) 
+  framework/mlt_repository.c + Symbols exported from plugins 
+  modules/kino/filehandler.cc modules/kino/filehandler.h + Audio handling of dv
+  mov  
+
+2005-07-09  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/configure: Mention that motion est is disabled by
+  default during ./configure.  
+
+  * configure, src/modules/motion_est/configure: Prevent motion estimation
+  components from building unless requested.  
+
+2005-07-08  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/motion_est/Makefile, src/modules/motion_est/configure: removed
+  a debugging target.  
+
+  * src/modules/motion_est/Makefile, src/modules/motion_est/arrow_code.c,
+  src/modules/motion_est/arrow_code.h, src/modules/motion_est/configure,
+  src/modules/motion_est/factory.c,
+  .../motion_est/filter_autotrack_rectangle.c,
+  src/modules/motion_est/filter_crop_detect.c,
+  src/modules/motion_est/filter_motion_est.c,
+  src/modules/motion_est/filter_motion_est.h,
+  src/modules/motion_est/filter_vismv.c, src/modules/motion_est/sad_sse.h:
+  Initial import of the motion estimation filter.  
+
+2005-07-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c:
+  src/modules/avformat/consumer_avformat.c + Correction for mpeg encoding -
+  Removal of erroneous frame rate checks  
+
+2005-07-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c:
+  src/modules/westley/producer_westley.c - Rollback on erroneous checkin
+  (functionality covered correctly in playlist)  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/fezzik.dict, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/westley/producer_westley.c: src/framework/mlt_frame.c +
+  Correction for aspect ratio of synthesized test card 
+  src/framework/mlt_playlist.c + Special case for handling fx cuts 
+  src/modules/fezzik.dict + Convenience jfx and jef extensions for jahshaka 
+  src/modules/core/transition_composite.c + Ensure that scaling and correct
+  image extraction is handled  src/modules/core/transition_luma.c + Ensure that
+  scaling and correct image extraction is handled 
+  src/modules/gtk2/producer_pixbuf.c + Allow user overrides for progressive and
+  aspect_ration  src/modules/westley/producer_westley.c + Special case for fx
+  cuts  
+
+2005-06-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: src/modules/sdl/consumer_sdl.c + (Re)Added
+  audio volume control  
+
+  * src/framework/mlt_tractor.c: src/framework/mlt_tractor.c + Added support
+  for pango usage on audio only fx cuts (sigh...)  
+
+  * src/framework/mlt_tractor.c: src/framework/mlt_tractor.c + Slight
+  modification to allow pango use in fx cuts  
+
+2005-06-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_transition.c, src/modules/core/filter_transition.h:
+  src/modules/core/filter_transition.c src/modules/core/filter_transition.h +
+  Initial release  
+
+  * src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+  src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h: src/framework/mlt_deque.c
+  src/framework/mlt_deque.h + Added support for doubles 
+  src/framework/mlt_frame.c + Switched order of source/dest audio mix
+  extraction (for transition as filter usage)  src/framework/mlt_tractor.c -
+  Removed warning introduced from previous checkin (missing ctype.h) +
+  Temporary work around to allow frames to carry multiple frames (for
+  transition as filter usage)  src/modules/core/Makefile
+  src/modules/core/configure src/modules/core/factory.c + Support for new
+  transition filter :-)  src/modules/core/transition_composite.c
+  src/modules/core/transition_composite.h - Removed frame properties dependence
+  for process/get_image state communication + Extended alpha blending modes to
+  'and' and 'xor' logic (may change property triggering soon) + Provided
+  support for transition as filter usage + Cleaned up public copy region
+  functionality  
+
+  * src/modules/core/transition_composite.c: + Cleaned up compositing and alpha
+  usage (all frames always have an alpha mask) + Provided an alternative
+  rendering mechanism ('or' which takes a and b alpha into account) + Provided
+  a and b alpha mask overides ('alpha_a' and 'alpha_b')  
+
+2005-06-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_geometry.c, src/framework/mlt_tractor.c,
+  src/modules/core/transition_composite.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl_still.c: src/framework/mlt_geometry.c
+  src/modules/core/transition_composite.c src/modules/sdl/consumer_sdl.c
+  src/modules/sdl/consumer_sdl_still.c + replaced floats with doubles (attempt
+  to avoid rounding errors?)  src/framework/mlt_tractor.c + corrections for
+  fx_cuts (allows animated fx)  
+
+2005-06-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: + BGa's request for additional westley extensions 
+
+2005-06-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+  src/modules/core/filter_watermark.c, src/modules/core/producer_noise.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+  src/framework/mlt_consumer.c + Attempt to make all frames have the correct
+  aspect_ratio (works in many but not all cases)  src/framework/mlt_frame.h +
+  Provide macro access to the video and image RPN queues 
+  src/framework/mlt_tractor.c + Provides orphaned filters 
+  src/modules/core/producer_noise.c - remove specification of aspect ratio 
+  src/modules/core/filter_watermark.c src/modules/core/transition_composite.c
+  src/modules/core/transition_luma.c src/modules/plus/filter_affine.c
+  src/modules/plus/transition_affine.c + Corrections for frames with an aspect
+  ratio = 0 (supplement to mlt_consumer mod)  
+
+2005-06-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_producer.c, src/inigo/inigo.c,
+  src/modules/avformat/consumer_avformat.c, src/modules/core/filter_resize.c,
+  src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+  src/modules/dv/consumer_libdv.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/kino/Makefile,
+  src/modules/kino/avi.cc, src/modules/kino/avi.h, src/modules/kino/configure,
+  src/modules/kino/filehandler.cc, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+  src/framework/mlt_consumer.c src/framework/mlt_consumer.h + Added a general
+  profile handling for size, aspect ratio and display ratio 
+  src/framework/mlt_producer.c + Correction to aspect ratio properties 
+  src/inigo/inigo.c + Minimalist support for sdl_preview (still not very good) 
+  src/modules/avformat/consumer_avformat.c + Takes consumer profile into
+  account  src/modules/core/filter_resize.c + Corrections for synthesised
+  producers and aspect ratio (inherits from consumer) 
+  src/modules/core/producer_colour.c src/modules/core/producer_noise.c
+  src/modules/gtk2/producer_pango.c + Ensures that resize picks up consumer
+  aspect ratio  src/modules/dv/consumer_libdv.c + Honour wide screen output 
+  src/modules/gtk2/producer_pixbuf.c + Correction for 1:1 aspect ratio 
+  src/modules/kino/Makefile src/modules/kino/avi.cc src/modules/kino/avi.h
+  src/modules/kino/configure src/modules/kino/filehandler.cc + Attempt to allow
+  mov dv files to provide audio  src/modules/sdl/consumer_sdl.c
+  src/modules/sdl/consumer_sdl_preview.c src/modules/sdl/consumer_sdl_still.c +
+  Takes consumer profile into account  
+
+2005-06-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/configure: Quick temporary fix for mlt config in non-standard
+  paths (relates to mlt++)  
+
+2005-06-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_tractor.c,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/xine/filter_deinterlace.c: Consumer deinterlace_method property
+  added  
+
+  * src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/core/filter_resize.c, src/modules/xine/filter_deinterlace.c:
+  Sanity checks for normalising filters  
+
+2005-06-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik.dict: libdv/avformat switching  
+
+2005-06-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/filter_avcolour_space.c: Sanity checks  
+
+  * src/modules/gtk2/producer_pixbuf.c: Fallback to testcard  
+
+2005-05-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: NTSC fix  
+
+  * src/modules/fezzik.dict: Added bmp support  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+  src/framework/mlt_producer.c: Frame rate properites and factory
+  initialisation  
+
+2005-05-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: audio out fix  
+
+2005-05-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/filehandler.cc, src/modules/kino/filehandler.h: DVCPRO fix
+   
+
+2005-05-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c: jpeg and mjpeg fixes  
+
+2005-05-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/jackrack/filter_ladspa.c: bugfix segfault on closre when filter
+  never invoked  
+
+2005-05-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c: Build modification to ffmpeg/avformat  
+
+2005-05-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/configure, src/modules/gtk2/configure,
+  src/modules/jackrack/configure, src/modules/kino/configure,
+  src/modules/resample/configure, src/modules/sdl/configure,
+  src/modules/sox/configure, src/modules/vorbis/configure,
+  src/modules/westley/configure, src/modules/xine/configure: Bourne shell
+  compliance  
+
+  * configure: Bourne shell compliance  
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure: Corrections
+  to --avformat-cvs option  
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c,
+  src/modules/avformat/producer_avformat.c: FFMPEG revisions to match current
+  CVS (part 1)  
+
+2005-05-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/Makefile: fix compilation  
+
+2005-04-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/modules/configure, src/modules/jackrack/Makefile,
+  src/modules/jackrack/configure, src/modules/jackrack/control_message.h,
+  src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c,
+  src/modules/jackrack/filter_ladspa.c, src/modules/jackrack/filter_ladspa.h,
+  src/modules/jackrack/jack_rack.c, src/modules/jackrack/jack_rack.h,
+  src/modules/jackrack/plugin.c, src/modules/jackrack/plugin.h,
+  src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_mgr.c,
+  src/modules/jackrack/plugin_mgr.h, src/modules/jackrack/process.c,
+  src/modules/jackrack/process.h, src/modules/jackrack/ui.c,
+  src/modules/jackrack/ui.h: cleanup and reduce code in jackrack support code
+  and add new jack-less filter_ladspa.  
+
+2005-04-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/producer_libdv.c: Fix for file identification and dv  
+
+2005-04-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/kino/avi.h: Minor correction  
+
+  * src/modules/kino/Makefile, src/modules/kino/avi.cc, src/modules/kino/avi.h,
+  src/modules/kino/configure, src/modules/kino/endian_types.h,
+  src/modules/kino/error.cc, src/modules/kino/error.h,
+  src/modules/kino/factory.c, src/modules/kino/filehandler.cc,
+  src/modules/kino/filehandler.h, src/modules/kino/kino_wrapper.cc,
+  src/modules/kino/kino_wrapper.h, src/modules/kino/producer_kino.c,
+  src/modules/kino/producer_kino.h, src/modules/kino/riff.cc,
+  src/modules/kino/riff.h: Initial version  
+
+  * src/modules/dv/producer_libdv.c, src/modules/fezzik.dict: Preparation for
+  kino support  
+
+2005-04-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/dv/Makefile: corrected pkg-config libdv usage  
+
+2005-04-14  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl_still.c: Build
+  fixes.  
+
+  * src/modules/sdl/consumer_sdl.c: An unfinished attempt at porting the SDL
+  consumer to OS X. What remains is a bug in libSDL where the SDL screen object
+  becomes a NULL pointer when it shouldn't. This also affects 'ffplay' and the
+  SDL test program 'threadwin -threaded' I think.  
+
+  * src/modules/sdl/consumer_sdl_osx_hack.h: A hack to inform Cocoa that is
+  should be multithreaded by spinning of a dummy thread.  
+
+  * configure, src/albino/albino.c, src/inigo/inigo.c, src/miracle/miracle.c:
+  OS X uses -DDARWIN in
+  /System/Library/Frameworks/CoreFoundation.framework/Headers/CFBase.h; This in
+  combination with #include <Foundation/Foundation.h> caused compilation errors
+  while porting consumer_sdl to OS X.  
+
+2005-04-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * COPYING: License update  
+
+  * src/modules/sox/Makefile, src/modules/sox/configure: Disable sox when
+  unavailable  
+
+  * src/modules/jackrack/configure: Disable jackrack when unavailable  
+
+  * src/modules/dv/configure, src/modules/vorbis/configure: Disable libdv when
+  unavailable  
+
+  * src/modules/resample/configure: Disable libsamplerate when unavailable  
+
+  * src/modules/sdl/configure: Disable sdl when unavailable  
+
+  * src/modules/vorbis/configure: Disable vorbis when unavailable  
+
+  * configure: Automatic disabling off mmx on a OS/X; mmx detection on Linux;
+  other platforms probably broken  
+
+  * src/modules/xine/configure: Disable xine when mmx not available  
+
+  * src/modules/westley/configure: Conditional compilation of westley/libxml2
+  components  
+
+  * src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+  src/modules/gtk2/factory.c: Conditional compilation of gtk2 components  
+
+2005-04-12  dezeroex <dezeroex@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_geometry.c: Minor but confusing comment fix.  
+
+2005-04-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, setenv, src/albino/Makefile, src/albino/albino.c,
+  src/framework/Makefile, src/humperdink/Makefile, src/humperdink/io.c,
+  src/inigo/Makefile, src/inigo/inigo.c, src/inigo/io.c, src/miracle/Makefile,
+  src/miracle/miracle.c, src/modules/avformat/Makefile,
+  src/modules/avformat/configure, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/dv/Makefile,
+  src/modules/dv/configure, src/modules/fezzik/Makefile,
+  src/modules/fezzik/configure, src/modules/gtk2/Makefile,
+  src/modules/gtk2/configure, src/modules/inigo/Makefile,
+  src/modules/inigo/configure, src/modules/jackrack/Makefile,
+  src/modules/jackrack/configure, src/modules/normalize/Makefile,
+  src/modules/normalize/configure, src/modules/plus/Makefile,
+  src/modules/plus/configure, src/modules/resample/Makefile,
+  src/modules/resample/configure, src/modules/sdl/Makefile,
+  src/modules/sdl/configure, src/modules/sox/Makefile,
+  src/modules/sox/configure, src/modules/valerie/Makefile,
+  src/modules/valerie/configure, src/modules/vorbis/Makefile,
+  src/modules/vorbis/configure, src/modules/westley/Makefile,
+  src/modules/westley/configure, src/modules/xine/Makefile,
+  src/modules/xine/configure, src/tests/Makefile, src/valerie/Makefile,
+  src/valerie/valerie_socket.c: OS/X Patch from Torsten Spindler  
+
+  * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_repository.c, src/framework/mlt_repository.h: More const
+  usage  
+
+2005-04-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/modules/gtk2/Makefile,
+  src/modules/resample/filter_resample.c: Auto deinterlace on pause, fix for
+  audio resampling/test audio and MMX checks in gtk2  
+
+2005-04-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c,
+  src/modules/gtk2/Makefile, src/modules/jackrack/filter_jackrack.c,
+  src/modules/sox/filter_sox.c: avformat-cvs build fix and audio filter
+  correction  
+
+2005-04-05  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/albino.c, src/miracle/miracle.c: make miracle and albino local
+  use fifo instead of rr rt schedule  
+
+  * src/albino/albino.c, src/framework/mlt_consumer.c, src/inigo/inigo.c,
+  src/miracle/miracle.c, src/miracle/miracle_server.c,
+  src/modules/avformat/consumer_avformat.c, src/modules/core/consumer_null.c,
+  src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+  src/modules/fezzik/producer_hold.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c, src/modules/xine/filter_deinterlace.c:
+  realtime scheduling updates; suppress libdv errors; add frame property
+  deinterlace_method; default producer_hold to use onefield; add begin property
+  to producer_pixbuf  
+
+2005-03-16  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_producer.c: Frame rendering
+  event  
+
+2005-03-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/dvcp.txt, src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+  src/miracle/miracle_unit_commands.h, src/modules/avformat/factory.c,
+  src/valerie/valerie.c, src/valerie/valerie.h: Threading considerations and
+  DVCP WIPE introduced  
+
+2005-03-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_producer.c,
+  src/modules/core/transition_composite.c,
+  src/modules/plus/transition_affine.c: Minor corrections and more affine
+  experiments  
+
+2005-02-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_unit.c, src/modules/avformat/consumer_avformat.c: Minor
+  mods to playout via avformat and miracle unit generation on an xfer  
+
+  * src/modules/westley/producer_westley.c: Reinstatement of entity handling
+  and removal of libxml2 warning for non-existent file  
+
+2005-02-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/producer_colour.c,
+  src/modules/core/transition_composite.c,
+  src/modules/plus/transition_affine.c: Minor corrections with alpha and
+  affines  
+
+2005-02-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_unit.c: Smoother unit load  
+
+2005-02-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c, src/framework/mlt_tractor.c,
+  src/modules/core/producer_colour.c, src/modules/core/transition_composite.c,
+  src/modules/feeds/PAL/etv.properties, src/modules/gtk2/producer_pango.c,
+  src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c,
+  src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Alphas and global feeds revisted  
+
+2005-02-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: Speed switch corrections  
+
+2005-02-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c: Optional 8 or 16 bit pgm or png lumas;
+  fixes for non-existence  
+
+  * src/modules/lumas/configure, src/modules/lumas/create_lumas: Optional 8 or
+  16 bit pgm or png  
+
+2005-02-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+  more affine silliness  
+
+2005-02-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/transition_affine.c: affine silliness  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_frame.c, src/framework/mlt_tractor.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: SMP/HT fixes  
+
+2005-02-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/feeds/PAL/border.properties: fill for borders  
+
+  * src/modules/gtk2/Makefile: conditional mmx compilation  
+
+  * src/modules/core/transition_composite.c: int handling on the frame image
+  stack  
+
+  * src/framework/mlt_deque.c, src/framework/mlt_deque.h,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h: 64 bit fix and deque
+  int holding  
+
+2005-01-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c: Refresh count instead of flag  
+
+  * src/modules/sdl/consumer_sdl_preview.c: Mutex locking for refresh handling 
+
+  * src/modules/core/filter_rescale.c: Warning removal  
+
+  * src/modules/resample/filter_resample.c: Workaround for test card audio (may
+  need to review)  
+
+  * src/modules/inigo/producer_inigo.c: Empty track definition fix  
+
+  * src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: Consumer reworked  
+
+  * src/modules/plus/transition_affine.c: Pointless improvement on a bad filter
+  :-)  
+
+  * src/modules/gtk2/producer_pango.c: Memory leak fix  
+
+  * src/modules/westley/consumer_westley.c: titles and global feeds  
+
+  * src/modules/feeds/PAL/border.properties,
+  src/modules/feeds/PAL/data_fx.properties: Minor corrections  
+
+  * src/modules/core/filter_data_show.c: Global/local data show distinction  
+
+  * src/modules/core/Makefile: Removed superflous mmx compilation  
+
+  * src/framework/mlt_tractor.c: Global data feed handling  
+
+  * src/framework/mlt_filter.c, src/framework/mlt_service.c: Wild card filter
+  tracks  
+
+  * src/framework/mlt_events.c: Memory leak fix  
+
+  * src/framework/mlt_consumer.c: Small correction to deinterlacing  
+
+2005-01-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/feeds/PAL/border.properties,
+  src/modules/feeds/PAL/example.properties: Test case feeds added  
+
+  * src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_channelcopy.c, src/modules/core/filter_watermark.c,
+  src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/core/transition_mix.c, src/modules/core/transition_region.c,
+  src/modules/dv/producer_libdv.c, src/modules/feeds/PAL/etv.properties,
+  src/modules/jackrack/filter_jackrack.c,
+  src/modules/normalize/filter_volume.c, src/modules/plus/transition_affine.c,
+  src/modules/resample/filter_resample.c, src/modules/sox/filter_sox.c,
+  src/modules/vorbis/producer_vorbis.c: Remaining audio handling switched to
+  stacks; Minor corrections to compositing and mixing; localisation for pango  
+
+  * src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Localised data storage and utf-8
+  properties  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_producer.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h: Transitions reworked (always_active
+  capabilities); remaining audio handling switched to stacks  
+
+  * demo/mlt_news: Correction for audio mix  
+
+2005-01-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c: consumer close fix  
+
+  * src/modules/feeds/PAL/etv.properties, src/modules/gtk2/producer_pango.c:
+  iconv fixes  
+
+2005-01-16  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_slideshow_black, docs/services.txt,
+  src/modules/core/transition_composite.c,
+  src/modules/feeds/PAL/etv.properties: Minor modifications to compositing
+  options and etv fx  
+
+  * src/modules/gtk2/producer_pango.c: Added a weight property  
+
+2005-01-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_attributes: Correction for ETV specific filters  
+
+  * src/modules/feeds/PAL/etv.properties: Seperation for ETV specific filters  
+
+  * docs/testing.txt: Test case clean up  
+
+  * demo/demo, demo/mlt_watermark, src/framework/mlt_producer.c,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.c, src/framework/mlt_property.h,
+  src/framework/mlt_tractor.c, src/modules/core/filter_data_show.c,
+  src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c,
+  src/modules/feeds/PAL/data_fx.properties,
+  src/modules/feeds/PAL/obscure.properties, src/modules/fezzik.ini,
+  src/modules/gtk2/producer_pango.c: Sundry minor fixes and optimisations  
+
+2005-01-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_geometry.c: Corrections to geometry next key and
+  serialise  
+
+2005-01-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_geometry.c, src/framework/mlt_geometry.h: Next/Prev key
+  extraction  
+
+  * src/modules/feeds/PAL/data_fx.properties,
+  src/modules/feeds/PAL/obscure.properties: Smaller mask width/height  
+
+  * src/miracle/miracle_server.c, src/miracle/miracle_server.h: Fetch unit from
+  miracle server  
+
+2005-01-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c: Correction to clip_start at end of playlist  
+
+2004-12-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/demo.ini, src/framework/mlt_producer.c,
+  src/framework/mlt_properties.c, src/framework/mlt_property.c,
+  src/framework/mlt_transition.c: Corrections after valgrinding  
+
+  * demo/demo.ini, demo/mlt_attributes, demo/mlt_news, demo/mlt_slideshow,
+  demo/mlt_slideshow_black, demo/mlt_squeeze, demo/mlt_ticker,
+  demo/mlt_watermark: Corrections and minor fixes to use new geometry spec;
+  couple of new test cases  
+
+  * src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+  src/modules/inigo/producer_inigo.c: Sundry minor updates  
+
+  * src/modules/feeds/NTSC/obscure.properties,
+  src/modules/feeds/PAL/data_fx.properties: Feeds updates  
+
+  * src/framework/mlt_producer.c: Extension to mini fezzik for obscures on cuts
+   
+
+  * src/framework/mlt_tractor.c: Option to hold feed processing on a track  
+
+  * src/framework/mlt_playlist.c: Fix for join length correction  
+
+  * src/framework/mlt_frame.c: Resize fix for chroma offsets  
+
+  * src/framework/mlt_geometry.c, src/framework/mlt_geometry.h: Improved
+  geometry  
+
+2004-12-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/jackrack/filter_jackrack.c: bootstrap earlier with fixed number
+  of channels, better initial synchronisation phase, reduced internal buffer
+  size  
+
+  * src/modules/jackrack/filter_jackrack.c: even better close handling?  
+
+  * src/modules/jackrack/filter_jackrack.c: fixup includes  
+
+  * src/modules/jackrack/filter_jackrack.c: ensure disconnected from jack
+  before releasing any resources  
+
+2004-12-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/modules/jackrack/filter_jackrack.c: add
+  filter/jackrack to services.txt and apply a performance tweak to
+  filter_jackrack  
+
+  * src/modules/jackrack/Makefile, src/modules/jackrack/configure,
+  src/modules/jackrack/control_message.h, src/modules/jackrack/factory.c,
+  src/modules/jackrack/filter_jackrack.c,
+  src/modules/jackrack/filter_jackrack.h, src/modules/jackrack/jack_rack.c,
+  src/modules/jackrack/jack_rack.h, src/modules/jackrack/lock_free_fifo.c,
+  src/modules/jackrack/lock_free_fifo.h, src/modules/jackrack/plugin.c,
+  src/modules/jackrack/plugin.h, src/modules/jackrack/plugin_desc.c,
+  src/modules/jackrack/plugin_desc.h, src/modules/jackrack/plugin_mgr.c,
+  src/modules/jackrack/plugin_mgr.h, src/modules/jackrack/plugin_settings.c,
+  src/modules/jackrack/plugin_settings.h, src/modules/jackrack/process.c,
+  src/modules/jackrack/process.h, src/modules/jackrack/ui.c,
+  src/modules/jackrack/ui.h: added jackrack filter  
+
+  * demo/consumers.ini, docs/services.txt, setenv, setenv_mc,
+  src/modules/dv/producer_libdv.c, src/modules/fezzik.dict,
+  src/modules/fezzik.ini: fix aspect ratios in producer_libdv tweak fezzik
+  priorities minor fixes to setenv and demo/consumers.ini  
+
+2004-12-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_bouncy_ball, demo/mlt_my_name_is, demo/mlt_title_over_gfx,
+  src/framework/mlt_tractor.c, src/modules/core/filter_rescale.c,
+  src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c, src/modules/data_fx.properties,
+  src/modules/feeds/PAL/data_fx.properties, src/modules/plus/filter_affine.c,
+  src/modules/plus/transition_affine.c: Composite distort, fill and titles
+  rework  
+
+  * src/modules/core/transition_composite.c, src/modules/feeds/Makefile: Feeds
+  pseudo module added  
+
+  * src/modules/feeds/Makefile, src/modules/feeds/NTSC/data_fx.properties,
+  src/modules/feeds/PAL/data_fx.properties,
+  src/modules/feeds/PAL/obscure.properties: Feeds pseudo module added  
+
+  * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_geometry.c,
+  src/modules/core/filter_data_show.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/data_fx.properties,
+  src/modules/inigo/producer_inigo.c, src/modules/lumas/create_lumas,
+  src/modules/lumas/luma.c: Luma and composite fixes  
+
+2004-12-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: Luma generation and use  
+
+  * src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/lumas/Makefile,
+  src/modules/lumas/create_lumas, src/modules/lumas/luma.c: Luma generation and
+  use  
+
+  * demo/mlt_bouncy_ball, demo/mlt_push, demo/mlt_ticker,
+  src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_geometry.c,
+  src/framework/mlt_geometry.h, src/framework/mlt_types.h,
+  src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c,
+  src/modules/data_fx.properties, src/modules/xine/deinterlace.c: Framework
+  inclusion of geometry  
+
+2004-12-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/data_fx.properties: Correction to obscure data_show config  
+
+  * src/modules/data_fx.properties: Correction to obscure data_show config  
+
+2004-12-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c, src/modules/core/transition_composite.c,
+  src/modules/data_fx.properties: New geometry specification  
+
+2004-12-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_tractor.c, src/modules/core/filter_data_feed.c,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c, src/modules/data_fx.properties,
+  src/modules/gtk2/producer_pango.c, src/modules/westley/producer_westley.c,
+  src/valerie/valerie_remote.c: Feed rework and fixes to westley and composite 
+
+2004-12-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c: Mutex protection on put frame close  
+
+  * src/framework/mlt_producer.c, src/framework/mlt_service.c: Mutex locking in
+  the get frame  
+
+2004-12-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: blank_at method
+  added  
+
+2004-12-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: split_at method
+  added  
+
+2004-12-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_service.c,
+  src/modules/inigo/producer_inigo.c: Corrections to playlist manipulations and
+  producer type determination  
+
+2004-12-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/modules/data_fx.properties,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+  Possible fixes to xlib errors  
+
+2004-12-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_data_feed.c: ignore attr which are active, but have
+  no value  
+
+  * src/modules/data_fx.properties: Minor mods for ETV data filters  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_factory.c, src/framework/mlt_field.c,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+  src/framework/mlt_properties.c, src/framework/mlt_property.c,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+  src/framework/mlt_types.h, src/inigo/inigo.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit_commands.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/consumer_null.c,
+  src/modules/core/filter_brightness.c, src/modules/core/filter_channelcopy.c,
+  src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c,
+  src/modules/core/filter_gamma.c, src/modules/core/filter_luma.c,
+  src/modules/core/filter_mirror.c, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_region.c, src/modules/core/filter_rescale.c,
+  src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+  src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+  src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/core/transition_mix.c,
+  src/modules/core/transition_region.c, src/modules/data_fx.properties,
+  src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+  src/modules/fezzik.ini, src/modules/fezzik/producer_fezzik.c,
+  src/modules/fezzik/producer_hold.c, src/modules/gtk2/consumer_gtk2.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/producer_inigo.c,
+  src/modules/normalize/filter_volume.c, src/modules/plus/filter_affine.c,
+  src/modules/plus/filter_charcoal.c, src/modules/plus/filter_sepia.c,
+  src/modules/plus/transition_affine.c, src/modules/resample/filter_resample.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c, src/modules/sox/filter_sox.c,
+  src/modules/valerie/consumer_valerie.c, src/modules/vorbis/producer_vorbis.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c,
+  src/modules/xine/filter_deinterlace.c, src/valerie/valerie_remote.c: Big
+  modification - switch to macros for parent class access  
+
+2004-11-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/framework/mlt_service.c, src/framework/mlt_tractor.c,
+  src/modules/sdl/consumer_sdl_still.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Extendable factories; general
+  producer related modifications; westley storage; sdl_still increased latency 
+
+2004-11-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_still.c: Consumer sdl preview correction -
+  attach colour space conversion on start  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_service.c,
+  src/framework/mlt_service.h: More playlist modifications; service locking;
+  sticky services on frame  
+
+2004-11-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c: Extendible blank producers  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/modules/sdl/consumer_sdl_still.c, src/modules/valerie/consumer_valerie.c:
+  Added ref_count method to properties; temporary work around for test card;
+  titles with valerie  
+
+2004-11-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_transition.c,
+  src/modules/dv/consumer_libdv.c: Playlist and blank rearrangement, fix for
+  mlt_consumer and NULL  
+
+2004-11-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_still.c: Increased delay for polling  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: Simplified
+  playlist access  
+
+2004-11-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.c: Behavioural change - tracks with hide
+  properties now affect length (might be problematic)  
+
+2004-11-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c: Correction for direct playback of a cut  
+
+2004-11-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: Fixes threaded pixbuf usage and removes
+  flash when swicthing between sdl preview modes  
+
+2004-10-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_tokeniser.c, src/modules/fezzik.dict,
+  src/modules/gtk2/factory.c, src/modules/inigo/producer_inigo.c,
+  src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/westley/producer_westley.c: fixes for westley deserialise,
+  preview handling and tokenising amendment  
+
+2004-10-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_charcoal.c: Minor optimisation  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_producer.c, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+  src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+  src/modules/core/producer_noise.c, src/modules/core/transition_composite.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c:
+  Attempt at an aspect ratio clean up  
+
+2004-10-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_preview.c: Oops - need to parse the size in
+  the preview  
+
+  * mlt-config-template, src/framework/configure, src/miracle/configure,
+  src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+  src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/consumer_gtk2.h,
+  src/modules/gtk2/factory.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+  src/modules/westley/producer_westley.c, src/valerie/configure: Minor config
+  fixes and gtk2 consumer added  
+
+2004-10-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl_still.c: SDL Preview second checkin  
+
+  * src/framework/mlt_consumer.c, src/inigo/inigo.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c,
+  src/modules/sdl/consumer_sdl_still.c: SDL Preview second checkin  
+
+2004-10-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/modules/sdl/Makefile, src/modules/sdl/configure,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.h,
+  src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c,
+  src/modules/sdl/factory.c: SDL Preview provisional checkin  
+
+2004-10-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/transition_mix.c: audio mix and
+  repeated frames  
+
+2004-10-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/miracle/miracle_server.c,
+  src/miracle/miracle_server.h: Convenience functionality for properties load
+  and miracle_server_id function  
+
+  * src/miracle/miracle_server.c: Server shutdown state oops  
+
+  * src/miracle/miracle_server.c: Server shutdown state  
+
+2004-10-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/valerie/consumer_valerie.c: Error property for valerie returned
+   
+
+2004-10-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/consumer_westley.c, src/valerie/valerie_remote.c:
+  buffer fix and tractor handling  
+
+  * src/miracle/miracle_connection.c, src/miracle/miracle_local.c,
+  src/miracle/miracle_server.c, src/miracle/miracle_unit_commands.c,
+  src/miracle/miracle_unit_commands.h, src/modules/valerie/consumer_valerie.c,
+  src/modules/westley/producer_westley.c, src/valerie/valerie.c,
+  src/valerie/valerie.h, src/valerie/valerie_parser.c,
+  src/valerie/valerie_parser.h, src/valerie/valerie_remote.c: Improved push
+  capabilities  
+
+2004-10-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_service.c, src/modules/fezzik/producer_fezzik.c,
+  src/modules/valerie/consumer_valerie.c,
+  src/modules/westley/producer_westley.c: Fix for deep westleys and filter
+  in/out points  
+
+  * src/framework/mlt_consumer.c: Oops - fix for consumer progressive  
+
+  * docs/services.txt, src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_properties.c,
+  src/framework/mlt_tractor.c, src/inigo/inigo.c,
+  src/miracle/miracle_connection.c, src/miracle/miracle_connection.h,
+  src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+  src/modules/core/filter_rescale.c, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/plus/transition_affine.c: Some fixes for alpha masks  
+
+2004-10-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure, src/modules/avformat/producer_avformat.c:
+  Fix for current cvs  
+
+2004-10-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c: Mix on Mix and length corrections  
+
+2004-10-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/framework.txt, docs/inigo.txt, docs/install.txt: Some documentation
+  updates - more to follow  
+
+  * src/framework/mlt_producer.c: Removed fezzik usage from cloning  
+
+2004-10-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_filter.c, src/framework/mlt_producer.c,
+  src/framework/mlt_service.c, src/framework/mlt_tractor.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/core/filter_data_show.c, src/modules/core/filter_watermark.c,
+  src/modules/plus/filter_affine.c: Revised attached filter handling and clones
+   
+
+2004-10-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+  src/modules/core/transition_mix.c: More corrections to frame position and
+  audio/track handling  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_tractor.c,
+  src/modules/core/transition_mix.c: Corrects position and test_audio handling 
+
+2004-10-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_tractor.c, src/inigo/inigo.c:
+  Multitrack rearrangement and tractor cleanup  
+
+  * src/framework/mlt_parser.c, src/framework/mlt_producer.c: Yikes - another
+  corrections to cloning (oops)  
+
+  * src/framework/mlt_multitrack.c, src/framework/mlt_producer.c: Corrections
+  to cloning  
+
+  * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_factory.c,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_parser.c,
+  src/framework/mlt_parser.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_types.h, src/modules/data_fx.properties,
+  src/modules/inigo/producer_inigo.c, src/modules/plus/filter_affine.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Cloning optimisations and
+  introduction of the service parser  
+
+2004-10-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/inigo/producer_inigo.c: Allow filter attachment to clip  
+
+2004-10-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_service.c,
+  src/framework/mlt_tractor.c, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_data.h, src/modules/core/filter_data_feed.c,
+  src/modules/core/filter_data_show.c, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c, src/modules/data_fx.properties,
+  src/modules/dv/producer_libdv.c, src/modules/inigo/producer_inigo.c: Data
+  feed and show filters  
+
+2004-09-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h: clip and mix manipulation on playlist  
+
+2004-09-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_filter.c, src/framework/mlt_service.c,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c, src/modules/inigo/producer_inigo.c,
+  src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+  Corrections to filter attachment and in/out point handling  
+
+  * src/framework/mlt_playlist.c, src/modules/inigo/producer_inigo.c: Ensure
+  join inherits all attached filters; inigo can attach to producer or previous
+  attachment  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/modules/inigo/producer_inigo.c: Checkpoint
+  for current managed cuts (prototype on mix)  
+
+2004-09-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_rescale.c, src/modules/core/transition_composite.c:
+  First attempt at a composite clean up  
+
+2004-09-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c: Clean up - added new usage options  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/modules/inigo/producer_inigo.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Splits, joins and repeats  
+
+2004-09-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/consumer_westley.c: Fix for serialising multiple
+  overlapping mixes  
+
+  * src/framework/mlt_playlist.c: Whoops - mix fix  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Corrects cuts with filters  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_transition.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Finalisation of first phase of cut
+  handling (unmanaged)  
+
+  * src/framework/mlt_transition.c: Transitions ignore test frames  
+
+2004-09-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/modules/inigo/producer_inigo.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Cut management part 2 - corrects
+  playlist split/join and a little bit of mix  
+
+  * src/framework/mlt_properties.c: ...gah...  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+  src/framework/mlt_service.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Cut management part 1  
+
+  * src/modules/westley/consumer_westley.c: fix for in/out during serialisation
+   
+
+2004-09-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_tractor.c: Alpha from the tractor fix  
+
+2004-09-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c: Whoops  
+
+  * src/framework/mlt_factory.c, src/framework/mlt_properties.c,
+  src/miracle/miracle.c, src/miracle/miracle_local.c,
+  src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c:
+  Fix to compositing/watermark; miracle/mlt shutdown cleanup  
+
+  * src/framework/mlt_service.c, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c: In/out point handling on attached
+  filters revisted  
+
+2004-09-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/consumers.ini, src/modules/avformat/producer_avformat.c,
+  src/modules/gtk2/producer_pixbuf.c: Minor fixes  
+
+2004-09-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/valerie/valerie_response.h: Obtain stdio definitions  
+
+  * src/miracle/miracle.c, src/miracle/miracle_server.c,
+  src/miracle/miracle_server.h: Extending miracles functionality  
+
+2004-09-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, src/humperdink/Makefile, src/modules/dv/producer_libdv.c: Build
+  fix and temporary libdv compatability  
+
+  * src/framework/mlt_frame.c: aspect ratio fix for test card  
+
+  * src/framework/mlt_tractor.c: Aspect ratio fix  
+
+  * src/modules/sdl/consumer_sdl.c: Aspect ratio modifications  
+
+  * src/miracle/Makefile: Customising the miracle server part 1  
+
+2004-09-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/miracle/miracle_connection.c, src/miracle/miracle_local.c,
+  src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+  src/miracle/miracle_unit_commands.c, src/miracle/miracle_unit_commands.h,
+  src/modules/avformat/Makefile, src/modules/core/filter_watermark.c,
+  src/modules/core/producer_colour.c, src/modules/core/transition_region.c,
+  src/modules/gtk2/factory.c, src/modules/inigo/producer_inigo.c,
+  src/modules/plus/transition_affine.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sox/Makefile, src/modules/valerie/Makefile,
+  src/modules/valerie/configure, src/modules/valerie/consumer_valerie.c,
+  src/modules/valerie/consumer_valerie.h, src/modules/valerie/factory.c,
+  src/modules/westley/configure, src/modules/westley/consumer_westley.c,
+  src/modules/westley/factory.c, src/modules/westley/producer_westley.c,
+  src/modules/westley/producer_westley.h, src/valerie/Makefile,
+  src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_parser.c,
+  src/valerie/valerie_parser.h, src/valerie/valerie_remote.c: Consumer valerie,
+  pushes, and assorted modifications  
+
+2004-09-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/transition_luma.c: Work arounds
+  for scaling related issues  
+
+2004-09-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: position fixing  
+
+2004-09-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: Ugly temporary hack for aspect ratio  
+
+  * src/framework/mlt_playlist.c, src/inigo/inigo.c,
+  src/modules/inigo/producer_inigo.c: Fixes for removed tracks before/after mix
+   
+
+  * src/framework/mlt_field.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/modules/inigo/producer_inigo.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Adding the mix part 1  
+
+2004-09-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_producer.c,
+  src/framework/mlt_service.c, src/modules/avformat/consumer_avformat.c,
+  src/modules/core/consumer_null.c, src/modules/dv/consumer_libdv.c,
+  src/modules/sdl/consumer_sdl.c: More work with events  
+
+2004-09-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, docs/westley.txt,
+  src/modules/westley/producer_westley.c: Major westley rewrite - allows
+  attachable filters  
+
+2004-09-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/inigo/inigo.c,
+  src/modules/core/filter_region.c, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_region.c, src/modules/dv/producer_libdv.c,
+  src/modules/inigo/producer_inigo.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/westley/consumer_westley.c: Filter attachments to services  
+
+2004-09-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.c, src/framework/mlt_tractor.c: Multitrack and
+  tractor producer-changed event  
+
+2004-09-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: Fix occassional sdl core dumps  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_events.c, src/framework/mlt_events.h,
+  src/framework/mlt_playlist.c, src/modules/avformat/consumer_avformat.c,
+  src/modules/core/consumer_null.c, src/modules/dv/consumer_libdv.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c: event
+  fix for playlist and consumer-stopped event  
+
+  * src/framework/Makefile, src/framework/mlt_events.c,
+  src/framework/mlt_events.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.c, src/framework/mlt_service.c,
+  src/framework/mlt_types.h, src/modules/plus/transition_affine.c: First draft
+  of event handling  
+
+2004-08-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/framework/Makefile, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/modules/westley/consumer_westley.c,
+  src/valerie/Makefile: Minor make/configure mods and mlt_frame_waveform mod  
+
+2004-08-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: properly deal with evaluation of magnitude of 2s
+  complement for waveform generation  
+
+  * src/framework/mlt_frame.c: new, faster waveform generator that emphasizes
+  gain as opposed to shape  
+
+2004-08-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: bugfix in waveform method  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h: add waveform method
+  to frame  
+
+2004-08-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_tractor.h: Tractor
+  enhancements  
+
+  * docs/framework.txt, src/framework/mlt_field.c, src/framework/mlt_field.h,
+  src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+  src/modules/inigo/producer_inigo.c, src/modules/westley/producer_westley.c:
+  New tractor constructor  
+
+2004-08-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/modules/fezzik/producer_fezzik.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: Producer filter attach/detach
+  methods; major rework on westley consumer, minor on producer  
+
+2004-08-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/framework.txt, setenv_mc, src/framework/mlt_consumer.c,
+  src/framework/mlt_field.c, src/framework/mlt_filter.c,
+  src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c,
+  src/modules/core/producer_colour.c, src/modules/core/producer_noise.c,
+  src/modules/core/producer_ppm.c, src/modules/dv/producer_libdv.c,
+  src/modules/fezzik/producer_hold.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/westley/consumer_westley.c:
+  Mlt Ref Counts and Playlist split/join  
+
+2004-08-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_local.c, src/miracle/miracle_unit.c: Unit purge  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: consumer purge 
+
+2004-08-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_rescale.c, src/modules/gtk2/factory.c: Colour space
+  conversion with gdkpixbuf scaling  
+
+  * src/modules/avformat/producer_avformat.c: Another attempted mjpeg work
+  around  
+
+  * src/framework/mlt_consumer.c: Prefil consumer property  
+
+2004-08-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_service.c: NULL accpectance for connect/disconnect  
+
+2004-08-16  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_field.c,
+  src/framework/mlt_filter.c, src/framework/mlt_frame.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_service.c,
+  src/framework/mlt_tractor.c, src/framework/mlt_transition.c: NULL safety
+  checks  
+
+2004-08-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: gop/b frame fix, http/pipe
+  handling and logging off  
+
+2004-08-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c,
+  src/modules/avformat/filter_avcolour_space.h: Colour space filter  
+
+2004-08-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: Rudimentary rgb24 support  
+
+  * src/modules/avformat/producer_avformat.c: optimisations  
+
+2004-08-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_region.c: Flexible and animated shapes  
+
+2004-08-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/configure, src/modules/avformat/producer_avformat.c:
+  gop size == 0 fix and update to current ffmpeg for cvs co  
+
+  * src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+  src/modules/dv/producer_libdv.h: Fix for current libdv  
+
+  * src/modules/avformat/producer_avformat.c: Pipe workaround  
+
+2004-08-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_watermark.c, src/modules/core/transition_region.c:
+  Mutable shapes on regions  
+
+2004-08-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_affine.c: Small modifications to allow seeking  
+
+  * src/modules/sdl/consumer_sdl.c: Rectangle added to properties  
+
+2004-07-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_invert.c: Minor fix to invert  
+
+  * src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c: Mutable watermark producer and small
+  optimisation  
+
+2004-07-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c:
+  Minor affine modifications  
+
+  * src/modules/plus/Makefile, src/modules/plus/configure,
+  src/modules/plus/factory.c, src/modules/plus/filter_affine.c,
+  src/modules/plus/filter_affine.h: Affine filter  
+
+2004-07-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c: More mutable properties  
+
+2004-07-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/Makefile: link to mad  
+
+  * src/modules/core/filter_luma.c, src/modules/core/filter_mirror.c,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c: Mutable properties  
+
+  * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: Allow attached
+  filters when used in playlists  
+
+2004-07-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: Allows runtime modifications to
+  region fx  
+
+  * src/modules/core/filter_region.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_region.c: Allows runtime modifications to region
+  fx  
+
+2004-07-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: Pipe support for audio or video
+  only  
+
+2004-07-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/modules/westley/consumer_westley.c: Filter cleanup and fixes  
+
+2004-07-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/modules/xine/Makefile: Swig mods  
+
+  * src/modules/avformat/Makefile, src/modules/core/Makefile,
+  src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+  src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+  src/modules/normalize/Makefile, src/modules/plus/Makefile,
+  src/modules/plus/transition_affine.c, src/modules/resample/Makefile,
+  src/modules/sdl/Makefile, src/modules/sox/Makefile,
+  src/modules/vorbis/Makefile, src/modules/westley/Makefile: Fixes for swig  
+
+2004-06-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c, src/modules/core/filter_luma.c,
+  src/modules/core/transition_luma.c: consumer avformat fix and silly stuff in
+  lumas  
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/inigo/producer_inigo.c: stdout fix for avformat consumer and
+  change of defaults for inigo transition tracks  
+
+2004-06-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/filter_sepia.c, src/modules/plus/transition_affine.c:
+  Sepia fix and affine/alpha clean up  
+
+  * src/modules/plus/Makefile, src/modules/plus/configure,
+  src/modules/plus/factory.c, src/modules/plus/filter_sepia.c,
+  src/modules/plus/filter_sepia.h, src/modules/plus/transition_affine.c: affine
+  with alpha and a broken sepia  
+
+2004-06-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/transition_affine.c: Affine silliness  
+
+2004-06-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/modules/configure, src/modules/core/configure,
+  src/modules/core/transition_composite.c, src/modules/dv/configure,
+  src/modules/fezzik/configure, src/modules/gtk2/configure,
+  src/modules/inigo/configure, src/modules/normalize/configure,
+  src/modules/resample/configure, src/modules/sdl/configure,
+  src/modules/sdl/consumer_sdl.c, src/modules/sox/configure,
+  src/modules/vorbis/configure, src/modules/westley/configure,
+  src/modules/xine/configure: Portability modifications to scripts  
+
+  * src/modules/plus/Makefile, src/modules/plus/configure,
+  src/modules/plus/factory.c, src/modules/plus/transition_affine.c,
+  src/modules/plus/transition_affine.h: Experimental affine transformation  
+
+2004-06-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/plus/Makefile, src/modules/plus/configure,
+  src/modules/plus/factory.c, src/modules/plus/filter_charcoal.c,
+  src/modules/plus/filter_charcoal.h, src/modules/plus/filter_invert.c,
+  src/modules/plus/filter_invert.h: More silliness :-)  
+
+2004-06-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure: version bump  
+
+  * src/modules/avformat/configure: ffmpeg fixed date for cvs checkout  
+
+  * src/modules/avformat/ffmpeg.patch: ffmpeg patch for mandrake build  
+
+  * src/modules/avformat/producer_avformat.c: Temporary work around for missing
+  aspect ratio  
+
+  * src/framework/mlt_properties.c: Rudimentary arithmetic property assignment 
+
+2004-06-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/modules/core/producer_colour.c,
+  src/modules/core/producer_noise.c, src/modules/fezzik.ini,
+  src/modules/gtk2/producer_pixbuf.c, src/tests/charlie.c: Minor tweaks  
+
+2004-05-30  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: slightly better seeking in drop
+  frame cases  
+
+  * src/modules/sdl/consumer_sdl.c: real_time=0 fix  
+
+  * src/modules/avformat/consumer_avformat.c: Update to latest ffmpeg cvs  
+
+2004-05-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure: Yet another
+  way to configure ffmpeg  
+
+  * src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: Sync with current ffmpeg CVS and
+  minor clean up  
+
+2004-05-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/configure, src/framework/mlt_consumer.c,
+  src/framework/mlt_factory.c, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_repository.c: slight mods to
+  factory (for future module reporting); pool purge function; consumer drop
+  frame rework  
+
+  * src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c: fix for avformat seek < gop; fix
+  for avformat consumer qscale; additional avformat consumer properties  
+
+2004-05-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c: Removed unecessary locks in
+  avformat  
+
+2004-05-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: audio off  
+
+2004-05-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: aspect ratio and locking  
+
+2004-05-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt: a clarification  
+
+  * src/modules/core/filter_resize.c: set output frame aspect to consumer
+  sample aspect, not display aspect.  
+
+  * src/modules/sdl/consumer_sdl.c: fix aspect handling when rescale != none  
+
+2004-05-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: last sdl fix for now (sigh)  
+
+  * src/modules/sdl/consumer_sdl.c: yet another sdl tweak (sigh)  
+
+2004-05-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: last sdl fix for now (sigh)  
+
+2004-05-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/consumer_avformat.c, src/modules/fezzik.ini,
+  src/modules/sox/Makefile: sox fix; remove consumer avformat diagnostic  
+
+  * src/framework/Makefile, src/framework/mlt_consumer.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/consumer_null.c,
+  src/modules/core/consumer_null.h, src/modules/core/factory.c,
+  src/modules/core/producer_noise.c, src/modules/fezzik/producer_hold.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/vorbis/producer_vorbis.c: minor
+  clean ups; added a null consumer for easier valgrind testing  
+
+2004-05-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c: audio/video processing swap  
+
+2004-05-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/filter_sox.c: fix st.h include  
+
+2004-05-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: test card handling  
+
+2004-05-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/modules/sdl/consumer_sdl.c: Audio read ahead and fine tuning  
+
+  * src/framework/mlt_consumer.c, src/modules/avformat/producer_avformat.c,
+  src/modules/sdl/consumer_sdl.c: Clean up and border preservation  
+
+2004-04-30  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/Makefile, src/framework/mlt_consumer.c,
+  src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/filter_mirror.c,
+  src/modules/fezzik.ini, src/modules/sdl/consumer_sdl.c: Sundry consumer
+  modifications; albino compile fix; minor mods to avformat producer  
+
+2004-04-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_deque.h, src/framework/mlt_field.h,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.h,
+  src/framework/mlt_manager.h, src/framework/mlt_multitrack.h,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.h,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.h, src/framework/mlt_repository.h,
+  src/framework/mlt_service.c, src/framework/mlt_service.h,
+  src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.h,
+  src/framework/mlt_transition.h: C++ compatability  
+
+2004-04-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure: version shunt  
+
+  * README, configure, docs/install.txt, docs/services.txt,
+  src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c,
+  src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/producer_avformat.c, src/modules/configure,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c: config
+  mods; avformat static or shared build; corrections to sdl  
+
+2004-04-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, docs/services.txt, setenv: GPL checking (provisional
+  implementation), mc scaling docs  
+
+  * src/framework/mlt.h: added tokeniser to mlt header  
+
+  * src/modules/configure, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_rescale.c, src/modules/core/filter_rescale.h,
+  src/modules/fezzik.ini, src/modules/fezzik/Makefile,
+  src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/configure,
+  src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c: Rescaler and
+  fezzik rework (to allow inclusion of mc scaler)  
+
+2004-04-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/Makefile: whoops - missed some libs  
+
+  * src/albino/Makefile: albino Makefile cleanup  
+
+  * src/modules/dv/Makefile, src/modules/normalize/Makefile,
+  src/modules/sox/Makefile: Makefile cleanup in modules  
+
+  * src/modules/sox/Makefile, src/modules/sox/filter_sox.c: switched to
+  mlt_tokeniser and removed libst-config from Makefile  
+
+  * src/framework/Makefile, src/framework/mlt_tokeniser.c,
+  src/framework/mlt_tokeniser.h: added mlt_tokeniser  
+
+2004-04-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sox/filter_sox.c: add more comments  
+
+  * src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_volume.c,
+  src/modules/core/filter_volume.h, src/modules/normalize/Makefile,
+  src/modules/normalize/configure, src/modules/normalize/factory.c,
+  src/modules/normalize/filter_volume.c, src/modules/normalize/filter_volume.h,
+  src/modules/sox/Makefile, src/modules/sox/configure,
+  src/modules/sox/factory.c, src/modules/sox/filter_sox.c,
+  src/modules/sox/filter_sox.h: moved filter_volume into a normalize module,
+  added new sox module with filter_sox  
+
+2004-04-16  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/ffmpeg/Makefile, src/modules/ffmpeg/audio.sh,
+  src/modules/ffmpeg/configure, src/modules/ffmpeg/factory.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/ffmpeg/producer_ffmpeg.h,
+  src/modules/ffmpeg/video.sh: removed all ffmpeg files  
+
+  * src/modules/ffmpeg/Makefile, src/modules/ffmpeg/configure,
+  src/modules/ffmpeg/consumer_ffmpeg.c, src/modules/ffmpeg/consumer_ffmpeg.h,
+  src/modules/ffmpeg/factory.c, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  src/modules/ffmpeg/filter_ffmpeg_dub.h: ffmpeg cleanup  
+
+2004-04-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/fezzik/producer_fezzik.c: Change defaults to LGPL deinterlace
+  and resample  
+
+  * src/modules/avformat/Makefile, src/modules/avformat/configure,
+  src/modules/avformat/factory.c, src/modules/avformat/filter_avdeinterlace.c,
+  src/modules/avformat/filter_avdeinterlace.h,
+  src/modules/avformat/filter_avresample.c,
+  src/modules/avformat/filter_avresample.h, src/modules/avformat/mmx.h: LGPL
+  deinterlace and resampler  
+
+2004-04-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * configure, src/albino/Makefile, src/framework/Makefile,
+  src/framework/mlt_pool.c, src/humperdink/Makefile, src/inigo/Makefile,
+  src/miracle/Makefile, src/miracle/miracle_local.c, src/modules/Makefile,
+  src/modules/avformat/Makefile, src/modules/dv/Makefile,
+  src/modules/gtk2/Makefile, src/modules/resample/Makefile,
+  src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/tests/Makefile, src/valerie/Makefile,
+  src/valerie/valerie_socket.c: More configure and build tuning  
+
+  * configure, src/modules/configure: Configure and build tuning  
+
+  * configure, docs/install.txt, src/albino/Makefile, src/framework/Makefile,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/modules/avformat/Makefile, src/modules/core/Makefile,
+  src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+  src/modules/ffmpeg/Makefile, src/modules/gtk2/Makefile,
+  src/modules/inigo/Makefile, src/modules/resample/Makefile,
+  src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/modules/xine/Makefile, src/tests/Makefile,
+  src/valerie/Makefile: Configure and build tuning  
+
+2004-04-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, src/framework/mlt_frame.c, src/modules/Makefile,
+  src/modules/avformat/consumer_avformat.c: Makefile error handling and
+  consumer avformat cleanup  
+
+  * docs/install.txt: Installation docs update  
+
+2004-04-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+  src/modules/fezzik.dict, src/modules/westley/producer_westley.c: field order
+  normalisation fix, add .vob to fezzik, field order detection for avformat  
+
+2004-04-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_filter.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_properties.c,
+  src/framework/mlt_repository.c, src/inigo/inigo.c,
+  src/modules/dv/consumer_libdv.c, src/modules/resample/filter_resample.c,
+  src/modules/sdl/consumer_sdl.c: Memory leaks and resample rework  
+
+2004-04-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: test card and aspect ratio woes continued  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_properties.c: aspect ratio and test card woes  
+
+2004-04-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_news, docs/framework.txt, src/framework/mlt_consumer.c,
+  src/framework/mlt_factory.c, src/framework/mlt_frame.c,
+  src/framework/mlt_properties.c, src/modules/fezzik/producer_hold.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/sdl/consumer_sdl.c: hold
+  modifications and test card env var  
+
+2004-04-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/demo: remove setenv call  
+
+2004-04-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: added setenv_mc  
+
+  * setenv_mc, src/modules/sdl/consumer_sdl.c: added setenv_mc  
+
+  * demo/demo.ini, demo/mlt_squeeze, demo/mlt_squeeze_box, docs/framework.txt,
+  docs/services.txt, src/modules/core/transition_composite.c: minor mods  
+
+2004-03-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt: fix something that got disordered  
+
+  * src/modules/westley/producer_westley.c: qualitfy paths of known properties
+  that take a filename with server virtual root  
+
+2004-03-30  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/albino/Makefile, src/framework/Makefile,
+  src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_frame.c, src/humperdink/Makefile, src/inigo/Makefile,
+  src/miracle/Makefile, src/miracle/miracle_unit.c,
+  src/modules/avformat/Makefile, src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+  src/modules/dv/Makefile, src/modules/fezzik/Makefile,
+  src/modules/ffmpeg/Makefile, src/modules/gtk2/Makefile,
+  src/modules/inigo/Makefile, src/modules/resample/Makefile,
+  src/modules/sdl/Makefile, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/modules/xine/Makefile, src/tests/Makefile,
+  src/valerie/Makefile: Minor optimisations, consumer avformat experimentation 
+
+2004-03-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/modules/avformat/consumer_avformat.c,
+  src/modules/dv/consumer_libdv.c, src/modules/sdl/consumer_sdl.c: inherit
+  scheduling priority on any created thread  
+
+2004-03-29  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_luma.c, src/modules/gtk2/filter_rescale.c:
+  bugfix limits in transition luma  
+
+  * demo/consumers.ini, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/sdl/consumer_sdl.c: aspect
+  fixes for rescale=none  
+
+2004-03-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/valerie/valerie.c: insert fix  
+
+  * README, src/framework/configure, src/framework/mlt.h,
+  src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+  src/framework/mlt_pool.c, src/modules/avformat/Makefile,
+  src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c,
+  src/modules/avformat/consumer_avformat.h, src/modules/avformat/factory.c,
+  src/modules/avformat/producer_avformat.c, src/modules/dv/consumer_libdv.c,
+  src/modules/dv/producer_libdv.c, src/modules/sdl/consumer_sdl.c: consumer
+  avformat added, various cleanups and consumer realtime switching  
+
+2004-03-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, README, configure, mlt-framework.pc.in, mlt-miracle.pc.in,
+  mlt-valerie.pc.in: added pkgconfig files. fixed broken dist-clean make
+  target.  
+
+2004-03-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_fade_black, demo/mlt_push, demo/mlt_squeeze, docs/TODO,
+  docs/dvcp.txt, docs/framework.txt, docs/inigo.txt, docs/install.txt,
+  docs/services.txt, docs/testing.txt, docs/valerie.txt, docs/westley.txt: Doc
+  formating  
+
+2004-03-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/configure: make install part 2 - building configs  
+
+2004-03-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/entity.westley, demo/new.westley, docs/westley.txt,
+  src/modules/westley/Makefile, src/modules/westley/producer_westley.c,
+  src/modules/westley/westley.dtd: added westley.dtd  
+
+2004-03-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, configure, mlt-config-template, src/framework/configure,
+  src/miracle/configure, src/valerie/configure: make install part 2 - building
+  configs  
+
+  * src/modules/fezzik/Makefile: make install fix  
+
+2004-03-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/entity.westley, docs/westley.txt,
+  src/modules/westley/producer_westley.c: fix westley for mixed element text
+  and entity references  
+
+2004-03-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, src/modules/Makefile: make install part 1  
+
+  * Makefile, README, configure, src/albino/Makefile, src/framework/Makefile,
+  src/framework/config.h, src/humperdink/Makefile, src/inigo/Makefile,
+  src/miracle/Makefile, src/modules/Makefile, src/modules/avformat/Makefile,
+  src/modules/core/Makefile, src/modules/dv/Makefile,
+  src/modules/fezzik/Makefile, src/modules/ffmpeg/Makefile,
+  src/modules/gtk2/Makefile, src/modules/inigo/Makefile,
+  src/modules/resample/Makefile, src/modules/sdl/Makefile,
+  src/modules/vorbis/Makefile, src/modules/westley/Makefile,
+  src/modules/xine/Makefile, src/tests/Makefile, src/valerie/Makefile: make
+  install part 1  
+
+  * src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_pool.c, src/framework/mlt_properties.c,
+  src/modules/dv/producer_libdv.c, src/modules/fezzik.dict,
+  src/modules/fezzik/producer_fezzik.c, src/modules/sdl/consumer_sdl.c: pooling
+  and properties checks; dv decoder stack; factory cleanup registering  
+
+2004-03-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/README, demo/entity.westley, docs/services.txt, docs/westley.txt,
+  src/miracle/miracle_unit_commands.c, src/modules/westley/producer_westley.c:
+  enhance miracle LOAD command to accept a service: prefix. enhance
+  producer_westley to apply parameters on url as entities. bugfix
+  producer_westley memory leak.  
+
+  * demo/README, demo/pango.westley, src/modules/fezzik/producer_hold.c,
+  src/modules/westley/producer_westley.c: fixed westley/fezzik integration when
+  both service and resource supplied.  
+
+2004-03-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_push, demo/new.westley, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: reorganized consumer_westley. added
+  branch tracking and other bugfixes to producer_westley.  
+
+2004-03-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_tractor.c: tractor fix  
+
+2004-03-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_fade_black, demo/mlt_push, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: added track hiding to westley  
+
+2004-03-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_fade_black, demo/mlt_title_over_gfx,
+  demo/mlt_titleshadow_watermark: couple of fixes to hidden tracks  
+
+  * src/framework/mlt_multitrack.c: ignore length of hidden tracks  
+
+  * demo/consumers.ini, demo/luma1.pgm, demo/mlt_clock_in_and_out,
+  demo/mlt_fade_black, demo/mlt_my_name_is, demo/mlt_news, demo/mlt_squeeze,
+  demo/mlt_title_over_gfx, demo/mlt_voiceover: demo mods for reversed tracks  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_producer.c, src/framework/mlt_tractor.c,
+  src/framework/mlt_transition.c, src/modules/inigo/producer_inigo.c: track
+  reversal and hidden tracks  
+
+  * demo/demo, demo/demo.ini, demo/mlt_news, demo/mlt_squeeze: news and squeeze
+  added  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_tractor.c, src/modules/core/transition_composite.c,
+  src/modules/resample/filter_resample.c: Tractor frame handling reworked; fix
+  to composite for key diffs of 1; added mlt_consumer_new for consistency  
+
+2004-03-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/README, demo/consumers.ini, demo/demo.ini, demo/mlt_fade_black,
+  demo/mlt_jcut, demo/mlt_jcut2, demo/mlt_lcut, demo/mlt_push, demo/mlt_ticker,
+  docs/services.txt, src/modules/core/producer_colour.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c:
+  remove some progressive flag handling in field renderers bugfix compositing
+  images wider than the frame added more demos  
+
+2004-03-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/demo.ini, demo/mlt_jcut, demo/mlt_jcut2: added J Cut demos  
+
+2004-03-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+  src/miracle/miracle_unit_commands.h, src/valerie/valerie.c,
+  src/valerie/valerie.h: added clear to the miracle command set and valerie api
+   
+
+2004-03-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/install.txt: minor typos  
+
+  * README, demo/consumers.ini, docs/framework.txt, docs/install.txt,
+  docs/services.txt, docs/westley.txt, src/albino/albino.c,
+  src/humperdink/client.c, src/modules/gtk2/producer_pango.c,
+  src/modules/westley/producer_westley.c: documentation updates change some
+  references to dv1394d in the example clients to Miracle. more bugfixes for
+  producer_westley iconv for pango  
+
+2004-03-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/miracle/miracle_commands.c,
+  src/miracle/miracle_unit_commands.c: root corrections to miracle  
+
+  * src/valerie/valerie.c: quick valerie fix  
+
+  * docs/install.txt: Added install.txt  
+
+2004-03-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: null pointer check in end_playlist 
+
+2004-03-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c,
+  src/modules/core/producer_colour.c, src/modules/dv/consumer_libdv.c,
+  src/modules/fezzik/Makefile, src/modules/fezzik/configure,
+  src/modules/fezzik/factory.c, src/modules/fezzik/producer_hold.c,
+  src/modules/fezzik/producer_hold.h, src/modules/resample/filter_resample.c,
+  src/tests/dan.c, src/tests/pango.c, src/tests/pixbuf.c: producer hold,
+  experimental ac3 audio support  
+
+2004-03-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: touchup on the producer in/out
+  applied to parent entry  
+
+  * demo/circle.svg, demo/demo.kino, demo/new.westley, demo/svg.westley,
+  src/framework/mlt_filter.c, src/framework/mlt_playlist.c,
+  src/modules/fezzik.dict, src/modules/fezzik/producer_fezzik.c,
+  src/modules/westley/producer_westley.c: smarter and harder producer_westley  
+
+2004-03-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/modules/fezzik.dict, src/modules/fezzik/producer_fezzik.c,
+  src/modules/sdl/consumer_sdl.c, src/tests/hello.c: in point fix, low latency
+  sdl, minor fixes  
+
+2004-03-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/framework.txt, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/modules/Makefile, src/modules/configure,
+  src/modules/core/producer_noise.c, src/modules/fezzik.dict,
+  src/modules/fezzik/producer_fezzik.c: fezzik gets a rhyming dictionary  
+
+  * docs/framework.txt, docs/services.txt, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_mirror.c, src/modules/core/filter_mirror.h,
+  src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c,
+  src/modules/core/producer_noise.c, src/modules/core/producer_noise.h,
+  src/modules/fezzik/producer_fezzik.c, src/tests/hello.c: Noise and mirrors  
+
+2004-03-18  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/modules/avformat/producer_avformat.c: revert
+  avformat pts offset change and note bug in docs  
+
+  * src/modules/inigo/producer_inigo.c: fix brokenness  
+
+2004-03-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/framework.txt, docs/westley.txt, src/framework/config.h,
+  src/framework/mlt_factory.c, src/framework/mlt_frame.h,
+  src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_types.h,
+  src/modules/core/transition_composite.c, src/modules/sdl/consumer_sdl.c,
+  src/tests/Makefile, src/tests/hello.c: provisional framework docs and
+  corrections  
+
+2004-03-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/modules/avformat/producer_avformat.c,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_channelcopy.c,
+  src/modules/core/filter_channelcopy.h,
+  src/modules/resample/filter_resample.c: added filter_channelcopy. enhance
+  filter_resample to reproduce channels when producer does not create as many
+  as consumer requested.  
+
+  * src/modules/core/filter_volume.c: bugfix segfault in audio normaliser as
+  well as logical bug in smoothing.  
+
+  * docs/services.txt, src/modules/avformat/producer_avformat.c,
+  src/modules/fezzik/producer_fezzik.c, src/modules/inigo/producer_inigo.c:
+  fezzik now accepts service:resource and strips \'avformat:\' before fallback
+  avformat construction. avformat now accepts urls with a format and format
+  parameters designation. updated services.txt for above changes. added a video
+  pts offset to avformat.  
+
+2004-03-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/luma1.pgm, demo/mlt_obscure, docs/services.txt,
+  src/modules/core/composite_line_yuv_mmx.S, src/modules/core/filter_luma.c,
+  src/modules/core/transition_luma.c, src/modules/fezzik/producer_fezzik.c:
+  updated services docs plus minor fixes discovered during  
+
+2004-03-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/README, demo/consumers.ini, demo/demo, demo/mlt_clock_in_and_out,
+  demo/mlt_voiceover: notes for the demo  
+
+  * demo/demo.ini: bring into sync with changes  
+
+  * src/modules/sdl/consumer_sdl.c: default progressive off  
+
+  * demo/circle.png, demo/circle.svg, demo/consumers.ini, demo/luma1.pgm,
+  demo/mlt_bouncy_ball, demo/mlt_composite_transition,
+  demo/mlt_fade_in_and_out, demo/mlt_obscure, demo/mlt_title_over_gfx,
+  demo/mlt_titleshadow_watermark, demo/mlt_voiceover: some demo updates  
+
+  * src/modules/core/transition_luma.c: fix distortion in smoothness  
+
+  * src/modules/core/filter_gamma.c: fix broken gamma  
+
+  * src/modules/core/transition_luma.c: fix field rendering  
+
+  * src/modules/core/transition_composite.c: bugfixes with field rendering  
+
+  * src/modules/dv/producer_libdv.c: fix aspect  
+
+2004-03-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/consumers.ini, demo/demo, src/framework/mlt_consumer.c,
+  src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: more
+  sdl/consumer tuning and demo updates  
+
+2004-03-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * demo/mlt_voiceover, src/framework/mlt_deque.c,
+  src/framework/mlt_properties.c, src/framework/mlt_property.c,
+  src/framework/mlt_property.h, src/modules/sdl/consumer_sdl.c: more small
+  optimisations  
+
+  * demo/demo: demo framework added  
+
+  * demo/demo, demo/demo.ini, demo/luma1.pgm, demo/mlt_all,
+  demo/mlt_audio_stuff, demo/mlt_avantika_title, demo/mlt_bouncy,
+  demo/mlt_bouncy_ball, demo/mlt_clock_in_and_out,
+  demo/mlt_composite_transition, demo/mlt_effect_in_middle,
+  demo/mlt_fade_in_and_out, demo/mlt_intro, demo/mlt_levels,
+  demo/mlt_my_name_is, demo/mlt_obscure, demo/mlt_slideshow,
+  demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark, demo/mlt_voiceover,
+  demo/mlt_watermark, demo/pango.westley, demo/watermark1.png,
+  docs/westley.txt, setenv, src/inigo/io.c, src/modules/dv/producer_libdv.c,
+  src/modules/sdl/consumer_sdl.c: demo framework added  
+
+2004-03-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/Makefile, src/modules/core/composite_line_yuv_mmx.S,
+  src/modules/core/filter_resize.c, src/modules/core/transition_composite.c,
+  src/modules/gtk2/filter_rescale.c: added very preliminary mmx for composite.
+  bugfixes to -x and too small rescaling.  
+
+2004-03-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/core/transition_mix.c, src/modules/core/transition_region.c: RPN
+  clean up for frames  
+
+  * docs/inigo.txt, docs/westley.txt, src/framework/mlt_consumer.c,
+  src/modules/westley/producer_westley.c: Minor fixes to westley and
+  mlt_consumer; first draft westley docs  
+
+2004-03-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c: pgm scaling in transition_composite.
+  optimisations for luma producer.  
+
+2004-03-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_resize.c, src/modules/core/producer_ppm.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/modules/dv/producer_libdv.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/producer_westley.c: add
+  luma to composite. rework aspect handling to use sample aspect. workaround
+  westley segfault when another instance of libxml2 is used. improved inline
+  xml handling in westley - pango and svg.  
+
+2004-03-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/modules/dv/consumer_libdv.c: experimental
+  tuning  
+
+2004-03-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/xine/attributes.h, src/modules/xine/xineutils.h: add missing
+  header  
+
+2004-03-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c,
+  src/modules/sdl/consumer_sdl.c: tunable read ahead buffer and fix for luma  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/humperdink/client.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+  src/modules/dv/consumer_libdv.c, src/modules/sdl/consumer_sdl.c,
+  src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_status.c,
+  src/valerie/valerie_status.h: consumer read ahead and int32_t migration  
+
+2004-03-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_luma.c: reorg transition_luma to support
+  producer  
+
+  * src/modules/Makefile, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_deinterlace.c, src/modules/core/filter_deinterlace.h,
+  src/modules/core/producer_colour.c, src/modules/xine/Makefile,
+  src/modules/xine/configure, src/modules/xine/cpu_accel.c,
+  src/modules/xine/deinterlace.c, src/modules/xine/deinterlace.h,
+  src/modules/xine/factory.c, src/modules/xine/filter_deinterlace.c,
+  src/modules/xine/filter_deinterlace.h, src/modules/xine/xineutils.h: added
+  xine-based accellerated deinterlace  
+
+2004-03-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_frame.h, src/framework/mlt_properties.c,
+  src/framework/mlt_service.h, src/framework/mlt_types.h,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_region.c,
+  src/modules/core/transition_region.c, src/modules/core/transition_region.h:
+  transition region  
+
+2004-03-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_deinterlace.c: optimise deinterlace path  
+
+  * src/modules/core/producer_colour.c: producer_colour  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/producer_colour.c,
+  src/modules/core/producer_colour.h: producer_colour  
+
+  * src/framework/mlt_frame.c: more accurate and scaled rgb to yuv conversion  
+
+2004-03-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_multitrack.c, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+  src/framework/mlt_property.c, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_types.h, src/inigo/inigo.c,
+  src/modules/avformat/producer_avformat.c, src/modules/core/filter_region.c,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c: Yet
+  more sdl hacking, region memory leak fix, mlt_position changed to int32_t,
+  experimental hash in properties  
+
+2004-03-03  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/filter_region.c,
+  src/modules/core/transition_composite.c,
+  src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/westley/producer_westley.c: some bugfixes, filter_shape producer,
+  pixbuf takes svg xml, fezzik can take a service name  
+
+2004-03-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: More SDL fixes  
+
+2004-03-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: yet more sdl hacks  
+
+2004-03-01  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c,
+  src/modules/gtk2/scale_line_22_yuv_mmx.S: much improved mmx yuv scaler added
+  producer_libdv quality property improve avformat aspect_ratio and frame_rate
+  reporting  
+
+2004-03-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/sdl/consumer_sdl.c: Minor sdl hack  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_frame.c,
+  src/framework/mlt_producer.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/sdl/consumer_sdl.c: sdl hacks  
+
+2004-02-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_region.c,
+  src/modules/core/filter_region.h, src/modules/core/filter_watermark.c,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h: regionalised fx part 1  
+
+  * src/framework/mlt_factory.c, src/modules/core/filter_watermark.c,
+  src/modules/dv/producer_libdv.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c: unique ids  
+
+2004-02-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/scale_line_22_yuv_mmx.S: bugfix mmx scaling with
+  performance loss :-(  
+
+2004-02-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/filter_resize.c,
+  src/modules/dv/consumer_libdv.c: Scaling experimentation  
+
+2004-02-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c,
+  src/modules/gtk2/scale_line_22_33_mmx.S,
+  src/modules/gtk2/scale_line_22_yuv_mmx.S: mmx version of non-nearest, 2x2
+  rescaling  
+
+2004-02-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: composite alignment fix  
+
+2004-02-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c,
+  src/modules/gtk2/scale_line_22_33_mmx.S: updated mmx yuv scaling  
+
+2004-02-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_luma.c, src/modules/core/filter_luma.h,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c: composite aspect ratio fix (again ;-)),
+  added fill compositing test case, filter luma, mlt_properties_pass and sundry
+  fixes  
+
+2004-02-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: composite key frames  
+
+  * docs/TODO, src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_deinterlace.c, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: service
+  stack, various fixes  
+
+2004-02-24  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c: field rendering fix and disable
+  scaling height when normalising pixel aspect when output pixel aspect < 1  
+
+2004-02-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_producer.h,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c,
+  src/modules/core/filter_watermark.h, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/resample/filter_resample.c:
+  watermark added, minor mods to mlt framework required  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_filter.c,
+  src/framework/mlt_filter.h, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/modules/core/filter_brightness.c,
+  src/modules/core/filter_deinterlace.c, src/modules/core/filter_gamma.c,
+  src/modules/core/filter_greyscale.c, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_resize.c, src/modules/core/transition_composite.c,
+  src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c: Filter optimisations and cleanup part 1  
+
+2004-02-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_producer.c,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/fezzik/producer_fezzik.c: Minor fixes  
+
+  * src/modules/core/transition_luma.c, src/modules/sdl/consumer_sdl.c: sdl
+  rework (prepatory read-ahead implementation) and luma work around  
+
+  * src/framework/mlt_pool.c, src/framework/mlt_pool.h,
+  src/modules/core/transition_luma.c: Big luma optimisations, minor pooling
+  optimisations  
+
+2004-02-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c:
+  composite alpha operations, make obscure alpha aware  
+
+2004-02-21  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c,
+  src/tests/Makefile, src/tests/dan.c: fix broken aspect handling again  
+
+2004-02-21  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_pool.c, src/modules/avformat/producer_avformat.c,
+  src/modules/dv/producer_libdv.c: avformat whoops, pooling claridication and
+  removal of dv leak  
+
+2004-02-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/Makefile, src/framework/Makefile, src/framework/mlt_frame.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/modules/avformat/producer_avformat.c, src/modules/core/producer_ppm.c,
+  src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/resample/filter_resample.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/vorbis/producer_vorbis.c, src/tests/Makefile,
+  src/valerie/Makefile: Memory pooling part 2 and other optimisations  
+
+2004-02-19  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_luma.c: more dissolve optimisation  
+
+  * src/modules/core/transition_luma.c: optimise dissolve case  
+
+2004-02-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/framework/Makefile, src/framework/mlt_factory.c,
+  src/framework/mlt_frame.c, src/framework/mlt_pool.c,
+  src/framework/mlt_pool.h, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_types.h,
+  src/modules/avformat/producer_avformat.c, src/modules/core/filter_resize.c,
+  src/modules/core/producer_ppm.c, src/modules/core/transition_luma.c,
+  src/modules/dv/producer_libdv.c, src/modules/ffmpeg/producer_ffmpeg.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+  src/modules/resample/filter_resample.c, src/modules/vorbis/producer_vorbis.c:
+  Memory pooling  
+
+2004-02-19  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c: field rendering and alignment for
+  composite, bugfixes for luma, pixbuf and pango  
+
+2004-02-18  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c: create consumer_progressive property on frame
+   
+
+  * src/modules/sdl/consumer_sdl.c: default progressive on  
+
+  * src/modules/westley/consumer_westley.c: consumer_westley now only puts
+  in/out as element attributes and not property elements  
+
+  * src/modules/core/filter_deinterlace.c,
+  src/modules/core/transition_composite.c, src/modules/core/transition_luma.c:
+  split getting of b_frame image and composite  
+
+2004-02-18  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/albino/Makefile, src/framework/Makefile, src/framework/mlt_consumer.c,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_frame.c, src/framework/mlt_properties.c,
+  src/humperdink/Makefile, src/inigo/Makefile, src/miracle/Makefile,
+  src/miracle/miracle_local.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit_commands.c, src/modules/avformat/Makefile,
+  src/modules/avformat/producer_avformat.c, src/modules/core/Makefile,
+  src/modules/core/filter_obscure.c, src/modules/core/filter_resize.c,
+  src/modules/core/transition_composite.c, src/modules/dv/Makefile,
+  src/modules/fezzik/Makefile, src/modules/ffmpeg/Makefile,
+  src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/Makefile,
+  src/modules/resample/Makefile, src/modules/sdl/Makefile,
+  src/modules/sdl/consumer_sdl.c, src/modules/vorbis/Makefile,
+  src/modules/westley/Makefile, src/modules/westley/producer_westley.c,
+  src/tests/Makefile, src/valerie/Makefile: Optimisations (part 0), pixel v
+  percentage, reworked aspect ratio calcs, ante/post properties for dv
+  consumers, avformat rework, westley root  
+
+2004-02-16  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/transition_composite.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/sdl/consumer_sdl.c: bug fixes  
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_frame.c,
+  src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+  src/modules/core/filter_resize.c, src/modules/core/producer_ppm.c,
+  src/modules/core/producer_ppm.h, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c:
+  westley serialises with entry in/out; full field, aspect, and colour space
+  normalisation; scaling overlays to consumer size; tagged frame mallocs with
+  //IRRIGATE ME  
+
+2004-02-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_consumer.c, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h: Properties rename and dump function  
+
+  * docs/testing-20040110.txt, src/framework/mlt_consumer.c,
+  src/framework/mlt_consumer.h, src/framework/mlt_frame.c,
+  src/framework/mlt_producer.c, src/modules/avformat/producer_avformat.c,
+  src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c:
+  Defaults for PAL/NTSC on producers and consumers  
+
+2004-02-13  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_brightness.c,
+  src/modules/core/filter_brightness.h, src/modules/core/filter_volume.c,
+  src/modules/core/transition_mix.c, src/modules/gtk2/filter_rescale.c: added
+  brightness filter, added smooth ramping to audio processing, added start/end
+  interpolation points to filter_mix and filter_volume  
+
+2004-02-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/Makefile, mlt/README, mlt/configure, mlt/docs/dvcp.txt,
+  mlt/docs/inigo.txt, mlt/docs/services.txt, mlt/docs/testing-20040110.txt,
+  mlt/docs/testing.txt, mlt/docs/valerie.txt, mlt/setenv,
+  mlt/src/albino/Makefile, mlt/src/albino/albino.c, mlt/src/framework/Makefile,
+  mlt/src/framework/config.h, mlt/src/framework/configure,
+  mlt/src/framework/mlt.h, mlt/src/framework/mlt_consumer.c,
+  mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_factory.c,
+  mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_field.c,
+  mlt/src/framework/mlt_field.h, mlt/src/framework/mlt_filter.c,
+  mlt/src/framework/mlt_filter.h, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_frame.h, mlt/src/framework/mlt_manager.h,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+  mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+  mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+  mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+  mlt/src/framework/mlt_service.c, mlt/src/framework/mlt_service.h,
+  mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+  mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+  mlt/src/framework/mlt_types.h, mlt/src/humperdink/Makefile,
+  mlt/src/humperdink/client.c, mlt/src/humperdink/client.h,
+  mlt/src/humperdink/io.c, mlt/src/humperdink/io.h,
+  mlt/src/humperdink/remote.c, mlt/src/inigo/Makefile, mlt/src/inigo/inigo.c,
+  mlt/src/inigo/io.c, mlt/src/inigo/io.h, mlt/src/miracle/Makefile,
+  mlt/src/miracle/configure, mlt/src/miracle/miracle.c,
+  mlt/src/miracle/miracle_commands.c, mlt/src/miracle/miracle_commands.h,
+  mlt/src/miracle/miracle_connection.c, mlt/src/miracle/miracle_connection.h,
+  mlt/src/miracle/miracle_local.c, mlt/src/miracle/miracle_local.h,
+  mlt/src/miracle/miracle_log.c, mlt/src/miracle/miracle_log.h,
+  mlt/src/miracle/miracle_server.c, mlt/src/miracle/miracle_server.h,
+  mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit.h,
+  mlt/src/miracle/miracle_unit_commands.c,
+  mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/Makefile,
+  mlt/src/modules/configure, mlt/src/modules/core/Makefile,
+  mlt/src/modules/core/configure, mlt/src/modules/core/factory.c,
+  mlt/src/modules/core/filter_deinterlace.c,
+  mlt/src/modules/core/filter_deinterlace.h,
+  mlt/src/modules/core/filter_gamma.c, mlt/src/modules/core/filter_gamma.h,
+  mlt/src/modules/core/filter_greyscale.c,
+  mlt/src/modules/core/filter_greyscale.h,
+  mlt/src/modules/core/filter_resize.c, mlt/src/modules/core/filter_resize.h,
+  mlt/src/modules/core/filter_volume.c, mlt/src/modules/core/filter_volume.h,
+  mlt/src/modules/core/producer_ppm.c, mlt/src/modules/core/producer_ppm.h,
+  mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/core/transition_composite.h,
+  mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/core/transition_luma.h,
+  mlt/src/modules/core/transition_mix.c, mlt/src/modules/core/transition_mix.h,
+  mlt/src/modules/dv/Makefile, mlt/src/modules/dv/configure,
+  mlt/src/modules/dv/consumer_libdv.c, mlt/src/modules/dv/consumer_libdv.h,
+  mlt/src/modules/dv/factory.c, mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/modules/dv/producer_libdv.h, mlt/src/modules/ffmpeg/Makefile,
+  mlt/src/modules/ffmpeg/audio.sh, mlt/src/modules/ffmpeg/configure,
+  mlt/src/modules/ffmpeg/consumer_ffmpeg.c,
+  mlt/src/modules/ffmpeg/consumer_ffmpeg.h, mlt/src/modules/ffmpeg/factory.c,
+  mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  mlt/src/modules/ffmpeg/filter_ffmpeg_dub.h,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/ffmpeg/video.sh,
+  mlt/src/modules/gtk2/Makefile, mlt/src/modules/gtk2/configure,
+  mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pango.h,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/inigo/Makefile,
+  mlt/src/modules/inigo/configure, mlt/src/modules/inigo/factory.c,
+  mlt/src/modules/inigo/producer_inigo.c,
+  mlt/src/modules/inigo/producer_inigo.h, mlt/src/modules/resample/Makefile,
+  mlt/src/modules/resample/configure, mlt/src/modules/resample/factory.c,
+  mlt/src/modules/resample/filter_resample.c,
+  mlt/src/modules/resample/filter_resample.h, mlt/src/modules/sdl/Makefile,
+  mlt/src/modules/sdl/configure, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/modules/sdl/consumer_sdl.h, mlt/src/modules/sdl/factory.c,
+  mlt/src/modules/westley/Makefile, mlt/src/modules/westley/configure,
+  mlt/src/modules/westley/consumer_westley.c,
+  mlt/src/modules/westley/consumer_westley.h,
+  mlt/src/modules/westley/factory.c,
+  mlt/src/modules/westley/producer_westley.c,
+  mlt/src/modules/westley/producer_westley.h, mlt/src/tests/Makefile,
+  mlt/src/tests/charlie.c, mlt/src/tests/clock16ntsc.pgm,
+  mlt/src/tests/clock16pal.pgm, mlt/src/tests/dan.c, mlt/src/tests/dissolve.c,
+  mlt/src/tests/io.c, mlt/src/tests/io.h, mlt/src/tests/luma.c,
+  mlt/src/tests/pango.c, mlt/src/tests/pixbuf.c, mlt/src/tests/setenv,
+  mlt/src/tests/test.png, mlt/src/valerie/Makefile, mlt/src/valerie/configure,
+  mlt/src/valerie/valerie.c, mlt/src/valerie/valerie.h,
+  mlt/src/valerie/valerie_notifier.c, mlt/src/valerie/valerie_notifier.h,
+  mlt/src/valerie/valerie_parser.c, mlt/src/valerie/valerie_parser.h,
+  mlt/src/valerie/valerie_remote.c, mlt/src/valerie/valerie_remote.h,
+  mlt/src/valerie/valerie_response.c, mlt/src/valerie/valerie_response.h,
+  mlt/src/valerie/valerie_socket.c, mlt/src/valerie/valerie_socket.h,
+  mlt/src/valerie/valerie_status.c, mlt/src/valerie/valerie_status.h,
+  mlt/src/valerie/valerie_tokeniser.c, mlt/src/valerie/valerie_tokeniser.h,
+  mlt/src/valerie/valerie_util.c, mlt/src/valerie/valerie_util.h: remove child
+  mlt dir  
+
+  * docs/TODO, src/miracle/miracle_local.c: add TODO  
+
+2004-02-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_producer.c: test card handling  
+
+  * src/miracle/miracle_local.c: optional segv handling  
+
+2004-02-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_local.c: change segv handler to use log facility  
+
+  * src/valerie/valerie_notifier.c: cleanup  
+
+  * src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/miracle/miracle_local.c, src/valerie/valerie_notifier.c: segv handler,
+  playlist_move bugfix, resize_yuv422 optimisation  
+
+2004-02-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_unit.c: first of a few local tests  
+
+  * src/miracle/miracle_commands.c: first of a few local tests  
+
+  * docs/testing-20040110.txt, src/framework/mlt_frame.c,
+  src/miracle/miracle_unit.c, src/miracle/miracle_unit_commands.c,
+  src/modules/dv/consumer_libdv.c, src/valerie/valerie_notifier.c,
+  src/valerie/valerie_notifier.h: Miracle mods - clean working, test card fix,
+  silence dv when not playing  
+
+2004-02-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/testing-20040110.txt, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/miracle/miracle_unit.c, src/valerie/valerie_notifier.c,
+  src/valerie/valerie_status.h: Miracle mods  
+
+2004-02-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * setenv, src/framework/mlt_producer.c, src/modules/fezzik/producer_fezzik.c,
+  src/modules/resample/filter_resample.c,
+  src/modules/westley/producer_westley.c: bugfixes  
+
+2004-02-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_filter.c, src/framework/mlt_frame.c: filter fixes  
+
+2004-02-09  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_volume.c: remove spurious return in get_audio  
+
+2004-02-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/miracle/miracle_unit.c, src/modules/dv/consumer_libdv.c: brought by a
+  resizable bunny  
+
+  * src/modules/dv/consumer_libdv.c: brought by a bunny  
+
+  * docs/services.txt, src/modules/gtk2/producer_pango.c: pango colour handling
+   
+
+2004-02-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/core/transition_luma.c: luma funkiness  
+
+  * src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/modules/core/transition_composite.c,
+  src/modules/fezzik/producer_fezzik.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c:
+  pixbuf, composite and fezzik mirrors  
+
+2004-02-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/producer_westley.c: support in/out on entry and track  
+
+2004-02-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/producer_pango.c: pango producer rework  
+
+  * src/modules/avformat/producer_avformat.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/westley/producer_westley.c: Minor corrections, rescale=nearest
+  for sdl  
+
+2004-02-07  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/filter_rescale.c: rescale aspect handling redux  
+
+  * src/modules/avformat/producer_avformat.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c: fixup and disable rescale changes  
+
+2004-02-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/core/filter_volume.c,
+  src/modules/dv/producer_libdv.c, src/modules/fezzik/producer_fezzik.c,
+  src/modules/gtk2/filter_rescale.c, src/modules/resample/filter_resample.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: some bugfixes, westley property
+  handling reorg, make rescale respect the aspect ratio, make resize update the
+  aspect ratio, add resize to fezzik  
+
+2004-02-06  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, src/modules/core/filter_obscure.c,
+  src/modules/core/transition_composite.c: composite  
+
+  * src/framework/mlt_factory.c, src/framework/mlt_tractor.c,
+  src/miracle/miracle_unit.c, src/modules/Makefile,
+  src/modules/fezzik/Makefile, src/modules/fezzik/configure,
+  src/modules/fezzik/factory.c, src/modules/fezzik/producer_fezzik.c,
+  src/modules/fezzik/producer_fezzik.h, src/modules/inigo/producer_inigo.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: adding the rock thrower...  
+
+2004-02-05  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/resample/filter_resample.c: resample fix  
+
+  * docs/services.txt, setenv, src/framework/mlt_frame.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_producer.c,
+  src/framework/mlt_transition.c, src/miracle/miracle_unit.c,
+  src/modules/dv/producer_libdv.c, src/modules/ffmpeg/consumer_ffmpeg.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/resample/filter_resample.c,
+  src/modules/sdl/consumer_sdl.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: westley/libxml2 mods, mcdv/mpeg
+  release integration  
+
+2004-02-05  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/inigo.txt, src/framework/mlt_frame.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: bugfixes to westley  
+
+2004-02-04  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/gtk2/pixops.c: final rescale improvement and some optimisation 
+
+  * src/modules/gtk2/pixops.c: near final rescale improvements?  
+
+  * src/modules/gtk2/pixops.c: interim rescale improvements  
+
+  * src/modules/gtk2/pixops.c: interim rescale improvements  
+
+  * src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h: interim rescale
+  improvements  
+
+2004-02-04  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_factory.c, src/framework/mlt_field.c,
+  src/framework/mlt_repository.c, src/framework/mlt_tractor.c,
+  src/inigo/inigo.c, src/miracle/miracle_unit.c, src/modules/Makefile,
+  src/modules/core/filter_obscure.c, src/modules/inigo/configure,
+  src/modules/inigo/factory.c, src/modules/inigo/producer_inigo.c,
+  src/modules/inigo/producer_inigo.h, src/modules/westley/producer_westley.c:
+  pre-beta cleanup part 1  
+
+2004-02-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/inigo/inigo.c, src/modules/avformat/producer_avformat.c,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_obscure.c,
+  src/modules/core/filter_obscure.h, src/modules/inigo/Makefile,
+  src/modules/inigo/configure, src/modules/inigo/producer_inigo.c,
+  src/modules/vorbis/Makefile: obscurer filter, consistency mods and bug fixes 
+
+  * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_deque.c,
+  src/framework/mlt_deque.h, src/framework/mlt_factory.c,
+  src/framework/mlt_field.c, src/framework/mlt_frame.c,
+  src/framework/mlt_manager.h, src/framework/mlt_repository.c,
+  src/framework/mlt_types.h: added deque, api design for manager, minor affine
+  tweaks, experimental destructor work  
+
+2004-01-31  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt: doc updates  
+
+  * src/modules/core/filter_volume.c: configurable window size on volume
+  normalisation, also set default of max_gain to 20dB  
+
+2004-01-30  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c: updated affine  
+
+2004-01-30  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_frame.c, src/modules/avformat/producer_avformat.c,
+  src/modules/gtk2/Makefile, src/modules/gtk2/configure,
+  src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c,
+  src/modules/gtk2/filter_rescale.h, src/modules/gtk2/have_mmx.S,
+  src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/scale_line_22_33_mmx.S,
+  src/modules/vorbis/Makefile: some bugfixes and rescale filter  
+
+2004-01-28  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_volume.c: comment some diagnostics  
+
+  * docs/services.txt, src/modules/core/filter_volume.c: doc updates; property
+  changes, and tweaks for volume filter normalisation  
+
+2004-01-27  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/core/filter_volume.c, src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: westley bugfixes and audio
+  normalisation  
+
+2004-01-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * README, docs/services.txt, src/framework/mlt_multitrack.c,
+  src/miracle/miracle_unit.c, src/modules/Makefile,
+  src/modules/avformat/producer_avformat.c, src/modules/inigo/producer_inigo.c,
+  src/modules/vorbis/Makefile, src/modules/vorbis/configure,
+  src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c,
+  src/modules/vorbis/producer_vorbis.h: vorbis producer added, clean up on clip
+  handling in multitrack  
+
+2004-01-26  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: westley updates for non-inline
+  serialisation and code cleanup  
+
+2004-01-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_properties.c, src/miracle/miracle_unit.c,
+  src/modules/avformat/producer_avformat.c: mutex protection of avformat,
+  miracle avformat usage, and destrector reversal  
+
+  * README, docs/services.txt, src/modules/avformat/producer_avformat.c: Added
+  avformat  
+
+  * README, docs/inigo.txt, src/framework/mlt_producer.c, src/inigo/inigo.c,
+  src/modules/Makefile, src/modules/avformat/Makefile,
+  src/modules/avformat/configure, src/modules/avformat/factory.c,
+  src/modules/avformat/producer_avformat.c,
+  src/modules/avformat/producer_avformat.h, src/modules/inigo/producer_inigo.c,
+  src/modules/sdl/consumer_sdl.c: Added avformat  
+
+2004-01-25  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * src/framework/mlt_filter.c, src/framework/mlt_transition.c,
+  src/modules/core/transition_luma.c, src/modules/inigo/producer_inigo.c,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/producer_westley.c: updated westley  
+
+  * src/tests/dan.c: test cvs  
+
+2004-01-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/westley/consumer_westley.c,
+  src/modules/westley/consumer_westley.c: xml based westley serialisation  
+
+  * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_tractor.c,
+  mlt/src/framework/mlt_types.h, mlt/src/modules/westley/consumer_westley.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_tractor.c,
+  src/framework/mlt_types.h, src/modules/westley/consumer_westley.c: xml based
+  westley serialisation  
+
+2004-01-21  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, mlt/docs/services.txt,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+  mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_service.c,
+  mlt/src/framework/mlt_service.h, mlt/src/framework/mlt_tractor.c,
+  mlt/src/framework/mlt_types.h, mlt/src/modules/Makefile,
+  mlt/src/modules/westley/Makefile, mlt/src/modules/westley/configure,
+  mlt/src/modules/westley/consumer_westley.c,
+  mlt/src/modules/westley/consumer_westley.h,
+  mlt/src/modules/westley/factory.c,
+  mlt/src/modules/westley/producer_westley.c,
+  mlt/src/modules/westley/producer_westley.h, mlt/src/tests/dan.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+  src/framework/mlt_repository.c, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_types.h, src/modules/Makefile,
+  src/modules/westley/Makefile, src/modules/westley/configure,
+  src/modules/westley/consumer_westley.c,
+  src/modules/westley/consumer_westley.h, src/modules/westley/factory.c,
+  src/modules/westley/producer_westley.c,
+  src/modules/westley/producer_westley.h, src/tests/dan.c: added
+  modules/westley  
+
+2004-01-20  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/inigo.txt, mlt/docs/inigo.txt, mlt/src/modules/dv/consumer_libdv.c,
+  src/modules/dv/consumer_libdv.c: updated libdv consumer  
+
+2004-01-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/inigo.txt, docs/testing-20040110.txt, mlt/docs/inigo.txt,
+  mlt/docs/testing-20040110.txt, mlt/src/framework/mlt_consumer.c,
+  mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_tractor.c, mlt/src/inigo/inigo.c,
+  mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit_commands.c,
+  mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/core/transition_mix.c, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_frame.c, src/framework/mlt_tractor.c, src/inigo/inigo.c,
+  src/miracle/miracle_unit.c, src/miracle/miracle_unit_commands.c,
+  src/modules/core/transition_luma.c, src/modules/core/transition_mix.c,
+  src/modules/sdl/consumer_sdl.c: inigo docs load/stop corrections  
+
+2004-01-17  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_playlist.c,
+  mlt/src/framework/mlt_playlist.h, mlt/src/framework/mlt_producer.c,
+  mlt/src/modules/Makefile, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/modules/Makefile: insert/move/remove dvcp operations  
+
+2004-01-17  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/core/transition_mix.c, src/modules/core/transition_mix.c:
+  default mix to 0.5  
+
+  * docs/services.txt, mlt/docs/services.txt, mlt/src/miracle/miracle_log.c,
+  mlt/src/miracle/miracle_unit.c, mlt/src/modules/Makefile,
+  mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+  mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_volume.c,
+  mlt/src/modules/core/filter_volume.h,
+  mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/core/transition_composite.h,
+  mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/core/transition_mix.c, mlt/src/modules/core/transition_mix.h,
+  mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/resample/Makefile,
+  mlt/src/modules/resample/configure, mlt/src/modules/resample/factory.c,
+  mlt/src/modules/resample/filter_resample.c,
+  mlt/src/modules/resample/filter_resample.h, mlt/src/tests/luma.c,
+  mlt/src/tests/pango.c, src/miracle/miracle_log.c, src/miracle/miracle_unit.c,
+  src/modules/Makefile, src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_volume.c,
+  src/modules/core/filter_volume.h, src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h, src/modules/core/transition_luma.c,
+  src/modules/core/transition_mix.c, src/modules/core/transition_mix.h,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/resample/Makefile, src/modules/resample/configure,
+  src/modules/resample/factory.c, src/modules/resample/filter_resample.c,
+  src/modules/resample/filter_resample.h, src/tests/luma.c, src/tests/pango.c:
+  new volume, mix, and resample filters and transitions  
+
+2004-01-15  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo usage message  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_tractor.c,
+  mlt/src/modules/inigo/producer_inigo.c, src/framework/mlt_frame.c,
+  src/framework/mlt_tractor.c, src/modules/inigo/producer_inigo.c: finally -
+  multitrack inigo serialisation  
+
+  * mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_tractor.c,
+  src/framework/mlt_producer.c, src/framework/mlt_tractor.c: in/out
+  specification on .inigo serialisations  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_multitrack.c,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_producer.c,
+  mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_tractor.c,
+  mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+  mlt/src/inigo/inigo.c, mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/inigo/producer_inigo.c, mlt/src/tests/charlie.c,
+  src/framework/mlt_frame.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/framework/mlt_properties.c, src/framework/mlt_tractor.c,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+  src/inigo/inigo.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/inigo/producer_inigo.c,
+  src/tests/charlie.c: partial corrections to serialisation  
+
+2004-01-14  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+  mlt/src/framework/mlt_tractor.c, mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/dv/consumer_libdv.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/framework/mlt_tractor.c,
+  src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c,
+  src/modules/ffmpeg/producer_ffmpeg.c: some temporary fixes  
+
+  * mlt/src/modules/dv/consumer_libdv.c, src/modules/dv/consumer_libdv.c: Minor
+  mods  
+
+  * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+  mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_producer.c, src/modules/ffmpeg/producer_ffmpeg.c: Minor
+  mods  
+
+  * mlt/src/framework/mlt_frame.c, src/framework/mlt_frame.c: Minor mods  
+
+  * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt,
+  mlt/src/framework/mlt_consumer.c, mlt/src/framework/mlt_factory.c,
+  mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+  mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+  mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+  mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+  mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+  mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+  mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+  mlt/src/framework/mlt_types.h, mlt/src/inigo/inigo.c,
+  mlt/src/miracle/miracle_unit.c, mlt/src/modules/core/producer_ppm.c,
+  mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/core/transition_luma.c, mlt/src/modules/dv/Makefile,
+  mlt/src/modules/dv/configure, mlt/src/modules/dv/consumer_libdv.c,
+  mlt/src/modules/dv/consumer_libdv.h, mlt/src/modules/dv/factory.c,
+  mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/inigo/producer_inigo.c, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_consumer.c, src/framework/mlt_factory.c,
+  src/framework/mlt_field.c, src/framework/mlt_field.h,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_producer.h,
+  src/framework/mlt_properties.c, src/framework/mlt_properties.h,
+  src/framework/mlt_property.c, src/framework/mlt_property.h,
+  src/framework/mlt_tractor.c, src/framework/mlt_tractor.h,
+  src/framework/mlt_transition.c, src/framework/mlt_transition.h,
+  src/framework/mlt_types.h, src/inigo/inigo.c, src/miracle/miracle_unit.c,
+  src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c,
+  src/modules/core/transition_luma.c, src/modules/dv/Makefile,
+  src/modules/dv/configure, src/modules/dv/consumer_libdv.c,
+  src/modules/dv/consumer_libdv.h, src/modules/dv/factory.c,
+  src/modules/dv/producer_libdv.c, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/inigo/producer_inigo.c,
+  src/modules/sdl/consumer_sdl.c: Removal of timecodes, consumer libdv,
+  serialisation of inigo  
+
+2004-01-13  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * README, docs/testing-20040110.txt, mlt/README,
+  mlt/docs/testing-20040110.txt, mlt/setenv, setenv: minor doc updates  
+
+2004-01-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt,
+  mlt/src/albino/Makefile, mlt/src/modules/configure, src/albino/Makefile,
+  src/modules/configure: minor testing update  
+
+2004-01-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/testing-20040110.txt, docs/testing.txt, mlt/docs/testing-20040110.txt,
+  mlt/docs/testing.txt: update testing.txt for miracle and complete initial
+  testing.txt results  
+
+  * docs/services.txt, mlt/docs/services.txt: change bluefish arg  
+
+  * docs/testing-20040110.txt, mlt/docs/testing-20040110.txt: updated with user
+  acceptance test results  
+
+2004-01-12  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/inigo/inigo.c, mlt/src/modules/inigo/producer_inigo.c,
+  src/inigo/inigo.c, src/modules/inigo/producer_inigo.c: minor corrections  
+
+  * mlt/src/inigo/inigo.c, src/inigo/inigo.c: minor corrections  
+
+  * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: minor
+  corrections  
+
+  * mlt/src/albino/albino.c, mlt/src/miracle/miracle_commands.c,
+  mlt/src/miracle/miracle_connection.c, src/albino/albino.c,
+  src/miracle/miracle_commands.c, src/miracle/miracle_connection.c: minor
+  corrections  
+
+  * mlt/src/inigo/inigo.c, mlt/src/modules/inigo/producer_inigo.c,
+  src/inigo/inigo.c, src/modules/inigo/producer_inigo.c: inigo rewrite,
+  producer, serialise and deserialise  
+
+  * docs/services.txt, docs/testing-20040110.txt, mlt/docs/services.txt,
+  mlt/docs/testing-20040110.txt, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_playlist.c, mlt/src/inigo/inigo.c,
+  mlt/src/miracle/miracle_unit.c, mlt/src/modules/Makefile,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/inigo/Makefile,
+  mlt/src/modules/inigo/configure, mlt/src/modules/inigo/factory.c,
+  mlt/src/modules/inigo/producer_inigo.c,
+  mlt/src/modules/inigo/producer_inigo.h, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_frame.c, src/framework/mlt_playlist.c, src/inigo/inigo.c,
+  src/miracle/miracle_unit.c, src/modules/Makefile,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/inigo/Makefile,
+  src/modules/inigo/configure, src/modules/inigo/factory.c,
+  src/modules/inigo/producer_inigo.c, src/modules/inigo/producer_inigo.h,
+  src/modules/sdl/consumer_sdl.c: inigo rewrite, producer, serialise and
+  deserialise  
+
+2004-01-12  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/testing-20040110.txt, docs/testing.txt, mlt/docs/testing-20040110.txt,
+  mlt/docs/testing.txt: adding testing.txt and initial test results  
+
+  * docs/services.txt, mlt/docs/services.txt: pango markup encoding  
+
+  * docs/services.txt, mlt/docs/services.txt,
+  mlt/src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.c:
+  doc updates and better control of pixbuf composite property propogation  
+
+  * mlt/src/inigo/inigo.c, mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/gtk2/producer_pango.c, mlt/src/modules/gtk2/producer_pango.h,
+  src/inigo/inigo.c, src/modules/core/transition_composite.c,
+  src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.h: better
+  propogating of producer and transition properties to the frame in pango and
+  composite; add pango support to inigo  
+
+2004-01-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_frame.c, src/framework/mlt_frame.c: small change to
+  prevent segfault in some transitions time specifications  
+
+2004-01-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.c:
+  multitrack eof handling  
+
+  * docs/dvcp.txt, docs/valerie.txt, mlt/docs/dvcp.txt, mlt/docs/valerie.txt,
+  mlt/src/framework/mlt_playlist.c, mlt/src/miracle/miracle_unit.c,
+  mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+  src/framework/mlt_playlist.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c: uset and
+  doco  
+
+2004-01-11  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/tests/dissolve.c, mlt/src/tests/luma.c, src/tests/dissolve.c,
+  src/tests/luma.c: remove no longer necessary blanks  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/tests/Makefile,
+  mlt/src/tests/clock16ntsc.pgm, mlt/src/tests/clock16pal.pgm,
+  mlt/src/tests/dan.c, mlt/src/tests/dissolve.c, mlt/src/tests/luma.c,
+  mlt/src/tests/pango.c, mlt/src/tests/pixbuf.c, src/framework/mlt_frame.c,
+  src/modules/core/transition_luma.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/tests/Makefile,
+  src/tests/clock16ntsc.pgm, src/tests/clock16pal.pgm, src/tests/dan.c,
+  src/tests/dissolve.c, src/tests/luma.c, src/tests/pango.c,
+  src/tests/pixbuf.c: 4 new tests, bugfixes in pango, pixbuf, transition_luma,
+  and mlt_frame_audio_mix  
+
+2004-01-11  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c: eof=continue
+  and eof=pause  
+
+  * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: end of
+  playlist position fix  
+
+2004-01-10  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/sdl/consumer_sdl.c, mlt/src/tests/dan.c,
+  src/framework/mlt_frame.c, src/modules/core/transition_luma.c,
+  src/modules/sdl/consumer_sdl.c, src/tests/dan.c: attempt to retain samples in
+  mlt_frame_mix_audio, make consumers request the number of samples to
+  get_audio  
+
+2004-01-10  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: in/out fix 
+
+  * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo gets transitions  
+
+  * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+  mlt/src/miracle/miracle_unit.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/miracle/miracle_unit.c: more int64 frame
+  addressing in playlist  
+
+2004-01-09  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+  mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+  mlt/src/humperdink/client.c, mlt/src/humperdink/remote.c,
+  mlt/src/miracle/miracle_local.c, mlt/src/miracle/miracle_unit.c,
+  mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+  mlt/src/modules/dv/producer_libdv.c, mlt/src/valerie/valerie.c,
+  mlt/src/valerie/valerie.h, mlt/src/valerie/valerie_status.c,
+  mlt/src/valerie/valerie_status.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_property.c,
+  src/framework/mlt_property.h, src/humperdink/client.c,
+  src/humperdink/remote.c, src/miracle/miracle_local.c,
+  src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+  src/miracle/miracle_unit_commands.c, src/modules/dv/producer_libdv.c,
+  src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_status.c,
+  src/valerie/valerie_status.h: int64 based comms and more unit functionality  
+
+  * mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_local.c,
+  src/miracle/miracle.c, src/miracle/miracle_local.c: albino  
+
+  * Makefile, mlt/Makefile, mlt/setenv, mlt/src/albino/Makefile,
+  mlt/src/albino/albino.c, mlt/src/framework/mlt_transition.c,
+  mlt/src/framework/mlt_transition.h, mlt/src/miracle/Makefile, setenv,
+  src/albino/Makefile, src/albino/albino.c, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h, src/miracle/Makefile: albino  
+
+2004-01-08  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/inigo/inigo.c, src/inigo/inigo.c: inigo track test  
+
+  * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_playlist.c,
+  mlt/src/framework/mlt_playlist.h, mlt/src/framework/mlt_properties.c,
+  mlt/src/miracle/miracle_unit.c, mlt/src/miracle/miracle_unit.h,
+  mlt/src/miracle/miracle_unit_commands.c, mlt/src/modules/dv/producer_libdv.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_properties.c, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+  src/modules/dv/producer_libdv.c: More miracle mods  
+
+2004-01-08  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.c: some
+  fixes to the fixes  
+
+  * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_frame.h, mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/tests/dan.c, src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/modules/core/transition_luma.c, src/modules/dv/producer_libdv.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/sdl/consumer_sdl.c,
+  src/tests/dan.c: move audio sample calculator to mlt_frame and use from
+  ffmpeg and mcmpeg, add mlt_frame_audio_mix, add audio_crossfade to
+  transition_luma, add to docs  
+
+2004-01-07  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, docs/services.txt, mlt/Makefile, mlt/docs/services.txt,
+  mlt/setenv, mlt/src/framework/Makefile, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/humperdink/Makefile,
+  mlt/src/humperdink/client.c, mlt/src/humperdink/client.h,
+  mlt/src/humperdink/io.c, mlt/src/humperdink/io.h,
+  mlt/src/humperdink/remote.c, mlt/src/inigo/inigo.c, mlt/src/miracle/Makefile,
+  mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_commands.c,
+  mlt/src/miracle/miracle_commands.h, mlt/src/miracle/miracle_connection.c,
+  mlt/src/miracle/miracle_connection.h, mlt/src/miracle/miracle_local.c,
+  mlt/src/miracle/miracle_local.h, mlt/src/miracle/miracle_log.c,
+  mlt/src/miracle/miracle_log.h, mlt/src/miracle/miracle_server.c,
+  mlt/src/miracle/miracle_server.h, mlt/src/miracle/miracle_unit.c,
+  mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+  mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/core/producer_ppm.c,
+  mlt/src/modules/dv/producer_libdv.c, mlt/src/modules/ffmpeg/audio.sh,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c, mlt/src/modules/ffmpeg/video.sh,
+  mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/valerie/Makefile, mlt/src/valerie/valerie.c,
+  mlt/src/valerie/valerie.h, setenv, src/framework/Makefile,
+  src/framework/mlt_frame.c, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/humperdink/Makefile, src/humperdink/client.c, src/humperdink/client.h,
+  src/humperdink/io.c, src/humperdink/io.h, src/humperdink/remote.c,
+  src/inigo/inigo.c, src/miracle/Makefile, src/miracle/miracle.c,
+  src/miracle/miracle_commands.c, src/miracle/miracle_commands.h,
+  src/miracle/miracle_connection.c, src/miracle/miracle_connection.h,
+  src/miracle/miracle_local.c, src/miracle/miracle_local.h,
+  src/miracle/miracle_log.c, src/miracle/miracle_log.h,
+  src/miracle/miracle_server.c, src/miracle/miracle_server.h,
+  src/miracle/miracle_unit.c, src/miracle/miracle_unit.h,
+  src/miracle/miracle_unit_commands.c, src/miracle/miracle_unit_commands.h,
+  src/modules/core/producer_ppm.c, src/modules/dv/producer_libdv.c,
+  src/modules/ffmpeg/audio.sh, src/modules/ffmpeg/producer_ffmpeg.c,
+  src/modules/ffmpeg/video.sh, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+  src/modules/sdl/consumer_sdl.c, src/valerie/Makefile, src/valerie/valerie.c,
+  src/valerie/valerie.h: miracle part 1  
+
+2004-01-06  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/core/transition_luma.c,
+  mlt/src/modules/core/transition_luma.h, src/modules/core/transition_luma.c,
+  src/modules/core/transition_luma.h: add forgotten files  
+
+  * mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+  mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+  mlt/src/modules/core/factory.c, mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/tests/dan.c, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/dv/producer_libdv.c, src/tests/dan.c: added luma transition and
+  new frame properties  
+
+2004-01-03  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+  mlt/src/framework/mlt_producer.c, mlt/src/inigo/inigo.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/inigo/inigo.c, src/modules/ffmpeg/producer_ffmpeg.c: more complete
+  next/prev clip behaviour  
+
+2004-01-02  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/gtk2/producer_pango.c, mlt/src/modules/gtk2/producer_pango.h,
+  mlt/src/tests/dan.c, src/framework/mlt_frame.c,
+  src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pango.h, src/tests/dan.c: fixup and optimize edge
+  conditions of composite; updated property handling of producer_pango  
+
+2004-01-02  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, mlt/docs/services.txt,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_properties.c,
+  mlt/src/framework/mlt_properties.h, mlt/src/framework/mlt_types.h,
+  mlt/src/inigo/inigo.c, mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/modules/ffmpeg/Makefile, mlt/src/modules/ffmpeg/configure,
+  mlt/src/modules/ffmpeg/consumer_ffmpeg.c,
+  mlt/src/modules/ffmpeg/consumer_ffmpeg.h, mlt/src/modules/ffmpeg/factory.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h,
+  src/framework/mlt_playlist.c, src/framework/mlt_playlist.h,
+  src/framework/mlt_producer.c, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_types.h, src/inigo/inigo.c,
+  src/modules/dv/producer_libdv.c, src/modules/ffmpeg/Makefile,
+  src/modules/ffmpeg/configure, src/modules/ffmpeg/consumer_ffmpeg.c,
+  src/modules/ffmpeg/consumer_ffmpeg.h, src/modules/ffmpeg/factory.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c: incomplete next/prev clip behaviour  
+
+2004-01-01  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * docs/services.txt, mlt/docs/services.txt, mlt/src/framework/mlt_factory.c,
+  mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_multitrack.c,
+  mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/audio.sh,
+  mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/ffmpeg/video.sh,
+  src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_multitrack.c, src/framework/mlt_producer.c,
+  src/modules/ffmpeg/audio.sh, src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/ffmpeg/producer_ffmpeg.h,
+  src/modules/ffmpeg/video.sh: ntsc fixes and service doco for discussion  
+
+2003-12-31  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/ffmpeg/Makefile, mlt/src/modules/ffmpeg/configure,
+  mlt/src/modules/ffmpeg/factory.c, mlt/src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  mlt/src/modules/ffmpeg/filter_ffmpeg_dub.h,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c, src/modules/ffmpeg/Makefile,
+  src/modules/ffmpeg/configure, src/modules/ffmpeg/factory.c,
+  src/modules/ffmpeg/filter_ffmpeg_dub.c,
+  src/modules/ffmpeg/filter_ffmpeg_dub.h, src/modules/ffmpeg/producer_ffmpeg.c,
+  src/modules/gtk2/producer_pixbuf.c: ffmpeg audio dub  
+
+2003-12-30  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/sdl/consumer_sdl.c, src/modules/ffmpeg/producer_ffmpeg.c,
+  src/modules/sdl/consumer_sdl.c: correction on playlist ffmpeg sizing issue
+  and additional sdl tweaks  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_multitrack.c,
+  mlt/src/inigo/inigo.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/gtk2/producer_pixbuf.c, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_frame.c, src/framework/mlt_multitrack.c, src/inigo/inigo.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/sdl/consumer_sdl.c: More sdl experimental mods, pixbuf writable
+  work around and minor fixes  
+
+2003-12-29  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.c,
+  mlt/src/framework/mlt_producer.c, mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/sdl/consumer_sdl.c, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.c, src/framework/mlt_producer.c,
+  src/modules/ffmpeg/producer_ffmpeg.c, src/modules/sdl/consumer_sdl.c: Many
+  ffmpeg and sdl mods  
+
+2003-12-28  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.c: SDL a/v
+  sync issues [incomplete]  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+  mlt/src/inigo/inigo.c, mlt/src/modules/Makefile,
+  mlt/src/modules/core/factory.c, mlt/src/modules/core/producer_ppm.c,
+  mlt/src/modules/core/producer_ppm.h, mlt/src/modules/ffmpeg/Makefile,
+  mlt/src/modules/ffmpeg/configure, mlt/src/modules/ffmpeg/factory.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.c,
+  mlt/src/modules/ffmpeg/producer_ffmpeg.h, mlt/src/modules/sdl/consumer_sdl.c,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/inigo/inigo.c,
+  src/modules/Makefile, src/modules/core/factory.c,
+  src/modules/core/producer_ppm.c, src/modules/core/producer_ppm.h,
+  src/modules/ffmpeg/Makefile, src/modules/ffmpeg/configure,
+  src/modules/ffmpeg/factory.c, src/modules/ffmpeg/producer_ffmpeg.c,
+  src/modules/ffmpeg/producer_ffmpeg.h, src/modules/sdl/consumer_sdl.c: Added
+  ffmpeg producer  
+
+2003-12-27  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * Makefile, README, configure, mlt/Makefile, mlt/README, mlt/configure,
+  mlt/setenv, mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_playlist.c,
+  mlt/src/inigo/Makefile, mlt/src/inigo/inigo.c, mlt/src/inigo/io.c,
+  mlt/src/inigo/io.h, mlt/src/modules/core/filter_resize.c,
+  mlt/src/modules/core/filter_resize.h, mlt/src/modules/core/producer_ppm.c,
+  mlt/src/modules/core/producer_ppm.h, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/tests/charlie.c, setenv, src/framework/mlt_frame.c,
+  src/framework/mlt_playlist.c, src/inigo/Makefile, src/inigo/inigo.c,
+  src/inigo/io.c, src/inigo/io.h, src/modules/core/filter_resize.c,
+  src/modules/core/filter_resize.h, src/modules/core/producer_ppm.c,
+  src/modules/core/producer_ppm.h, src/modules/sdl/consumer_sdl.c,
+  src/tests/charlie.c: ppm ffmpeg  
+
+2003-12-26  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+  mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_gamma.c,
+  mlt/src/modules/core/filter_gamma.h, mlt/src/modules/core/filter_resize.h,
+  mlt/src/tests/io.c, src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_gamma.c,
+  src/modules/core/filter_gamma.h, src/modules/core/filter_resize.h,
+  src/tests/io.c: Gamma filter  
+
+  * mlt/src/tests/charlie.c, src/tests/charlie.c: quit fix for SDL  
+
+  * mlt/src/framework/mlt_playlist.c, src/framework/mlt_playlist.c: playlist
+  fps fix  
+
+  * mlt/src/tests/io.c, mlt/src/tests/io.h, src/tests/io.c, src/tests/io.h:
+  added io files  
+
+  * mlt/src/tests/charlie.c, src/tests/charlie.c: SDL transport callback  
+
+  * mlt/src/framework/mlt_property.c, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/tests/Makefile, mlt/src/tests/charlie.c,
+  src/framework/mlt_property.c, src/modules/sdl/consumer_sdl.c,
+  src/tests/Makefile, src/tests/charlie.c: SDL transport callback  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+  mlt/src/modules/sdl/consumer_sdl.c, src/framework/mlt_frame.c,
+  src/framework/mlt_frame.h, src/modules/sdl/consumer_sdl.c: More SDL tweaks  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/modules/sdl/consumer_sdl.h, mlt/src/tests/charlie.c,
+  src/framework/mlt_frame.c, src/modules/sdl/consumer_sdl.c,
+  src/modules/sdl/consumer_sdl.h, src/tests/charlie.c: More SDL updates  
+
+  * mlt/src/modules/core/filter_resize.c, mlt/src/modules/sdl/consumer_sdl.c,
+  src/modules/core/filter_resize.c, src/modules/sdl/consumer_sdl.c: SDL updates
+  and resizing fix  
+
+2003-12-25  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+  mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/modules/core/Makefile,
+  mlt/src/modules/core/configure, mlt/src/modules/core/factory.c,
+  mlt/src/modules/core/filter_resize.c, mlt/src/modules/core/filter_resize.h,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/tests/charlie.c, src/framework/mlt_field.c,
+  src/framework/mlt_field.h, src/framework/mlt_filter.c,
+  src/framework/mlt_filter.h, src/framework/mlt_playlist.c,
+  src/modules/core/Makefile, src/modules/core/configure,
+  src/modules/core/factory.c, src/modules/core/filter_resize.c,
+  src/modules/core/filter_resize.h, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/gtk2/producer_pixbuf.h, src/modules/sdl/consumer_sdl.c,
+  src/tests/charlie.c: field and playlist enhancements, producer pixbuf reorg  
+
+2003-12-24  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/Makefile, mlt/src/framework/mlt.h,
+  mlt/src/framework/mlt_field.c, mlt/src/framework/mlt_field.h,
+  mlt/src/framework/mlt_filter.c, mlt/src/framework/mlt_filter.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_types.h, mlt/src/tests/charlie.c, mlt/src/tests/setenv,
+  src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_field.c,
+  src/framework/mlt_field.h, src/framework/mlt_filter.c,
+  src/framework/mlt_filter.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_types.h, src/tests/charlie.c,
+  src/tests/setenv: field and playlist provisional implementations  
+
+2003-12-23  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/sdl/consumer_sdl.c, mlt/src/tests/charlie.c,
+  mlt/src/tests/dan.c, src/modules/sdl/consumer_sdl.c, src/tests/charlie.c,
+  src/tests/dan.c: SDL fixes on close  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/framework/mlt_frame.h,
+  mlt/src/modules/Makefile, mlt/src/tests/charlie.c, mlt/src/tests/setenv,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/modules/Makefile,
+  src/tests/charlie.c, src/tests/setenv: test frame services  
+
+2003-12-23  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt_consumer.c, mlt/src/framework/mlt_consumer.h,
+  mlt/src/framework/mlt_frame.h, mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pango.h,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/tests/dan.c,
+  src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_frame.h, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/gtk2/producer_pixbuf.h, src/tests/dan.c: add video_standard enum
+  to mlt_frame, add mlt_consumer_properties, add properties to gtk2 producers
+  and bluefish consumer  
+
+2003-12-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/Makefile, mlt/src/modules/dv/producer_libdv.c,
+  mlt/src/tests/charlie.c, src/modules/Makefile,
+  src/modules/dv/producer_libdv.c, src/tests/charlie.c: minor tidy up  
+
+2003-12-22  ddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/Makefile, mlt/src/modules/gtk2/configure,
+  mlt/src/tests/dan.c, src/modules/Makefile, src/modules/gtk2/configure,
+  src/tests/dan.c: allow for building mainconcept and bluefish plugins outside
+  cvs  
+
+  * mlt/src/framework/mlt_frame.c, mlt/src/modules/gtk2/Makefile,
+  mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pango.c,
+  mlt/src/modules/gtk2/producer_pango.h,
+  mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/tests/dan.c,
+  src/framework/mlt_frame.c, src/modules/gtk2/Makefile,
+  src/modules/gtk2/factory.c, src/modules/gtk2/producer_pango.c,
+  src/modules/gtk2/producer_pango.h, src/modules/gtk2/producer_pixbuf.c,
+  src/modules/gtk2/producer_pixbuf.h, src/tests/dan.c: add sample aspect ratio
+  scaling output to producer_pixbuf, fix a bug in rgb to yuv conversions, add
+  producer_pango  
+
+2003-12-22  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/framework/mlt.h, mlt/src/framework/mlt_repository.c,
+  src/framework/mlt.h, src/framework/mlt_repository.c: c++ compatability  
+
+  * README, mlt/README, mlt/src/framework/Makefile, mlt/src/framework/mlt.h,
+  mlt/src/framework/mlt_factory.c, mlt/src/framework/mlt_factory.h,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+  mlt/src/modules/core/Makefile, mlt/src/modules/dv/Makefile,
+  mlt/src/modules/gtk2/Makefile, mlt/src/modules/sdl/Makefile,
+  mlt/src/tests/Makefile, mlt/src/tests/charlie.c, mlt/src/tests/dan.c,
+  src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_factory.c,
+  src/framework/mlt_factory.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_repository.c,
+  src/framework/mlt_repository.h, src/modules/core/Makefile,
+  src/modules/dv/Makefile, src/modules/gtk2/Makefile, src/modules/sdl/Makefile,
+  src/tests/Makefile, src/tests/charlie.c, src/tests/dan.c: Factory
+  implementation  
+
+2003-12-19  lilo_booter <lilo_booter@d19143bc-622f-0410-bfdd-b5b2a6649095>
+
+  * mlt/src/modules/core/Makefile, mlt/src/modules/core/configure,
+  mlt/src/modules/core/factory.c, mlt/src/modules/core/filter_deinterlace.c,
+  mlt/src/modules/core/filter_deinterlace.h,
+  mlt/src/modules/core/filter_greyscale.c,
+  mlt/src/modules/core/filter_greyscale.h, mlt/src/modules/core/producer_ppm.c,
+  mlt/src/modules/core/producer_ppm.h,
+  mlt/src/modules/core/transition_composite.c,
+  mlt/src/modules/core/transition_composite.h, src/modules/core/Makefile,
+  src/modules/core/configure, src/modules/core/factory.c,
+  src/modules/core/filter_deinterlace.c, src/modules/core/filter_deinterlace.h,
+  src/modules/core/filter_greyscale.c, src/modules/core/filter_greyscale.h,
+  src/modules/core/producer_ppm.c, src/modules/core/producer_ppm.h,
+  src/modules/core/transition_composite.c,
+  src/modules/core/transition_composite.h: Added files rejected by import  
+
+  * Makefile, README, configure, mlt/Makefile, mlt/README, mlt/configure,
+  mlt/src/framework/Makefile, mlt/src/framework/config.h,
+  mlt/src/framework/configure, mlt/src/framework/mlt_consumer.c,
+  mlt/src/framework/mlt_consumer.h, mlt/src/framework/mlt_factory.c,
+  mlt/src/framework/mlt_factory.h, mlt/src/framework/mlt_filter.c,
+  mlt/src/framework/mlt_filter.h, mlt/src/framework/mlt_frame.c,
+  mlt/src/framework/mlt_frame.h, mlt/src/framework/mlt_manager.h,
+  mlt/src/framework/mlt_multitrack.c, mlt/src/framework/mlt_multitrack.h,
+  mlt/src/framework/mlt_playlist.c, mlt/src/framework/mlt_playlist.h,
+  mlt/src/framework/mlt_producer.c, mlt/src/framework/mlt_producer.h,
+  mlt/src/framework/mlt_properties.c, mlt/src/framework/mlt_properties.h,
+  mlt/src/framework/mlt_property.c, mlt/src/framework/mlt_property.h,
+  mlt/src/framework/mlt_repository.c, mlt/src/framework/mlt_repository.h,
+  mlt/src/framework/mlt_service.c, mlt/src/framework/mlt_service.h,
+  mlt/src/framework/mlt_tractor.c, mlt/src/framework/mlt_tractor.h,
+  mlt/src/framework/mlt_transition.c, mlt/src/framework/mlt_transition.h,
+  mlt/src/framework/mlt_types.h, mlt/src/miracle/configure,
+  mlt/src/miracle/miracle.c, mlt/src/miracle/miracle_commands.c,
+  mlt/src/miracle/miracle_commands.h, mlt/src/miracle/miracle_connection.c,
+  mlt/src/miracle/miracle_connection.h, mlt/src/miracle/miracle_local.c,
+  mlt/src/miracle/miracle_local.h, mlt/src/miracle/miracle_log.c,
+  mlt/src/miracle/miracle_log.h, mlt/src/miracle/miracle_server.c,
+  mlt/src/miracle/miracle_server.h, mlt/src/miracle/miracle_unit.c,
+  mlt/src/miracle/miracle_unit.h, mlt/src/miracle/miracle_unit_commands.c,
+  mlt/src/miracle/miracle_unit_commands.h, mlt/src/modules/Makefile,
+  mlt/src/modules/configure, mlt/src/modules/dv/Makefile,
+  mlt/src/modules/dv/configure, mlt/src/modules/dv/factory.c,
+  mlt/src/modules/dv/producer_libdv.c, mlt/src/modules/dv/producer_libdv.h,
+  mlt/src/modules/gtk2/Makefile, mlt/src/modules/gtk2/configure,
+  mlt/src/modules/gtk2/factory.c, mlt/src/modules/gtk2/producer_pixbuf.c,
+  mlt/src/modules/gtk2/producer_pixbuf.h, mlt/src/modules/sdl/Makefile,
+  mlt/src/modules/sdl/configure, mlt/src/modules/sdl/consumer_sdl.c,
+  mlt/src/modules/sdl/consumer_sdl.h, mlt/src/modules/sdl/factory.c,
+  mlt/src/tests/charlie.c, mlt/src/tests/dan.c, mlt/src/tests/test.png,
+  mlt/src/valerie/Makefile, mlt/src/valerie/configure,
+  mlt/src/valerie/valerie.c, mlt/src/valerie/valerie.h,
+  mlt/src/valerie/valerie_notifier.c, mlt/src/valerie/valerie_notifier.h,
+  mlt/src/valerie/valerie_parser.c, mlt/src/valerie/valerie_parser.h,
+  mlt/src/valerie/valerie_remote.c, mlt/src/valerie/valerie_remote.h,
+  mlt/src/valerie/valerie_response.c, mlt/src/valerie/valerie_response.h,
+  mlt/src/valerie/valerie_socket.c, mlt/src/valerie/valerie_socket.h,
+  mlt/src/valerie/valerie_status.c, mlt/src/valerie/valerie_status.h,
+  mlt/src/valerie/valerie_tokeniser.c, mlt/src/valerie/valerie_tokeniser.h,
+  mlt/src/valerie/valerie_util.c, mlt/src/valerie/valerie_util.h,
+  src/framework/Makefile, src/framework/config.h, src/framework/configure,
+  src/framework/mlt_consumer.c, src/framework/mlt_consumer.h,
+  src/framework/mlt_factory.c, src/framework/mlt_factory.h,
+  src/framework/mlt_filter.c, src/framework/mlt_filter.h,
+  src/framework/mlt_frame.c, src/framework/mlt_frame.h,
+  src/framework/mlt_manager.h, src/framework/mlt_multitrack.c,
+  src/framework/mlt_multitrack.h, src/framework/mlt_playlist.c,
+  src/framework/mlt_playlist.h, src/framework/mlt_producer.c,
+  src/framework/mlt_producer.h, src/framework/mlt_properties.c,
+  src/framework/mlt_properties.h, src/framework/mlt_property.c,
+  src/framework/mlt_property.h, src/framework/mlt_repository.c,
+  src/framework/mlt_repository.h, src/framework/mlt_service.c,
+  src/framework/mlt_service.h, src/framework/mlt_tractor.c,
+  src/framework/mlt_tractor.h, src/framework/mlt_transition.c,
+  src/framework/mlt_transition.h, src/framework/mlt_types.h,
+  src/miracle/configure, src/miracle/miracle.c, src/miracle/miracle_commands.c,
+  src/miracle/miracle_commands.h, src/miracle/miracle_connection.c,
+  src/miracle/miracle_connection.h, src/miracle/miracle_local.c,
+  src/miracle/miracle_local.h, src/miracle/miracle_log.c,
+  src/miracle/miracle_log.h, src/miracle/miracle_server.c,
+  src/miracle/miracle_server.h, src/miracle/miracle_unit.c,
+  src/miracle/miracle_unit.h, src/miracle/miracle_unit_commands.c,
+  src/miracle/miracle_unit_commands.h, src/modules/Makefile,
+  src/modules/configure, src/modules/dv/Makefile, src/modules/dv/configure,
+  src/modules/dv/factory.c, src/modules/dv/producer_libdv.c,
+  src/modules/dv/producer_libdv.h, src/modules/gtk2/Makefile,
+  src/modules/gtk2/configure, src/modules/gtk2/factory.c,
+  src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.h,
+  src/modules/sdl/Makefile, src/modules/sdl/configure,
+  src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.h,
+  src/modules/sdl/factory.c, src/tests/charlie.c, src/tests/dan.c,
+  src/tests/test.png, src/valerie/Makefile, src/valerie/configure,
+  src/valerie/valerie.c, src/valerie/valerie.h, src/valerie/valerie_notifier.c,
+  src/valerie/valerie_notifier.h, src/valerie/valerie_parser.c,
+  src/valerie/valerie_parser.h, src/valerie/valerie_remote.c,
+  src/valerie/valerie_remote.h, src/valerie/valerie_response.c,
+  src/valerie/valerie_response.h, src/valerie/valerie_socket.c,
+  src/valerie/valerie_socket.h, src/valerie/valerie_status.c,
+  src/valerie/valerie_status.h, src/valerie/valerie_tokeniser.c,
+  src/valerie/valerie_tokeniser.h, src/valerie/valerie_util.c,
+  src/valerie/valerie_util.h: Initial revision  
diff --git a/Doxyfile b/Doxyfile
new file mode 100644 (file)
index 0000000..f876c57
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1462 @@
+# Doxyfile 1.5.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME = mlt
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.3.8
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH = src/framework/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = "properties=\xrefitem properties \"Property\" \"Properties Dictionary\"" "event=\xrefitem event \"Event\" \"Events Dictionary\""
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is 
+# probably good enough. For larger projects a too small cache size can cause 
+# doxygen to be busy swapping symbols to and from disk most of the time 
+# causing a significant performance penality. 
+# If the system has enough physical memory increasing the cache will improve the 
+# performance by keeping more symbols in memory. Note that the value works on 
+# a logarithmic scale so increasing the size by one will rougly double the 
+# memory usage. The cache size is given by this formula: 
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST = YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER = 
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
+# doxygen. The layout file controls the global structure of the generated output files 
+# in an output format independent way. The create the layout file that represents 
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
+# of the layout file.
+
+LAYOUT_FILE = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT = src/framework
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup. 
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
+# are set, an additional index file will be generated that can be used as input for 
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
+# be used to specify the file name of the resulting .qch file. 
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
+# be used to specify the location of Qt's qhelpgenerator. 
+# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# .qhp file .
+
+QHG_LOCATION = 
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, because dot on Windows does not 
+# seem to support this out of the box. Warning: Depending on the platform used, 
+# enabling this option may lead to badly anti-aliased labels on the edges of 
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/GPL b/GPL
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/GPL
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..264fdaa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+SUBDIRS = src/framework \
+                 src/inigo \
+                 src/valerie \
+                 src/miracle \
+                 src/humperdink \
+                 src/albino \
+                 src/modules \
+                 profiles
+
+all clean:
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               $(MAKE) -s -C $$subdir depend || exit 1; \
+               $(MAKE) -C $$subdir $@ || exit 1; \
+       done
+
+distclean:
+       rm mlt-config packages.dat; \
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               $(MAKE) -C $$subdir $@ || exit 1; \
+       done; \
+       rm config.mak;
+
+dist-clean: distclean
+
+include config.mak
+
+install:
+       install -d "$(DESTDIR)$(prefix)/bin"
+       install -d "$(DESTDIR)$(prefix)/include"
+       install -d "$(DESTDIR)$(libdir)"
+       install -d "$(DESTDIR)$(libdir)/mlt"
+       install -d "$(DESTDIR)$(libdir)/pkgconfig"
+       install -d "$(DESTDIR)$(prefix)/share/mlt"
+       install -c -m 755 mlt-config "$(DESTDIR)$(bindir)"
+       install -c -m 644 *.pc "$(DESTDIR)$(libdir)/pkgconfig"
+       install -m 644 packages.dat "$(DESTDIR)$(prefix)/share/mlt/"
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+       done; \
+       if test -z "$(DESTDIR)"; then \
+         /sbin/ldconfig 2> /dev/null || true; \
+       fi
+
+uninstall:
+       rm -f "$(DESTDIR)$(bindir)"/mlt-config
+       rm -f "$(DESTDIR)$(libdir)"/pkgconfig/mlt-*.pc
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+       done
+       rm -rf "$(DESTDIR)$(prefix)/include/mlt"
+       rm -rf "$(DESTDIR)$(prefix)/share/mlt"
+
+dist:
+       [ -d "mlt-$(version)" ] && rm -rf "mlt-$(version)" || echo
+       svn export http://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt "mlt-$(version)"
+       tar -cvzf "mlt-$(version).tar.gz" "mlt-$(version)"
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..f96d4ad
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,173 @@
+MLT Release Notes
+-----------------
+
+Version 0.3.8
+
+The recommended version of FFmpeg for use with this release is SVN r17923.
+
+This almost entirely a bugfix release to coincide with the Kdenlive 0.7.3
+release. See the ChangeLog (SVN log) for details.
+
+framework:
+* added mlt_cache API
+* improved doxygen documentation comments
+* added some 15 fps profiles
+* improved color property handling (support web-style '#' color value)
+* add const qualifier to many string parameters
+
+modules:
+* core: improved brightness filter
+* core: added image crop filter
+* frei0r: added support for producer/source plugins
+* frei0r: added support for color parameters
+* sdl: added window_background color property
+
+
+Version 0.3.6
+
+The recommended version of FFmpeg for use with this release is SVN r16849.
+
+This almost entirely a bugfix release to coincide with the Kdenlive 0.7.2
+release. See the ChangeLog (SVN log) for details.
+
+framework:
+* added mlt_log logging API
+* improved doxygen documentation comments
+
+avformat module:
+* consumer: report list of muxers when f=list and codecs when acodec=list or
+  vcodec=list
+* consumer: added support for an=1 or acodec=none and vn=1 or vcodec=none
+* producer: list available demuxers and decoders when constructor arg is like
+  f-list[[,]acodec-list][[,]vcodec-list]
+
+
+Version 0.3.4
+
+The recommended version of FFmpeg for use with this release is SVN r16313.
+
+This almost entirely a bugfix release. See the ChangeLog (SVN log) for details.
+There are a few notes:
+
+framework:
+* improved doxygen documentation comments (work in progress)
+  published docs are at http://mltframework.org/doxygen/
+
+avformat module:
+* added support for AVOption parsing to producer
+* added filter_swscale as alternative rescaler
+* added recommended FFmpeg revision to configure option --help
+* use recommended FFmpeg revision with --avformat-svn on release versions
+* added configure option --avformat-no-codecs
+* added configure option --avformat-no-filters
+
+misc:
+* new profile atsc_1080i_50
+* added --disable-sse option to configure script
+* improved build for OS X and x86-64 and improved handling of mmx/sse
+
+
+Version 0.3.2
+
+In addition to bug fixes detailed in the ChangeLog, here is a list of
+enhancements.
+
+framework:
+* deprecated mlt-config; use pkg-config instead
+* added more HD profiles
+
+modules:
+* sdl: added fullscreen property
+* sox: sox v14.1.0 compatibility
+* gtk: added force_reload property to producer_pixbuf
+* frei0r: added support for YAML Tiny metadata
+* frei0r: added keyframe support on double and boolean parameters
+* oldfilm: added keyframe support for filter_vignette
+* kdenlive: added filter_freeze
+
+inigo:
+* added -version, -silent, and -progress options
+* improved output of usage information
+* removed realtime process scheduling
+
+
+Version 0.3.0
+
+framework:
+* fix bugs with introduction of mlt_profile in v0.2.4
+* added versioning to libs
+* remove module registry and add dynamic module loading:
+  added mlt_repository_register, mlt_repository_consumers,
+  mlt_repository_filters, mlt_repository_producers, mlt_repository_transitions
+* new module metadata system based on YAML Tiny:
+  added mlt_repository_register_metadata, mlt_repository_metadata,
+  mlt_repository_languages, mlt_properties_is_sequence,
+  mlt_properties_parse_yaml, mlt_properties_serialise_yaml, and
+  added metaschema.yaml Kwalify schema file
+* mlt_consumer: added threaded, non-lossy processing when real_time=-1
+* added autoclose property to mlt_playlist for sequential processing
+  of very large playlists (prevents resource exhaustion)
+* mlt_factory_init now returns the global mlt_repository
+* change mlt_repository_fetch to mlt_repository_create
+* change mlt_factory_prefix to mlt_factory_directory
+* added mlt_field_disconnect_service
+
+modules:
+* move all modules from $datadir to $libdir
+* new oldfilm module by Marco Gittler
+* new frei0r module by Marco Gittler
+* new dgraft module by Dan Dennedy for inverse telecine (not ready yet)
+* avformat: added support for multi-threaded encoding and decoding
+* consumer_avformat: added support for AVOption to support all ffmpeg options
+  using ffmpeg-style property names
+* consumer_avformat: added support for dual pass encoding
+* qimage: added support for Qt4
+* sox: added support for sox v14.0.0
+* transition_composite: added animatable geometry-type "pan" property to crop
+  and pan instead of automatic down-scale
+
+inigo:
+* added -query option to lookup module metadata
+* added -profile option and support for progress=1 for kdenlive
+
+
+Version 0.2.4
+
+* framework: new extensible profiles system to replace MLT_NORMALISATION
+* module avformat: interlaced coding support for ffmpeg/libavcodec
+* module avformat: build improvements for --avformat-svn
+* new effectv module with BurningTV video filter
+* module qimage: added support for psd, xcf and exr images
+* numerous bugfixes
+
+
+Version 0.2.3
+
+* Addition of kdenlive module
+* Support for ffmpeg from subversion
+* Support for ffmpeg libswscale
+* Copyright and license cleanup
+
+
+Version 0.2.2
+
+* Prepared specifically for the kdenlive 0.3 release.
+* Contains some patches to support rgb24a output for the gdk-pixbuf and qimage
+  producers as well as some minor bugfixes.
+
+
+Version 0.2.1
+
+* Many improvements since initial releases due to development of Shotcut and
+  Jahshaka editing interfaces.
+
+
+Version 0.1.1
+
+* Minor modifications and bug fixes from the previous release. Better
+  ffmpeg/avformat integration and more reliable playback.
+
+
+Version 0.1.0
+
+* First official release
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..aaac6e2
--- /dev/null
+++ b/README
@@ -0,0 +1,65 @@
+MLT/Miracle README
+------------------
+
+       Sponsored by Ushodaya Enterprises Limited
+       Written by Charles Yates <charles.yates@pandora.be>
+       and Dan Dennedy <dan@dennedy.org>
+
+       MLT is a LGPL multimedia framework designed for television broadcasting,
+       and Miracle is a GPL multi-unit video playout server with realtime
+       effects.
+
+       This document provides a quick reference for the minimal configuration,
+       build and installation of MLT. See the docs directory for usage and
+       development details.
+
+
+Configuration
+-------------
+
+       Configuration is triggered by running:
+
+           ./configure 
+
+       More information on usage is found by running:
+
+           ./configure --help
+
+       NB: This script must be run to register new services after a CVS checkout
+       or subsequent update.
+
+
+Compilation
+-----------
+
+       Once configured, it should be sufficient to run:
+
+           make
+
+       to compile the system.
+
+
+Testing
+-------
+
+       To execute the mlt tools without installation, or to test a new version
+       on a system with an already installed mlt version, you should run:
+
+           . setenv
+
+       NB: This applies to your current shell only and it assumes a bash or 
+       regular bourne shell is in use.
+
+
+Installation
+------------
+
+       The install is triggered by running:
+       
+           make install 
+       
+
+More Information
+----------------
+
+       For more detailed information, please refer to docs/install.txt.
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..72bbfe5
--- /dev/null
+++ b/configure
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+export version=0.3.9
+export soversion=1
+
+show_help()
+{
+       cat << EOF
+Non-autotool config script for MLT.
+
+Help options:
+
+  --help                  - this information
+
+General build options:
+
+  --prefix=directory      - install prefix for path (default: $prefix)
+  --libdir=directory      - lib directory (default: $prefix/lib)
+  --enable-gpl            - Enable GPL components
+  --disable-debug         - Compile without debug support (default: on)
+  --disable-mmx           - Compile without MMX support (default: on)
+  --disable-sse           - Compile without SSE support (default: on)
+  --arch='arch'             - Compile for a specific architecture (default: none)
+  --cpu='cpu'             - Compile for a specific CPU (default: none)
+
+Module disables options:
+
+EOF
+
+       for i in src/modules/*
+       do
+               [ -d $i ] && [ "`basename $i`" != "CVS" ] && echo `basename $i` `[ -f $i/gpl ] && echo [GPL]`
+       done |
+       awk '{ printf( "  --disable-%-14.14s- Disable the %s module %s\n", $1, $1, $2 ); }'
+
+       echo
+       echo "  NOTE: libraries marked [GPL] will not be built unless --enable-gpl is stipulated."
+       echo
+}
+
+build_config()
+{
+       (
+               echo "version=$version"
+               echo "soversion=$soversion"
+               echo "prefix=$prefix"
+               echo "libdir=$libdir"
+               echo "bindir=$prefix/bin"
+               echo "targetos=$targetos"
+
+               [ "$mmx" = "true" ] && 
+               echo "MMX_FLAGS=-DUSE_MMX"
+
+               [ "$sse" = "true" ] && 
+               echo "SSE_FLAGS=-DUSE_SSE"
+
+               [ "$debug" = "true" ] && 
+               echo "DEBUG_FLAGS=-g"
+
+               echo "LARGE_FILE=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE"
+
+               [ "$arch" != "" ] && echo "TARGETARCH=-march=$arch"
+               [ "$cpu" != "" ] && echo "TARGETCPU=-mcpu=$cpu"
+               echo "OPTIMISATIONS=-O2 -pipe -fomit-frame-pointer"
+
+               echo "CFLAGS+=-Wall -fPIC -DPIC \$(TARGETARCH) \$(TARGETCPU) \$(OPTIMISATIONS) \$(MMX_FLAGS) \$(SSE_FLAGS) \$(DEBUG_FLAGS) \$(LARGE_FILE)"
+
+               case $targetos in
+               Darwin)
+               sysctl -a hw | grep "x86_64: 1" > /dev/null && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+               echo "CFLAGS+=-D__DARWIN__ `sdl-config --cflags`"
+               echo "SHFLAGS=-dynamiclib"
+               echo "LDFLAGS+=`sdl-config --libs`"
+               ;;
+               Linux)
+               [ "$(uname -m)" = "x86_64" ] && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+               echo "OPTIMISATIONS+=-ffast-math"
+               echo "CFLAGS+=-pthread"
+               echo "SHFLAGS=-shared"
+               echo "LIBDL=-ldl"
+               echo "RDYNAMIC=-rdynamic"
+               ;;
+               FreeBSD)
+               [ "$(uname -m)" = "x86_64" ] && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64"
+               echo "OPTIMISATIONS+=-ffast-math"
+               echo "CFLAGS+=-pthread"
+               echo "SHFLAGS=-shared"
+               echo "RDYNAMIC=-rdynamic"
+               ;;
+               *)
+               ;;
+               esac
+               echo "LIBSUF=$LIBSUF"
+       ) > config.mak
+
+       echo "#!/bin/sh" > mlt-config
+       (
+               echo export version=$version
+               echo export prefix=$prefix
+               echo export libdir=$libdir
+               echo export bindir=$prefix/bin
+       ) >> mlt-config
+
+       cat < mlt-config-template >> mlt-config
+
+       echo -n > packages.dat
+}
+
+build_pkgconfig()
+{
+       for i in framework valerie miracle
+       do
+               echo prefix="$prefix" > mlt-$i.pc
+               (
+                       echo exec_prefix=$prefix
+                       echo libdir=$libdir
+                       echo includedir=$prefix/include
+                       echo version=$version
+                       echo cflags=`grep ^$i packages.dat | cut -f 2`
+                       echo libs=`grep ^$i packages.dat | cut -f 3`
+               ) >> mlt-$i.pc
+               cat mlt-$i.pc.in >>mlt-$i.pc
+       done
+}
+
+# Debug mode
+set +x
+
+# Define build directory for scripts called
+export build_dir=`dirname $0`
+export prefix=/usr/local
+export libdir=""
+export help=0
+export debug=true
+export mmx=true
+export sse=true
+export gpl=false
+export arch=
+export cpu=
+export targetos=
+
+# Determine OS
+targetos=$(uname -s)
+# Chose appropriate suffix for libraries
+case $targetos in
+       Darwin)
+       LIBSUF=".dylib"
+       ;;
+       Linux|FreeBSD)
+       LIBSUF=".so"
+       ;;
+       *)
+       LIBSUF=".so"
+       ;;
+esac
+export LIBSUF
+
+# Iterate through arguments
+for i in "$@"
+do
+       case $i in
+               --help )                        help=1 ;;
+               --prefix=* )            prefix="${i#--prefix=}" ;;
+               --libdir=* )            libdir="${i#--libdir=}" ;;
+               --disable-debug )       debug=false ;;
+               --disable-mmx )         mmx=false; sse=false ;;
+               --disable-sse )         sse=false ;;
+               --enable-gpl )          gpl=true ;;
+               --arch=* )                      arch="${i#--arch=}" ;;
+               --cpu=* )                       cpu="${i#--cpu=}" ;;
+       esac
+done
+
+# Determine the libdir if it's not specified in the args
+[ "$libdir" = "" ] && libdir=$prefix/lib
+
+# Double check MMX (Darwin, Linux and FreeBSD supported, may end up disabling MMX on other platforms incorrectly)
+if [ "$mmx" = "true" ]
+then
+       case $targetos in
+               Darwin)
+               sysctl -a hw | grep "mmx: 1" > /dev/null || mmx=false
+               ;;
+               Linux)
+               grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false
+               ;;
+               FreeBSD)
+               [ "$(make -V MACHINE_CPU:Mmmx)" ] || mmx=false
+               ;;
+               *)
+               grep mmx /proc/cpuinfo > /dev/null 2>&1 || mmx=false
+               ;;
+       esac
+fi
+
+# Double check SSE (Darwin, Linux and FreeBSD supported, may end up disabling SSE on other platforms incorrectly)
+if [ "$sse" = "true" ]
+then
+       case $targetos in
+               Darwin)
+               sysctl -a hw | grep "sse: 1" > /dev/null || sse=false
+               ;;
+               Linux)
+               grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false
+               ;;
+               FreeBSD)
+               [ "$(make -V MACHINE_CPU:Msse)" ] || sse=false
+               ;;
+               *)
+               grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false
+               ;;
+       esac
+fi
+
+# Show help if requested
+if [ $help = 1 ]
+then
+       show_help
+else
+       # Log the configuration history
+       date >> config.log
+       echo "$0 $@" >> config.log
+
+       build_config
+fi
+
+# Iterate through each of the components
+for i in framework modules inigo valerie miracle humperdink
+do
+       if [ -x src/$i/configure ]
+       then
+               [ $help = 0 ] && echo "Configuring `basename $i`:"
+               olddir=`pwd`
+               cd src/$i
+               ./configure "$@"
+               [ $? != 0 ] && exit 1
+               cd $olddir
+       fi
+done
+
+# Build the pkg-config files
+build_pkgconfig
+
+# Report GPL Usage
+[ $help != 1 ] && 
+( [ "$gpl" = "false" ] && 
+echo "GPL Components are disabled" || 
+echo "GPL License Used" )
diff --git a/demo/README b/demo/README
new file mode 100644 (file)
index 0000000..a9a5a08
--- /dev/null
@@ -0,0 +1,217 @@
+MLT Demo Notes
+
+Before running the demo script, make sure you '. setenv' from the parent
+directory. Also, please create clips clip1.dv, clip2.dv, clip3.dv, clip1.mpeg,
+clip2.mpeg, clip3.mpeg, and music1.ogg. Please make sure clips are at least 500 
+frames duration.
+
+These notes explain the the concepts presented in each demonstration and
+what details to look for.
+
+First, a note on consumers. When you start the script, the main menu asks
+you to choose a consumer. A consumer is like a viewer, but it could also
+write to a stream/file. The "SDL" consumer is the popular Simple DirectMedia
+Layer audio and video output. The "Westley" consumer generates an XML
+representation of the service network. That can be played directly due to the
+westley producer plugin. See docs/westley.txt for more information. The
+"MainConcept DV" consumer refers to the proprietary MLT plugin required to
+use MLT with MainConcept DV, DVCPro, and MPEG codecs. "/dev/dv1394/0" refers
+to a device file for transmitting DV over FireWire using the Linux dv1394 kernel
+module. The "BlueFish444" consumer is another proprietary plugin to use
+the BlueFish444 manufactured SDI video/audio output cards with MLT.
+
+And now the demos...
+
+All clips
+
+       Simply builds a playlist containing each video clip, and you can transport
+       between them using j and k keys.
+
+Filter in/out
+
+       A video filter can be applied to a portion of a producer (clip, playlist,
+       or multitrack). This examples shows the greyscale filter.
+
+Watermark
+
+       A graphic can overlay video in realtime with support for alpha channel.
+       This example uses a PNG file with an alpha channel. Distortion is explicitly
+       enabled here so the otherwise circular graphic is scaled to fill the 
+       compositing region. By default, compositing honours the aspect ratio of the
+       overlay.
+
+My name is...
+
+       Titles are very easy to composite in realtime. The titler uses Pango
+       with the FreeType2 rendering backend. This means it supports high
+       quality scalable font rendering with anti-aliasing, unicode (UTF-8),
+       and Pango markup capabilities. The compsiting here respects the aspect
+       ratio of the rendered title in the first two title pieces but distorts
+       the final one. This demo also shows the motion and scaling capabilities
+       of the compositor in conjunction with honouring aspect. The compositor
+       is doing field-based rendering. So, when displayed non-progressively
+       with SDL, you can see motion artifacts during animation. 
+       
+A composite transition
+
+       The compositor also handles video over video as demonstrated in this
+       usage of the compositor to create a special transition. This demonstration
+       also crossfades the audio during the transition! Progressive rendering
+       is explicitly enabled on the compositor due to the poor results that
+       would otherwise occur due to scaling an interleaved video frame and moving
+       the video in a reverse direction horizontally.
+       
+Fade in and out
+
+       A simple series of transitions betwen 3 clips using dissolves and audio 
+       crossfades. This is easy :-).
+
+Clock in and out
+       
+       Wipe transitions are very easy and highly extensible as they are generated 
+       using a very convenient lookup table based upon the luma of an image.
+       This image can be a 16 bit PGM (grayscale bitmap) or the luma channel of
+       any video producer. A number of high quality wipes can be downloaded from
+       http://mlt.sf.net/. It also performs field rendering.
+       The second wipe demonstrates the ability to control the direction of the
+       wipe as well.
+       
+Obscure
+
+       A popular requirement in news production is to obscure a face, obscenity,
+       or trademarked logo. This demonstrates using a simple rectangular 
+       obscure filter applied to a region of the image. The second example is more
+       advanced and shows using the "region" filter to select the image area and a 
+       property of the region filter to "shape" the region using the alpha channel
+       of another image (circle.png) and another property to "filter" the region
+       using the obscure filter.
+
+Audio Stuff
+
+       A music bed sound track can be mixed with a video. The sound track of the
+       video clip has a "floating" amplitude normalisation filter applied.
+       Typically, audio normalisation applies a constant gain factor across the
+       entire duration of an audio segment from a single source where the 
+       gain factor is automatically determined by anaylsing the maximum "power"
+       or peak levels. However, in news production, a popular requirement is to
+       to dynamically boost the amplitude in soft areas and reduce the amplitude
+       in louder areas. Thus, the gain analysis is performed using a "sliding
+       window" approach. This example also applies a constant gain factor of 
+       0.5 (50%) to the normalised audio of the video clip (to get a nicer
+       mix level).
+       
+Audio and Video Levels
+
+       Audio can be normalised by setting a target amplitude level in decibels.
+       A gamma curve can be applied to the luma channel of video.
+
+Shadowed Title and Watermark
+
+       Two instances of the titler are used to create a shadow effect.
+       The aspect ratio of the watermark in this example is not distorted. Since
+       the original image is a circle with square pixels--a computer-generated
+       image--and ITU BT.601 video is not composed of square samples. Therefore,
+       the compositor normalises the pixel aspect ratio of the overlay to the 
+       destination image, and the circular image remains circular on the analog
+       video output. Finally, a greyscale filter is applied to the watermark
+       while its opacity is set at 30%.
+
+Station Promo into Story?
+
+       Here is fun demo that might show using a still graphic with some music
+       to introduce a show. A luma wipe with an audio crossfade transitions from
+       the show title or station promotional material.
+
+Voiceover 2 clips with title
+
+       A common news production requirement to have a "voiceover" audio track
+       to a clip or even multiple clips as demonstrated here. Likewise, it is 
+       common to place a title caption on the video at the same time! This
+       demo has a little fun with the titler at the sake of practicality :-)
+       The foreground of the title is transparent while the opacity of the 
+       background is reduced to blend with the video. Meanwhile, the compositor
+       stretches the image to fill the bottom slice of the video--not suitable
+       for overscan displays ;-)
+       
+       Also, pay close attention to the mixing levels of the audio tracks.
+       The audio of the video fades out as the voiceover track (just music
+       in this demo) fades in. Then, the voiceover remains mixed with the 
+       ambient audio at a 60% level. Finally, the voiceover fades out smoothly 
+       from the 60% level to nothing.
+
+GJ-TTAvantika title
+
+       This demo requires a special TrueType font called Avantika. If you have the
+       font, register it with fontconfig using the fc-cache utility. This 
+       demonstrates i18n capabilities of the titler and the alignment capabilities 
+       of both the titler and the compositor. The titler centre aligns
+       the two lines of text, and the compositor centre aligns the title 
+       horizontally on the frame. 
+
+Title over graphic
+
+       You can superimpose a title over a graphic over video! Also,
+       you can apply a luma wipe to the compositor!
+
+Slideshow
+
+       This demo requires any number of JPEG images with the extension ".jpg"
+       in a subdirectory named "Scotland."
+
+Bouncy, Bouncy
+
+       The "watermark" filter encapsulates the compositor, and you have full
+       control over the compositor properties. Who says a watermark can not 
+       also be a video?!
+
+Bouncy, Bouncy Ball
+
+       A variation on the above Bouncy, Bouncy demo that applies a shape, or
+       alpha producer, to the the compositing region.
+       
+Breaking News
+
+       This demonstrates layout capabilities of the compositor.
+
+Squeeze Transitions
+
+       This demonstrates a distorting barndoor-like wipe.
+
+
+J Cut
+
+       A J cut is an edit where the audio cuts before the video.
+       It gets its name from the way it looks on a NLE timeline user interface.
+       When the audio cuts over, it does an audio crossfade over the duration of 
+       one frame. This makes the audio cut slightly less abrupt and avoids any 
+       "click" due to mismatched sample levels at the edit point. The video edit
+       is a hard cut.
+
+L Cut
+
+       An L cut is an edit where the video cuts before the audio.
+       It gets its name from the way it looks on a NLE timeline user interface.
+       This demo shows a very quick dissolve over 5 frames for a soft video cut.
+       Like the J Cut demo, an audio crossfade for the duration of one frame makes
+       an audio edit nearly instantaneous while being slightly softened and 
+       avoiding aberrations.
+
+Fade from/to black/silence
+
+       Of course, it is possible using MLT to fade from black on video and silence
+       on audio as well fade to black and silence.
+       
+Push wipe
+
+       A push wipe is a somewhat fancier transition than most standard wipes
+       because it involves motion. The new video clip "pushes" the old video
+       clip off one edge. If you can preview on an analog monitor you will notice
+       how smooth the motion is due to field-based rendering.
+       
+Ticker tape
+
+       A very minimal reverse crawling title neard the bottom of the screen.
+       The goal of the demo is show fluid motion of the field-based rendering of
+       the compositor when viewed on an analog monitor using a DV or BlueFish444
+       consumer. The demo also shows the potientional for using and extending the
+       existing set of services for a full blown news ticker implementation.
diff --git a/demo/circle.png b/demo/circle.png
new file mode 100644 (file)
index 0000000..b597928
Binary files /dev/null and b/demo/circle.png differ
diff --git a/demo/circle.svg b/demo/circle.svg
new file mode 100644 (file)
index 0000000..476ef3c
--- /dev/null
@@ -0,0 +1 @@
+<svg width='300' height='300'><circle cx='150' cy='150' r='150' fill='white'/></svg>
diff --git a/demo/consumers.ini b/demo/consumers.ini
new file mode 100644 (file)
index 0000000..b02695f
--- /dev/null
@@ -0,0 +1,12 @@
+SDL Default                                            sdl
+SDL Half D1                                            sdl:360x288 rescale=nearest resize=1
+SDL High Latency                               sdl buffer=12 rescale=none
+SDL Progressive                                        sdl progressive=1
+Westley to Terminal                            westley
+Westley to File                                        westley:
+MainConcept DV to /dev/dv1394/0        mcdv:/dev/dv1394/0 rescale=nearest buffer=25
+libdv to /dev/dv1394/0                 libdv:/dev/dv1394/0 rescale=nearest buffer=25
+BlueFish444 PAL                                        bluefish:1
+BlueFish444 NTSC                               bluefish:1 standard=NTSC
+BlueFish444 PAL Prog LL                        bluefish:1 progressive=1 buffer=1 frames=4
+BlueFish444 NTSC Prog LL               bluefish:1 standard=NTSC progressive=1 buffer=1 frames=4
diff --git a/demo/demo b/demo/demo
new file mode 100755 (executable)
index 0000000..98777ca
--- /dev/null
+++ b/demo/demo
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+function show_consumers( )
+{
+       awk -F '\t' '{ printf( "%d. %s\n", ++ i, $1 ); }' < consumers.ini
+}
+
+function get_consumer( )
+{
+       option=$1
+       [ "$option" != "" ] && [ $option -gt 0 ] && sed 's/\t\+/\t/g' < consumers.ini | cut -f 2 | head -n $option | tail -n -1
+}
+
+function show_menu( )
+{
+       sed 's/\t\+/\t/g' < demo.ini |
+       awk -F '\t' '{ printf( "%2d. %-30.30s", ++ i, $2 ); if ( i % 2 == 0 ) printf( "\n" ); } END { if ( i % 2 == 1 ) printf( "\n" ); }'
+}
+
+function check_dependencies( )
+{
+       option=$1
+       if [ $option -gt 0 ]
+       then
+               deps=`sed 's/\t\+/\t/g' < demo.ini | cut -f 3 | head -n $option | tail -n -1`
+               if [ "$deps" != "" ]
+               then
+                       echo "$deps" | 
+                       tr ',' '\n' | 
+                       while read dep 
+                       do
+                               ls $dep > /dev/null 2>&1
+                               val=$?
+                               [ $val != 0 ] && echo Failed to find $dep >&2 && echo $val
+                       done
+               fi
+               echo 0
+       fi
+}
+
+function get_demo( )
+{
+       option=$1
+       if [ $option -gt 0 ]
+       then
+               cut -f 1 demo.ini | head -n $option | tail -n -1
+       fi
+}
+
+while [ 1 ]
+do
+
+       echo Select Consumer
+       echo
+
+       show_consumers
+
+       echo
+       echo 0. Exit
+       echo
+       echo -n "Option: "
+       read option
+       echo
+
+       [ "$option" == "0" ] && break
+
+       export MLT_CONSUMER=`get_consumer $option`
+
+       while [ "$option" != "0" -a "$MLT_CONSUMER" != "" ]
+       do
+               echo Choose Demo
+               echo
+       
+               show_menu
+       
+               echo
+               echo -n "Option: "
+               read option
+               echo
+
+               [ "$option" == "" ] && break
+       
+               demo=`get_demo $option`
+               usable=`check_dependencies $option`
+       
+               if [ "$usable" = "0" -a "$demo" != "" ]
+               then
+                       if [ "$MLT_CONSUMER" == "westley:" ]
+                       then    export WESTLEY_CONSUMER="westley:$demo.westley"
+                                       bash $demo -consumer $WESTLEY_CONSUMER
+                                       inigo +$demo.txt out=100 $demo.westley $demo.westley -filter watermark:watermark1.png composite.fill=1 composite.geometry=85%,5%:10%x10%
+                       elif [ "$MLT_CONSUMER" == "westley" ]
+                       then    bash $demo -consumer $MLT_CONSUMER | less
+                       else    bash $demo -consumer $MLT_CONSUMER
+                       fi
+               elif [ "$usable" != "" ]
+               then
+                       echo 
+                       echo Unable to locate suitable files for the demo - please provide them.
+                       read pause
+               fi
+       
+               stty sane
+       done
+
+done
diff --git a/demo/demo.ini b/demo/demo.ini
new file mode 100644 (file)
index 0000000..b01b049
--- /dev/null
@@ -0,0 +1,28 @@
+mlt_all                                                All clips                                                       clip*
+mlt_effect_in_middle           Filter in/out                                           clip1.mpeg
+mlt_watermark                          Watermark                                                       clip2.dv,watermark1.png
+mlt_my_name_is                         My name is...                                           clip3.dv
+mlt_composite_transition       A composite transition                          clip1.dv,clip2.mpeg
+mlt_fade_in_and_out                    Fade in and out                                         clip1.dv,clip2.mpeg,clip3.dv
+mlt_clock_in_and_out           Clock in and out                                        clip2.dv,clip1.dv,clip3.mpeg
+mlt_obscure                                    Obscure                                                         clip2.mpeg,circle.png
+mlt_audio_stuff                                Audio Stuff                                                     clip*.dv,music1.ogg
+mlt_levels                                     Audio and Video Levels                          clip*.dv
+mlt_titleshadow_watermark      Shadowed Title and Watermark            clip3.dv
+mlt_intro                                      Station Promo into Story?                       watermark1.png,clip3.mpeg,music1.ogg
+mlt_voiceover                          Voiceover 2 clips with title            clip1.dv,clip2.mpeg,music1.ogg
+mlt_avantika_title                     GJ-TTAvantika title                                     pango.westley
+mlt_title_over_gfx                     Title over graphic                                      watermark1.png,clip1.dv
+mlt_slideshow                          Slideshow                                                       Scotland
+mlt_bouncy                                     Bouncy, Bouncy                                          clip1.dv,clip3.dv
+mlt_bouncy_ball                                Bouncy, Bouncy Ball                                     clip1.mpeg,clip3.mpeg,circle.png
+mlt_news                                       Breaking News                                           clip1.dv,clip2.dv
+mlt_squeeze                                    Squeeze Transitions                                     clip1.dv,clip2.dv,clip3.dv
+mlt_squeeze_box                                Squeeze Box                                                     clip1.dv,clip2.dv,clip3.dv
+mlt_jcut                                       J Cut                                                           clip1.dv,clip2.dv
+mlt_lcut                                       L Cut                                                           clip1.dv,clip2.dv
+mlt_fade_black                         Fade from/to black/silence                      clip3.mpeg
+mlt_push                                       Push wipe                                                       clip1.mpeg, clip2.mpeg
+mlt_ticker                                     Ticker tape                                                     clip1.dv
+mlt_attributes                         Attributes                                                      clip1.dv
+mlt_slideshow_black                    Composite slideshow                                     Scotland
diff --git a/demo/demo.kino b/demo/demo.kino
new file mode 100644 (file)
index 0000000..99e8767
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<smil xmlns:smil2="http://www.w3.org/2001/SMIL20/Language">
+  <seq title="MLT" copyright="2004" abstract="foo man choo">
+    <video src="clip2.dv" clipBegin="171" clipEnd="450"/>
+  </seq>
+  <seq title="Demo">
+    <video src="clip2.dv" clipBegin="0" clipEnd="170"/>
+  </seq>
+  <seq>
+    <video src="clip1.dv" clipBegin="0" clipEnd="159"/>
+    <video src="clip3.dv" clipBegin="0" clipEnd="106"/>
+  </seq>
+</smil>
diff --git a/demo/entity.westley b/demo/entity.westley
new file mode 100644 (file)
index 0000000..0f6a6b9
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!DOCTYPE westley SYSTEM "../src/modules/westley/westley.dtd" [
+       <!ENTITY name "Westley">
+]>
+<westley>
+  <producer id="producer0">
+    <property name="mlt_service">pango</property>
+       <property name="text">Hello &name;,
+My name is Inigo Montoya.</property>
+  </producer>
+</westley>
diff --git a/demo/luma1.pgm b/demo/luma1.pgm
new file mode 100644 (file)
index 0000000..ac689e5
Binary files /dev/null and b/demo/luma1.pgm differ
diff --git a/demo/mlt_all b/demo/mlt_all
new file mode 100644 (file)
index 0000000..d563da8
--- /dev/null
@@ -0,0 +1,3 @@
+inigo \
+clip* \
+$*
diff --git a/demo/mlt_attributes b/demo/mlt_attributes
new file mode 100644 (file)
index 0000000..e4fffb5
--- /dev/null
@@ -0,0 +1,7 @@
+inigo clip1.dv \
+meta.attr.location=1 meta.attr.location.markup="Location" \
+meta.attr.exclusive=1 meta.attr.exclusive.markup="Exclusive" \
+meta.attr.special=1 meta.attr.special.markup="Special" \
+meta.attr.super=1 meta.attr.super.0="Line 1" meta.attr.super.1="Line 2" \
+-filter data_show \
+$*
diff --git a/demo/mlt_audio_stuff b/demo/mlt_audio_stuff
new file mode 100644 (file)
index 0000000..c0074c1
--- /dev/null
@@ -0,0 +1,6 @@
+inigo \
+clip*.dv \
+-track music1.ogg \
+-filter volume:0.5 normalise= track=0 \
+-transition mix out=9999 a_track=0 b_track=1 \
+$*
diff --git a/demo/mlt_avantika_title b/demo/mlt_avantika_title
new file mode 100644 (file)
index 0000000..89d0242
--- /dev/null
@@ -0,0 +1,3 @@
+inigo \
+pango.westley \
+$*
diff --git a/demo/mlt_bouncy b/demo/mlt_bouncy
new file mode 100644 (file)
index 0000000..5315ec9
--- /dev/null
@@ -0,0 +1,10 @@
+inigo \
+clip3.dv \
+-filter \
+watermark:clip1.dv \
+composite.start=10%,10%:20%x20% \
+composite.key[33]=30%,70%:25%x25% \
+composite.key[66]=70%,30%:15%x15% \
+composite.end=70%,70%:20%x20% \
+composite.out=100 \
+$*
diff --git a/demo/mlt_bouncy_ball b/demo/mlt_bouncy_ball
new file mode 100644 (file)
index 0000000..1aedb5c
--- /dev/null
@@ -0,0 +1,14 @@
+inigo \
+clip3.dv \
+-track \
+clip1.dv \
+-transition \
+region:circle \
+composite.geometry="10%,10%:20%x20%;33=30%,70%:25%x25%;66=70%,30%:15%x15%;-1=70%,70%:20%x20%" \
+composite.out=100 \
+composite.softness=0.1 \
+a_track=0 \
+b_track=1 \
+in=0 \
+out=5000 \
+$*
diff --git a/demo/mlt_clock_in_and_out b/demo/mlt_clock_in_and_out
new file mode 100644 (file)
index 0000000..f19dd5e
--- /dev/null
@@ -0,0 +1,7 @@
+inigo \
+clip2.dv in=100 out=174 -blank 99 clip3.dv in=100 \
+-track \
+-blank 49 clip3.mpeg in=100 out=249 \
+-transition luma:luma1.pgm softness=0.5 in=50 out=74 a_track=0 b_track=1 \
+-transition luma:luma1.pgm softness=0.2 in=175 out=199 a_track=0 b_track=1 reverse=1 \
+$*
diff --git a/demo/mlt_composite_transition b/demo/mlt_composite_transition
new file mode 100644 (file)
index 0000000..cc38ec8
--- /dev/null
@@ -0,0 +1,7 @@
+inigo \
+clip1.dv out=74 \
+-track \
+-blank 49 clip2.mpeg \
+-transition composite:57%,10%:33%x33% end=0%,0%:100%x100% progressive=1 distort=true in=50 out=74 a_track=0 b_track=1 \
+-transition mix:-1 in=50 out=74 a_track=0 b_track=1 \
+$*
diff --git a/demo/mlt_effect_in_middle b/demo/mlt_effect_in_middle
new file mode 100644 (file)
index 0000000..0fc3974
--- /dev/null
@@ -0,0 +1,4 @@
+inigo \
+clip1.mpeg in=100 out=500 \
+-filter greyscale in=100 out=199 \
+$*
diff --git a/demo/mlt_fade_black b/demo/mlt_fade_black
new file mode 100644 (file)
index 0000000..376fe86
--- /dev/null
@@ -0,0 +1,9 @@
+inigo \
+colour:black out=199 \
+-track \
+clip3.mpeg in=100 out=299 \
+-transition luma in=0 out=49 a_track=0 b_track=1 \
+-transition luma in=150 out=199 a_track=0 b_track=1 reverse=1 \
+-filter volume in=0 out=49 track=1 gain=0 end=1.0 \
+-filter volume in=150 out=199 track=1 gain=1.0 end=0 \
+$*
diff --git a/demo/mlt_fade_in_and_out b/demo/mlt_fade_in_and_out
new file mode 100644 (file)
index 0000000..fbe1fa3
--- /dev/null
@@ -0,0 +1,9 @@
+inigo \
+clip1.dv out=74 -blank 99 clip3.dv in=25 \
+-track \
+-blank 49 clip2.mpeg out=149 \
+-transition luma in=50 out=74 a_track=0 b_track=1 \
+-transition luma in=175 out=199 a_track=0 b_track=1 reverse=1 \
+-transition mix:-1 in=50 out=74 a_track=0 b_track=1 \
+-transition mix:-1 in=175 out=199 a_track=0 b_track=1 reverse=1 \
+$*
diff --git a/demo/mlt_intro b/demo/mlt_intro
new file mode 100644 (file)
index 0000000..4b00848
--- /dev/null
@@ -0,0 +1,9 @@
+inigo \
+music1.ogg in=100 out=224 \
+-track \
+watermark1.png out=124 \
+clip3.mpeg \
+-mix 25 \
+-mixer luma resource=luma1.pgm softness=0.2 \
+-transition mix:-1 in=100 out=124 \
+$*
diff --git a/demo/mlt_jcut b/demo/mlt_jcut
new file mode 100644 (file)
index 0000000..a5d844e
--- /dev/null
@@ -0,0 +1,10 @@
+inigo \
+-blank 49 \
+clip2.dv in=100 \
+-track \
+clip1.dv out=99 \
+-transition \
+mix start=0 end=1 in=49 out=50 a_track=1 b_track=0 \
+-transition \
+mix:1 in=51 out=99 a_track=1 b_track=0 \
+$*
diff --git a/demo/mlt_lcut b/demo/mlt_lcut
new file mode 100644 (file)
index 0000000..45f81dd
--- /dev/null
@@ -0,0 +1,12 @@
+inigo \
+clip1.dv out=100 \
+-track \
+-blank 49 \
+clip2.dv in=100 \
+-transition \
+luma in=50 out=55 a_track=0 b_track=1 \
+-transition \
+mix:1 in=50 out=98 a_track=1 b_track=0 \
+-transition \
+mix start=1 end=0 in=99 out=100 a_track=1 b_track=0 \
+$*
diff --git a/demo/mlt_levels b/demo/mlt_levels
new file mode 100644 (file)
index 0000000..16bf9f5
--- /dev/null
@@ -0,0 +1,5 @@
+inigo \
+*.dv \
+-filter gamma:1.5 \
+-filter volume normalise=-20db \
+$*
diff --git a/demo/mlt_my_name_is b/demo/mlt_my_name_is
new file mode 100644 (file)
index 0000000..bd6bc3f
--- /dev/null
@@ -0,0 +1,10 @@
+inigo \
+clip3.dv \
+-track \
+"+My name is Inigo Montoya.txt" out=99 -blank 49 "+Prepare to die!.txt" out=99 \
+-track \
+-blank 74 "+You killed my father.txt" out=74 \
+-transition composite:50%,20%:5%x4% end=10%,20%:80%x12% distort=1 halign=centre valign=centre in=0 out=99 a_track=0 b_track=1 \
+-transition composite:0%,70%:100%x10% end=100%,70%:100%x10% in=75 out=149 a_track=0 b_track=2 \
+-transition composite:25%,25%:50%x50%! in=150 out=249 a_track=0 b_track=1 \
+$*
diff --git a/demo/mlt_news b/demo/mlt_news
new file mode 100644 (file)
index 0000000..3399f1f
--- /dev/null
@@ -0,0 +1,18 @@
+inigo \
+colour:black out=199 \
+-track \
+clip1.dv in=0 out=0 -repeat 99 clip1.dv \
+-track \
+clip2.dv out=199 \
+-track \
+pango: text=" Breaking News                 
+ MLT Rocks India" bgcolour=0xff000080 out=149 \
+pango: text=" Breaking News                 
+ MLT Rocks the World" bgcolour=0xff000080 out=349 \
+-transition mix:0.5 always_active=1 a_track=0 b_track=2 \
+-transition composite geometry=50%,15%:37.5%x40% a_track=0 b_track=1 in=0 out=174 \
+-transition composite geometry=10%,15%:37.5%x40% a_track=0 b_track=2 in=0 out=199 \
+-transition composite geometry="50%,15%:37.5%x40%;-1=0%,0%:100%x100%" a_track=0 b_track=1 in=175 out=199 distort=1 \
+-transition composite geometry=10%,65%:90%x20% a_track=0 b_track=3 in=0 out=199 \
+-transition composite geometry=10%,65%:90%x20% a_track=1 b_track=3 in=200 out=499 \
+$*
diff --git a/demo/mlt_obscure b/demo/mlt_obscure
new file mode 100644 (file)
index 0000000..0c0f838
--- /dev/null
@@ -0,0 +1,5 @@
+inigo \
+clip2.mpeg \
+-filter obscure:25%,25%:25%x25%:10x10 in=0 out=68 \
+-filter region:circle.png filter=obscure composite.start=55%,25%:12%x50% in=68 out=200 \
+$*
diff --git a/demo/mlt_push b/demo/mlt_push
new file mode 100644 (file)
index 0000000..bcbc8bf
--- /dev/null
@@ -0,0 +1,18 @@
+inigo \
+-blank 49 colour:black out=25 -blank 999 \
+-track \
+clip3.dv in=200 out=275 \
+-track \
+-blank 49 \
+clip2.dv in=200 \
+-transition \
+composite in=50 out=75 a_track=0 b_track=1 \
+start=0,0:100%x100%:100 \
+end=100%,0:100%x100%:100 \
+-transition \
+composite in=50 out=75 a_track=0 b_track=2 \
+start=-100%,0:100%x100%:100 \
+end=0,0:100%x100%:100 \
+-transition \
+mix:-1 in=50 out=75 a_track=1 b_track=2 \
+$*
diff --git a/demo/mlt_slideshow b/demo/mlt_slideshow
new file mode 100644 (file)
index 0000000..efe0d06
--- /dev/null
@@ -0,0 +1,4 @@
+inigo \
+Scotland/.all.jpg ttl=75 \
+-filter luma:luma1.pgm luma.softness=0.1 luma.invert=0 \
+$*
diff --git a/demo/mlt_slideshow_black b/demo/mlt_slideshow_black
new file mode 100644 (file)
index 0000000..ddbf595
--- /dev/null
@@ -0,0 +1,3 @@
+inigo Scotland/.all.jpg ttl=100 \
+-filter watermark:colour:black reverse=1 composite.geometry="15%,15%:10%,10%;0.1625=0,0:100%x100%;-.1625=;-1=70%,70%:10%x10%" composite.mirror_off=1 composite.cycle=100 composite.fill=1 composite.valign=c composite.halign=c \
+$*
diff --git a/demo/mlt_squeeze b/demo/mlt_squeeze
new file mode 100644 (file)
index 0000000..a56780c
--- /dev/null
@@ -0,0 +1,9 @@
+inigo \
+clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \
+-track \
+-blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \
+-group progressive=1 distort=1 \
+-transition composite geometry="0%,0%:100%x100%;25=50%,0%:5%x100%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=100 out=149 \
+-transition composite geometry="0%,0%:100%x100%;25=0%,50%:100%x5%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=250 out=299 \
+-transition composite geometry="0%,0%:100%x100%;25=100%,0%:5%x100%;-1=0%,0%:100%x100%" a_track=1 b_track=0 in=400 out=449 \
+$*
diff --git a/demo/mlt_squeeze_box b/demo/mlt_squeeze_box
new file mode 100644 (file)
index 0000000..dfa0ced
--- /dev/null
@@ -0,0 +1,9 @@
+inigo \
+clip1.dv out=124 clip2.dv out=149 clip3.dv in=75 out=224 clip1.dv \
+-track \
+-blank 99 colour:black out=49 -blank 99 colour:black out=49 -blank 99 colour:black out=49 \
+-group progressive=1 \
+-transition composite:0%,0%:100%x100% key[25]=50%,0%:5%x100% end=0%,0%:100%x100% a_track=1 b_track=0 in=100 out=149 \
+-transition composite:0%,0%:100%x100% key[25]=0%,50%:100%x5% end=0%,0%:100%x100% a_track=1 b_track=0 in=250 out=299 \
+-transition composite:0%,0%:100%x100% key[25]=100%,0%:5%x100% end=0%,0%:100%x100% a_track=1 b_track=0 in=400 out=449 \
+$*
diff --git a/demo/mlt_ticker b/demo/mlt_ticker
new file mode 100644 (file)
index 0000000..f6a8329
--- /dev/null
@@ -0,0 +1,15 @@
+inigo \
+clip1.dv out=299 \
+-track \
+colour:black out=299 \
+-track \
+"+The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog..txt" \
+out=299 \
+-transition \
+composite a_track=0 b_track=1 out=299 distort=1 \
+start=0,70%:100%x64:100 \
+-transition \
+composite a_track=0 b_track=2 out=299 titles=1 \
+start=100%,70%:999%x20% \
+end=-299%,70%:999%x20% \
+$*
diff --git a/demo/mlt_title_over_gfx b/demo/mlt_title_over_gfx
new file mode 100644 (file)
index 0000000..e9b7f66
--- /dev/null
@@ -0,0 +1,25 @@
+inigo \
+       watermark1.png out=9999 \
+-track \
+       "+title over gfx.txt" fgcolour=0x000000ff \
+-track \
+       clip1.dv \
+-transition \
+       composite start=30%,20%:40%x60% \
+               in=50 \
+               out=199 \
+               a_track=0 \
+               b_track=1 \
+               distort=1 \
+-transition \
+       composite:0%,75%:100%x20%:0 \
+               in=50 \
+               out=199 \
+               a_track=2 \
+               b_track=0 \
+               key[24]=0%,75%:100%x20%:100 \
+               key[-25]=0%,75%:100%x20%:100 \
+               luma=luma1.pgm \
+               end=0%,75%:100%x20%:0 \
+               distort=1 \
+$*
diff --git a/demo/mlt_titleshadow_watermark b/demo/mlt_titleshadow_watermark
new file mode 100644 (file)
index 0000000..79ef8ae
--- /dev/null
@@ -0,0 +1,10 @@
+inigo \
+"+hello~world.txt" align=1 out=1000 \
+-track "+hello~world.txt" align=1 out=1000 fgcolour=0x000000ff \
+-track watermark1.png out=1000 \
+-track clip3.dv \
+-filter greyscale track=2 \
+-transition composite:21%,11%:100%x100%:50 end=61%,41%:100%x100% out=99 a_track=3 b_track=1 \
+-transition composite:20%,10%:100%x100% end=60%,40%:100%x100% out=99 a_track=3 b_track=0 \
+-transition composite:85%,80%:10%x10%:30 out=1000 a_track=3 b_track=2 \
+$*
diff --git a/demo/mlt_voiceover b/demo/mlt_voiceover
new file mode 100644 (file)
index 0000000..338f4d8
--- /dev/null
@@ -0,0 +1,36 @@
+inigo \
+"+voice over demo.txt" \
+               font="Sans Bold 72" \
+               fgcolour=0x00000000 \
+               bgcolour=0xff9933aa \
+               pad=10 \
+-track music1.ogg \
+-track clip1.dv out=149 clip2.mpeg \
+-transition \
+       mix:0.0 \
+       end=0.6 \
+       in=75 \
+       out=99 \
+       a_track=2 \
+       b_track=1 \
+-transition \
+       mix:0.6 \
+       in=100 \
+       out=299 \
+       a_track=2 \
+       b_track=1 \
+-transition \
+       mix:0.6 \
+       end=0.0 \
+       in=300 \
+       out=324 \
+       a_track=2 \
+       b_track=1 \
+-transition \
+       composite:0%,80%:100%x20% \
+       distort=1 \
+       in=100 \
+       out=299 \
+       a_track=2 \
+       b_track=0 \
+$*
diff --git a/demo/mlt_watermark b/demo/mlt_watermark
new file mode 100644 (file)
index 0000000..b832d5b
--- /dev/null
@@ -0,0 +1,6 @@
+inigo \
+clip2.dv out=1000 \
+-track \
+watermark1.png out=1000 \
+-transition composite fill=1 in=0 out=1000 a_track=0 b_track=1 geometry=85%,5%:10%x10% \
+$*
diff --git a/demo/new.westley b/demo/new.westley
new file mode 100644 (file)
index 0000000..c623b3e
--- /dev/null
@@ -0,0 +1,51 @@
+<playlist>
+  <entry>
+    <multitrack>
+         <playlist>
+        <producer id="foo" in="100" out="149">
+             <property name="resource">clip2.mpeg</property>
+               </producer>
+               <blank length="25"/>
+        <entry producer="foo" in="10" out="59"/>
+         </playlist>
+         <playlist>
+               <blank length="25"/>
+               <producer id="bar" in="100" out="149">
+                 <property name="resource">clip3.mpeg</property>
+               </producer>
+               <entry out="99" producer="bar"/>
+         </playlist>
+    </multitrack>
+    <filter track="0">
+      <property name="mlt_service">greyscale</property>
+    </filter>
+    <transition in="25" out="49" a_track="0" b_track="1">
+      <property name="mlt_service">luma</property>
+    </transition>
+    <transition in="75" out="99" a_track="0" b_track="1" mlt_service="luma">
+         <property name="reverse" value="1"/>
+    </transition>
+  </entry>
+
+  <entry>
+    <multitrack>
+         <playlist>
+        <entry producer="foo" in="100" out="149"/>
+               <blank length="25"/>
+        <entry producer="foo" in="10" out="59"/>
+         </playlist>
+         <playlist>
+               <blank length="25"/>
+               <entry producer="bar" in="100" out="149"/>
+               <entry out="99" producer="bar"/>
+         </playlist>
+    </multitrack>
+    <transition in="25" out="49" a_track="0" b_track="1">
+      <property name="mlt_service">luma</property>
+    </transition>
+    <transition in="75" out="99" a_track="0" b_track="1" mlt_service="luma">
+      <property name="reverse">1</property>
+    </transition>
+  </entry>
+
+</playlist>
diff --git a/demo/pango.westley b/demo/pango.westley
new file mode 100644 (file)
index 0000000..4684d48
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<westley>
+  <producer id="video">
+    <property name="resource">clip1.dv</property>
+  </producer>
+  <producer id="title">
+    <property name="mlt_service">pango</property>
+    <property name="resource">+.txt</property>
+    <property name="font">GJ-TTAvantika 36</property>
+    <property name="align">1</property>
+    <property name="fgcolour">0xffffddff</property>
+    <property name="bgcolour">0x8c101080</property>
+    <property name="pad">8</property>
+    <property name="text"><![CDATA[ʾúlÉäºÉÒ qàö»ÉÉ<
+HíÉà~ÉÒ+àeôÒ`ò­÷ +y«ÉKɠ§ÉÉWð~É]]></property>
+  </producer>
+  <tractor>
+    <multitrack>
+      <track producer="title"/>
+      <track producer="video"/>
+    </multitrack>
+    <transition in="0" out="150">
+      <property name="mlt_service">composite</property>
+      <property name="a_track">1</property>
+      <property name="b_track">0</property>
+      <property name="start">-70%,65%:100%x35%:0</property>
+      <property name="key[25]">0,65%:100%x35%:100</property>
+      <property name="key[125]">0,65%:100%x35%:100</property>
+      <property name="end">0,65%:100%x35%:0</property>
+      <property name="halign">centre</property>
+      <property name="valign">centre</property>
+    </transition>
+  </tractor>
+</westley>
diff --git a/demo/svg.westley b/demo/svg.westley
new file mode 100644 (file)
index 0000000..a41ff4a
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN"   "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+       <!ENTITY st0 "fill-rule:evenodd;clip-rule:evenodd;fill:url(#aigrd1);stroke:none;">
+       <!ENTITY st1 "stroke-width:0.8755;">
+       <!ENTITY st2 "fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;stroke:none;">
+       <!ENTITY st3 "fill:none;stroke:none;">
+       <!ENTITY st4 "fill-rule:evenodd;clip-rule:evenodd;fill:url(#aigrd2);stroke:none;">
+       <!ENTITY st5 "fill-rule:evenodd;clip-rule:evenodd;stroke:none;">
+       <!ENTITY st6 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+]>
+<westley>
+<playlist>
+  <entry>
+    <producer out="99" length="99">
+      <property name="mlt_service">pixbuf</property>
+      <property name="resource">
+         
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In  -->
+<svg  width="24pt" height="24pt" viewBox="0 0 24 24" xml:space="preserve">
+       <g id="Layer_x0020_2" style="&st6;">
+               <path style="&st1;" d="M1,12.1c0,6.1,5,11.1,11.1,11.1c6.1,0,11.1-5,11.1-11.1c0-6.1-5-11.1-11.1-11.1C5.9,1.1,1,6,1,12.1z"/>
+               <radialGradient id="aigrd1" cx="8.7681" cy="8.0513" r="13.7645" fx="8.7681" fy="8.0513" gradientUnits="userSpaceOnUse">
+                       <stop  offset="0" style="stop-color:#FED182"/>
+                       <stop  offset="0.2809" style="stop-color:#FFB310"/>
+                       <stop  offset="1" style="stop-color:#996E04"/>
+               </radialGradient>
+               <path style="&st0;" d="M21.3,12c0,5.2-4.2,9.4-9.4,9.4c-5.2,0-9.4-4.2-9.4-9.4c0-5.2,4.2-9.4,9.4-9.4c5.2,0,9.4,4.2,9.4,9.4z"/>
+       </g>
+       <g id="Layer_x0020_3" style="&st6;">
+               <path style="&st5;" d="M16.4,7.2c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6c-0.9-0.9-2.5-2.5-2.5-2.5c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+                       c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+               <path style="&st2;" d="M16.6,7.4c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6c-0.9-0.9-2.5-2.5-2.5-2.5c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+                       c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+               <radialGradient id="aigrd2" cx="8.6733" cy="8.6187" r="11.0872" fx="8.6733" fy="8.6187" gradientUnits="userSpaceOnUse">
+                       <stop  offset="0" style="stop-color:#FFFFFF"/>
+                       <stop  offset="1" style="stop-color:#F0E1BD"/>
+               </radialGradient>
+               <path style="&st4;" d="M16.5,7.3c-0.5,0.1-0.9,0.3-1.2,0.7c0,0-3,4.2-4.1,5.6C10.3,12.6,8.6,11,8.6,11c-0.3-0.3-0.8-0.5-1.3-0.5c-0.5,0-0.9,0.2-1.3,0.5c-0.7,0.7-0.7,1.8,0,2.5l3.9,3.9c0.4,0.4,0.9,0.5,1.4,0.5c0.5,0,1-0.3,1.3-0.7l5.4-7.1
+                       c0.6-0.8,0.5-1.9-0.3-2.5c-0.4-0.3-0.8-0.4-1.3-0.4z"/>
+       </g>
+       <g id="Layer_x0020_4" style="&st6;">
+               <path style="&st3;" d="M24,24H0V0h24v24z"/>
+       </g>
+</svg>
+
+      </property>
+    </producer>
+  </entry>
+</playlist>
+</westley>
diff --git a/demo/watermark1.png b/demo/watermark1.png
new file mode 100644 (file)
index 0000000..1ec3e57
Binary files /dev/null and b/demo/watermark1.png differ
diff --git a/docs/TODO b/docs/TODO
new file mode 100644 (file)
index 0000000..ec188f5
--- /dev/null
+++ b/docs/TODO
@@ -0,0 +1 @@
+See http://www.mltframework.org/twiki/bin/view/MLT/ToDo
diff --git a/docs/dvcp.txt b/docs/dvcp.txt
new file mode 100644 (file)
index 0000000..18da20f
--- /dev/null
@@ -0,0 +1,339 @@
+Miracle Control Protocol (DVCP) Reference Documentation
+
+Copyright (C) 2004 Ushodaya Enterprised Limited
+Author: Dan Dennedy <dan@dennedy.org>
+Last Revision: 2004-03-20
+
+
+General Format
+--------------
+       DVCP is an ASCII-based request/response TCP protocol much like FTP and
+       inspired by the SGI MVCP (Multiport Video Computer Protocol). Each
+       command is three to eight characters long followed by zero or more
+       arguments. Every item (command or argument) in the request is delimited
+       by a space and terminated with a new line. Arguments that contain spaces
+       must be surrounded by double quotation marks. The new line must contain
+       a line feed optionally preceeded by a carriage return. There are no
+       request header lines or body.
+
+
+Response Codes
+--------------
+       Responses consist of a numeric result code followed by a space folowed
+       by a brief textual description of the result. No quoting is applied to
+       descriptions regardless if it contains spaces. The result codes are
+       grouped by the hundreds into general categories of responses. Anything
+       in the 200-299 range is considered a success and anything 300 and above
+       is an error or exception. Most responses do not contain a body except
+       some of the success results that report information and sometimes the
+       500 Server Error returns specific information. 
+
+       A 200 result code contains no body.
+       A 201 result code contains one or more lines in the body, and an empty
+       line terminates the response.
+       A 202 result code contains only a single response line in the body.
+
+       Errors in the 400 range indicate a normally handled error where the
+       command could not perform its action due to protocol syntax errors or
+       problems with validation of one or more of the arguments. This usually
+       indicates that the client is responsible for performing an illegal
+       request.
+       
+       Errors in the 500 range indicate a server error or exception.
+       
+       The following is a list of response codes and their descriptions:
+       200 OK
+       201 OK
+       202 OK
+       400 Unknown command
+       401 Operation timed out
+       402 Argument missing
+       403 Unit not found
+       404 Failed to locate or open clip
+       405 Argument value out of range
+       500 Server Error
+
+
+Establishing a Connection
+-------------------------
+       One can connect to the miracle server using telnet or a custom client,
+       preferrably one developed using the valerie client API. The default port
+       is 5250. Connections can be broken at will or use the BYE command to
+       request the server to terminate the connection.
+
+
+General Command Information
+---------------------------
+
+       All commands are case insensitive. Arguments may or may not be case
+       sensitive. There are two categories of commands: global and unit. Global
+       commands operate at the server level. Unit commands address a specific
+       unit. miracle is a multi-unit system. Units are named as U? where ?
+       is the unit number, for example, U0. As units are added to the server,
+       the unit number increases; the first unit is U0.
+       
+       The command HELP lists all commands known to the server with a brief
+       description of their purpose and arguments. Most commands take zero or
+       one argument outside of the unit name. Sometimes an argument is
+       optional, and an optional argument always follows required arguments.
+       All units command required a unit name argument.
+       
+       {} = required argument
+       [] = optional argument
+       () = one of a set of pre-defined values
+       
+
+Global Commands
+---------------
+
+HELP
+       List the commands and their brief description.
+
+BYE
+       Close the connection.
+       
+SHUTDOWN
+       Shutdown the server and all client connections.
+
+SET {key=value}
+       Set a global server configuration property.
+       Currently, the only planned key is "root" to set the base directory
+       path for the CLS and LOAD commands. The default root value is /.
+
+GET {key}
+       Get the current value of a configuration property.
+       The value is returned by itself in the body of the response.
+
+CLS {path}
+       List the clips and subdirectories at {path} on the server.
+       Only subdirectories, non-hidden regular files, symbolic links, and NFS
+       shares are supported.
+       The response body contains one line per item.
+       The name of the subdirectory/file is always surrounded by double
+       quotation marks in case it contains spaces.
+       Subdirectories are listed before files and have a trailing / in their
+       name.
+       File entries have a size value in bytes in the second column position.
+
+RUN {file}
+       Process the commands in a file located on the server.
+       Commands are executed one after the other with no delay until the end
+       of file is reached or a command returns a response code not in the 200
+       range.
+       The response body contains each command sent along with its arguments,
+       followed by each command's response status code and response body.
+
+
+STATUS
+       Responds with the output of USTA for each unit and accepts no further
+       input. Each time the state of the unit changes, a new row is returned by
+       the server containing the state of the unit. 
+
+Unit Management
+
+       The following global commands manage the DV units within the server.
+       Currently there is a maximum of four units, and units can not be
+       removed. Each unit may be in an online or offline state. Offline units
+       can not be used, and any unit commands issued against an offline unit
+       results in a 403 response. 
+       
+NLS
+       * NOT IMPLEMENTED IN MIRACLE YET *
+
+
+UADD mlt-consumer[:argument]
+       Add a unit based upon the mlt-consumer id and optional constructor
+       argument.
+       If the consumer is not found, then it still added but in an
+       offline manner. Later, by adding the device to the bus, the unit will
+       automatically become online.
+       The response body contains the name of the new unit: U0, U1, U2, or U3.
+       Channel is an optional setting. 
+
+ULS
+       List the units.
+       The response body contains a space-delimited row for each unit in the
+       server containing the following columns:
+       - unit name (one of U0, U1, U2, or U3)
+       - mlt-consumer[:argument] from uadd
+       - 1394 node GUID (defunt - always 0 with miracle for now)
+       - online flag (1 = online, 0 = offline)
+
+SHUTDOWN
+       Shutdown the server.
+
+
+Unit Commands
+-------------
+
+       The first argument of any unit command is the unit name (U0 - U3). A
+       unit must be loaded with a file before it can play anything. A "clip"
+       refers to the presence of a file loaded into the unit. A clip can
+       contain an in and out point to set the playback region. The default in
+       point is 0, and the default out point is the number of frames in the
+       file minus one. Therefore, all frame positions are zero-based.
+
+USET {unit} {key=value}
+       Set a unit's configuration property.
+       Key is one of the following: eof, points.
+       
+       Property "eof" determines what the playback engine does when it reaches
+       the end of a clip. The eof property takes one of the following values:
+       stop, loop, continue or pause. The default is pause.
+       
+       Property "points" determines whether the playback engine restricts the
+       playback region to the in and out points. It takes one of the following
+       values: use, ignore.
+       
+UGET {unit} {key}
+       Get a unit's configuration property.
+       Key is one of the following: eof, points.
+       The response body contains only the key's value. See USET for information 
+       about each property.
+
+LIST {unit}
+       List the clips associated to the unit.
+       The response body consists of two sections - the first section is a single row
+       containing the generation number of the playlist associated to the unit (an
+       integer starting from 0 which is incremented on each action which changes the
+       playlist). The second sections contais a space-delimited row for each clip in the
+       units playlistcontaining the following columns:
+       - clip index (starts from 0)
+       - file name
+       - in point
+       - out point
+       - real length of the files
+       - calculated length of file
+       When USET points=use is specified (default), the calculated size is (out-in)+1. 
+       When points are ignored, the real length of the file is returned.
+
+LOAD {unit} {filename} [in out]
+       Load a clip into the unit.
+       Optionally set the in and out points to the specified absolute frame numbers.
+       Sets the current position to the first frame in the clip.
+       Preface the filename with '!' to tell the disk reader thread to remove only
+       duplicate frames from the tail of its buffer queue (from a previously loaded
+       and playing clip). Otherwise, miracle flushes all of its buffers upon LOAD
+       to make the effect of LOAD instantaneous. The LOAD !, USET eof=pause, and
+       extended USTA information can be used for client-side playlists (see the 
+       demo programs).
+
+APND {unit} {filename} [in out]
+       Append a clip onto the unit's playlist.
+       Optionally set the in and out points to the specified absolute frame numbers.
+       
+INSERT {unit} {filename} [ [+|-]clip [ in out ] ]
+       Insert a clip into the units playlist at the specified clip index or relative
+       to the currently playing clip index.
+
+REMOVE {unit} [ [+|-]clip ]
+       Removes a clip from the specified clip index or position relative to the 
+       currently playing clip index.
+       
+CLEAN {unit}
+       Removes all by the playing clip.
+       
+WIPE {unit}
+       Removes all clips before the playing clip.
+       
+MOVE {unit} [+|-]clip [ [+|-]clip ]
+       Move a clip in the playlist to position specified or position relative to the
+       currently playing clip.
+
+PLAY {unit} [speed]
+       Commence unit playback from the current position.
+       The default speed is 100% if not specified.
+       Speed is represented as a percentage value multiplied by 10. Therefore
+       the default playback speed is 1000 (1X or 100%), 2X is 2000.
+       Negative speed values play in reverse.
+
+STOP {unit}
+       Terminate the unit playback resulting in no video being sent.
+
+PAUSE {unit}
+       Pause the unit playback causing the current frame position to he held
+       indefinitely.
+
+REW {unit}
+       Rewind the unit.
+       If the unit it playing, then REW sets the playback speed to -2000
+       (200%).
+       If the unit is stopped, then the frame position is reset to the first
+       frame. First frame depends upon the "points" unit configuration property
+       and whether an in point has been established for the clip using the SIN
+       command.
+       Set the currently loaded clip's in point.
+       Frame is zero-based and absolute. It is not dependent upon the clip's
+       current in point.
+       A frame-number of -1, resets the in point to 0.
+
+FF {unit}
+       Fast forward the unit.
+       If the unit it playing, then FF sets the playback speed to 2000 (200%
+       in reverse).
+       If the unit is stopped, then the frame position is reset to the first
+       frame. First frame depends upon the "points" unit configuration property
+       and whether an in point has been established for the clip using the SIN
+       command.
+
+STEP {unit} {number-of-frames}
+       Adjust the current frame position by the number of frames specified.
+       Number-of-frames can accept positive or negative values.
+
+GOTO {unit} {frame-number} [ [+|-]clip ]
+       Set the current frame position to frame-number.
+       Frame-number is zero-based and absolute within the clip, which means it is 
+       relative to the file beginning and not the clip in point.
+       It does not alter the playback status of the unit. 
+
+SIN {unit} {frame-number} [ [+|-]clip ]
+       Set the currently loaded clip's in point.
+       The in point is the logical starting frame of the clip.
+       Frame is zero-based and absolute. It is not dependent upon the clip's
+       current in point.
+       A frame-number of -1, resets the in point to 0.
+
+SOUT {unit} {frame-number} [ [+|-]clip ]
+       Set the currently loaded clip's out point.
+       The out point is the logical last frame of the clip.
+       Frame is zero-based and absolute. It is not dependent upon the clip's
+       current out point.
+       A frame-number of -1, resets the out point to the number of frames in
+       the file minus 1.
+
+USTA {unit}
+       Get the unit status report.
+       The response body contains the following fields delimited by spaces:
+       - unit number: U0, U1, U2, or U3 without the "U" prefix
+       - mode: (offline|not_loaded|playing|stopped|paused|disconnected|unknown)
+         "unknown" means the unit has not been added
+         "disconnected" means the server has closed the connection to the client.
+       - current clip name: filename
+       - current position: in absolute frame number units
+       - speed: playback rate in (percent * 10)
+       - fps: frames-per-second of loaded clip
+       - current in-point: starting frame number
+       - current out-point: ending frame number
+       - length of the clip
+       - buffer tail clip name: filename
+       - buffer tail position: in absolute frame number units
+       - buffer tail in-point: starting frame number
+       - buffer tail out-point: ending frame number
+       - buffer tail length: length of clip in buffer tail
+       - seekable flag: indicates if the current clip is seekable (relates to head)
+       - playlist generation number
+       - current clip index (relates to head)
+        
+       The status contains information based not only on the current frame being
+       output (current above) but also based upon the most recent frame read by
+       the disk reader thread and added to the tail of the input buffer queue
+       (buffer tail above).
+
+XFER {unit} {target-unit}
+       Transfer the unit's clip to the target unit.
+       The clip inherently includes the in- and out-point information.
+       The target unit's "points" configuration property is set to "use."
+
+
+
+
diff --git a/docs/framework.txt b/docs/framework.txt
new file mode 100644 (file)
index 0000000..1c2bafb
--- /dev/null
@@ -0,0 +1,1341 @@
+Framework Documentation
+
+Copyright (C) 2004 Ushodaya Enterprises Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-10-08
+
+
+MLT FRAMEWORK
+-------------
+
+Preamble:
+
+       MLT is a multimedia framework designed for television broadcasting. As such, 
+       it provides a pluggable architecture for the inclusion of new audio/video 
+       sources, filters, transitions and playback devices.
+
+       The framework provides the structure and utility functionality on which
+       all of the MLT applications and services are defined. 
+
+       On its own, the framework provides little more than 'abstract classes' and
+       utilities for managing resources, such as memory, properties, dynamic object
+       loading and service instantiation. 
+
+       This document is split roughly into 3 sections. The first section provides a
+       basic overview of MLT, the second section shows how it's used and the final
+       section shows structure and design, with an emphasis on how the system is 
+       extended.
+
+
+Target Audience:
+
+       This document is provided as a 'road map' for the framework and should be
+       considered mandatory reading for anyone wishing to develop code at the MLT
+       level. 
+
+       This includes:
+
+       1. framework maintainers;
+       2. module developers;
+       3. application developers;
+       4. anyone interested in MLT.
+
+       The emphasis of the document is in explaining the public interfaces, as
+       opposed to the implementation details.
+
+       It is not required reading for the MLT client/server integration - please
+       refer to valerie.txt and dvcp.txt for more details on this area.
+
+
+SECTION 1 - BASIC OVERVIEW
+--------------------------
+
+Basic Design Information:
+
+       MLT is written in C. 
+       
+       The framework has no dependencies other than the standard C99 and POSIX 
+       libraries. 
+
+       It follows a basic Object Oriented design paradigm, and as such, much of the
+       design is loosely based on the Producer/Consumer design pattern. 
+       
+       It employs Reverse Polish Notation for the application of audio and video FX.
+
+       The framework is designed to be colour space neutral - the currently
+       implemented modules, however, are very much 8bit YUV422 oriented. In theory,
+       the modules could be entirely replaced.
+
+       A vague understanding of these terms is assumed throughout the remainder of
+       this document.
+
+
+Structure and Flow:
+
+       The general structure of an MLT 'network' is simply the connection of a
+       'producer' to a 'consumer':
+
+       +--------+   +--------+
+       |Producer|-->|Consumer|
+       +--------+   +--------+
+
+       A typical consumer requests MLT Frame objects from the producer, does 
+       something with them and when finished with a frame, closes it. 
+       
+        /\  A common confusion with the producer/consumer terminology used here is 
+       /!!\ that a consumer may 'produce' something. For example, the libdv consumer
+       \!!/ produces DV and the libdv producer seems to consume DV. However, the
+        \/  naming conventions refer only to producers and consumers of MLT Frames.
+
+       To put it another way - a producer produces MLT Frame objects and a consumer 
+       consumes MLT Frame objects.
+
+       An MLT Frame essentially provides an uncompressed image and its associated
+       audio samples.
+
+       Filters may also be placed between the producer and the consumer:
+
+       +--------+   +------+   +--------+
+       |Producer|-->|Filter|-->|Consumer|
+       +--------+   +------+   +--------+
+
+       A service is the collective name for producers, filters, transitions and
+       consumers. 
+
+       The communications between a connected consumer and producer or service are 
+       carried out in 3 phases:
+
+       * get the frame
+       * get the image
+       * get the audio
+
+       MLT employs 'lazy evaluation' - the image and audio need not be extracted
+       from the source until the get image and audio methods are invoked. 
+
+       In essence, the consumer pulls from what it's connected to - this means that
+       threading is typically in the domain of the consumer implementation and some
+       basic functionality is provided on the consumer class to ensure realtime
+       throughput.
+
+
+SECTION 2 - USAGE
+-----------------
+
+Hello World:
+
+       Before we go in to the specifics of the framework architecture, a working
+       example of usage is provided. 
+       
+       The following simply provides a media player:
+
+       #include <stdio.h>
+       #include <unistd.h>
+       #include <framework/mlt.h>
+
+       int main( int argc, char *argv[] )
+       {
+           // Initialise the factory
+           if ( mlt_factory_init( NULL ) == 0 )
+           {
+               // Create the default consumer
+               mlt_consumer hello = mlt_factory_consumer( NULL, NULL );
+
+               // Create via the default producer
+               mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
+
+               // Connect the producer to the consumer
+               mlt_consumer_connect( hello, mlt_producer_service( world ) );
+
+               // Start the consumer
+               mlt_consumer_start( hello );
+
+               // Wait for the consumer to terminate
+               while( !mlt_consumer_is_stopped( hello ) )
+                   sleep( 1 );
+
+               // Close the consumer
+               mlt_consumer_close( hello );
+
+               // Close the producer
+               mlt_producer_close( world );
+
+               // Close the factory
+               mlt_factory_close( );
+           }
+           else
+           {
+               // Report an error during initialisation
+               fprintf( stderr, "Unable to locate factory modules\n" );
+           }
+
+           // End of program
+           return 0;
+       }
+
+       This is a simple example - it doesn't provide any seeking capabilities or
+       runtime configuration options. 
+
+       The first step of any MLT application is the factory initialisation - this
+       ensures that the environment is configured and MLT can function. The factory
+       is covered in more detail below.
+
+       All services are instantiated via the factories, as shown by the
+       mlt_factory_consumer and mlt_factory_producer calls above. There are similar
+       factories for filters and transitions. There are details on all the standard
+       services in services.txt. 
+
+       The defaults requested here are a special case - the NULL usage requests
+       that we use the default producers and consumers. 
+       
+       The default producer is "fezzik". This producer matches file names to 
+       locate a service to use and attaches 'normalising filters' (such as scalers,
+       deinterlacers, resamplers and field normalisers) to the loaded content -
+       these filters ensure that the consumer gets what it asks for.
+
+       The default consumer is "sdl". The combination of fezzik and sdl will
+       provide a media player.
+
+       In this example, we connect the producer and then start the consumer. We
+       then wait until the consumer is stopped (in this case, by the action of the
+       user closing the SDL window) and finally close the consumer, producer and
+       factory before exiting the application.
+
+       Note that the consumer is threaded - waiting for an event of some sort is 
+       always required after starting and before stopping or closing the consumer.
+
+       Also note, you can override the defaults as follows:
+
+       $ MLT_CONSUMER=westley ./hello file.avi
+
+       This will create a westley xml document on stdout.
+
+       $ MLT_CONSUMER=westley MLT_PRODUCER=avformat ./hello file.avi
+
+       This will play the video using the avformat producer directly, thus it will
+       bypass the normalising functions.
+
+       $ MLT_CONSUMER=libdv ./hello file.avi > /dev/dv1394
+
+       This might, if you're lucky, do on the fly, realtime conversions of file.avi
+       to DV and broadcast it to your DV device.
+
+
+Factories:
+
+       As shown in the 'Hello World' example, factories create service objects.
+
+       The framework itself provides no services - they are provided in the form of
+       a plugin structure. A plugin is organised in the form of a 'module' and a
+       module can provide many services of different types. 
+
+       Once the factory is initialised, all the configured services are available
+       for use.
+
+       The complete set of methods associated to the factory are as follows:
+
+       int mlt_factory_init( char *prefix );
+       const char *mlt_factory_prefix( );
+       char *mlt_environment( char *name );
+       mlt_producer mlt_factory_producer( char *name, void *input );
+       mlt_filter mlt_factory_filter( char *name, void *input );
+       mlt_transition mlt_factory_transition( char *name, void *input );
+       mlt_consumer mlt_factory_consumer( char *name, void *input );
+       void mlt_factory_close( );
+
+       The mlt_factory_prefix returns the path to the location of the installed
+       modules directory. This can be specified in the mlt_factory_init call
+       itself, or it can be specified via the MLT_REPOSITORY environment variable,
+       or in the absence of either of those, it will default to the install
+       prefix/shared/mlt/modules. 
+
+       The mlt_environment provides read only access to a collection of name=value
+       pairs as shown in the following table:
+
+       +------------------+------------------------------------+------------------+
+       |Name              |Description                         |Values            |
+       +------------------+------------------------------------+------------------+
+       |MLT_NORMALISATION |The normalisation of the system     |PAL or NTSC       |
+       +------------------+------------------------------------+------------------+
+       |MLT_PRODUCER      |The default producer                |"fezzik" or other |
+       +------------------+------------------------------------+------------------+
+       |MLT_CONSUMER      |The default consumer                |"sdl" or other    |
+       +------------------+------------------------------------+------------------+
+       |MLT_TEST_CARD     |The default test card producer      |any producer      |
+       +------------------+------------------------------------+------------------+
+
+       These values are initialised from the environment variables of the same
+       name.
+
+       As shown above, a producer can be created using the 'default normalising'
+       producer, and they can also be requested by name. Filters and transitions 
+       are always requested by name - there is no concept of a 'default' for these.
+
+
+Service Properties:
+
+       As shown in the services.txt document, all services have their own set of
+       properties than can be manipulated to affect their behaviour.
+
+       In order to set properties on a service, we need to retrieve the properties
+       object associated to it. For producers, this is done by invoking:
+
+           mlt_properties properties = mlt_producer_properties( producer );
+
+       All services have a similar method associated to them.
+
+       Once retrieved, setting and getting properties can be done directly on this
+       object, for example:
+
+           mlt_properties_set( properties, "name", "value" );
+       
+       A more complete description of the properties object is found below.
+
+
+Playlists:
+
+       So far, we've shown a simple producer/consumer configuration - the next
+       phase is to organise producers in playlists.
+
+       Let's assume that we're adapting the Hello World example, and wish to queue
+       a number of files for playout, ie:
+
+           hello *.avi
+
+       Instead of invoking mlt_factory_producer directly, we'll create a new
+       function called create_playlist. This function is responsible for creating
+       the playlist, creating each producer and appending to the playlist.
+
+       mlt_producer create_playlist( int argc, char **argv )
+       {
+           // We're creating a playlist here
+           mlt_playlist playlist = mlt_playlist_init( );
+
+           // We need the playlist properties to ensure clean up
+           mlt_properties properties = mlt_playlist_properties( playlist );
+
+           // Loop through each of the arguments
+           int i = 0;
+           for ( i = 1; i < argc; i ++ )
+           {
+               // Create the producer
+               mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
+
+               // Add it to the playlist
+               mlt_playlist_append( playlist, producer );
+
+               // Close the producer (see below)
+               mlt_producer_close( producer );
+           }
+
+           // Return the playlist as a producer
+           return mlt_playlist_producer( playlist );
+       }
+
+       Notice that we close the producer after the append. Actually, what we're 
+       doing is closing our reference to it - the playlist creates its own reference
+       to the producer on append and insert, and it will close its reference 
+       when the playlist is destroyed[*].
+
+       Note also that if you append multiple instances of the same producer, it 
+       will create multiple references to it.
+
+       Now all we need do is to replace these lines in the main function:
+
+           // Create a normalised producer
+           mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] );
+
+       with:
+
+           // Create a playlist
+           mlt_producer world = create_playlist( argc, argv );
+
+       and we have a means to play multiple clips.
+
+       [*] This reference functionality was introduced in mlt 0.1.2 - it is 100%
+       compatable with the early mechanism of registering the reference and 
+       destructor with the properties of the playlist object.
+
+
+Filters:
+
+       Inserting filters between the producer and consumer is just a case of
+       instantiating the filters, connecting the first to the producer, the next
+       to the previous filter and the last filter to the consumer.
+
+       For example:
+
+           // Create a producer from something
+           mlt_producer producer = mlt_factory_producer( ... );
+
+           // Create a consumer from something
+           mlt_consumer consumer = mlt_factory_consumer( ... );
+
+           // Create a greyscale filter
+           mlt_filter filter = mlt_factory_filter( "greyscale", NULL );
+
+           // Connect the filter to the producer
+           mlt_filter_connect( filter, mlt_producer_service( producer ), 0 );
+
+           // Connect the consumer to filter
+           mlt_consumer_connect( consumer, mlt_filter_service( filter ) );
+
+       As with producers and consumers, filters can be manipulated via their
+       properties object - the mlt_filter_properties method can be invoked and
+       properties can be set as needed.
+
+       The additional argument in the filter connection is an important one as it
+       dictates the 'track' on which the filter operates. For basic producers and
+       playlists, there's only one track (0), and as you will see in the next
+       section, even multiple tracks have a single track output.
+
+
+Attached Filters:
+
+       All services can have attached filters.
+
+       Consider the following example:
+
+           // Create a producer
+           mlt_producer producer = mlt_factory_producer( NULL, clip );
+
+           // Get the service object of the producer
+           mlt_producer service = mlt_producer_service( producer );
+
+           // Create a filter
+           mlt_filter filter = mlt_factory_filter( "greyscale" );
+
+           // Create a playlist
+           mlt_playlist playlist = mlt_playlist_init( );
+
+           // Attach the filter to the producer
+           mlt_service_attach( producer, filter );
+
+           // Construct a playlist with various cuts from the producer
+           mlt_playlist_append_io( producer, 0, 99 );
+           mlt_playlist_append_io( producer, 450, 499 );
+           mlt_playlist_append_io( producer, 200, 399 );
+       
+           // We can close the producer and filter now
+           mlt_producer_close( producer );
+           mlt_filter_close( filter );
+
+       When this is played out, the greyscale filter will be executed for each frame 
+       in the playlist which comes from that producer.
+
+       Further, each cut can have their own filters attached which are executed after 
+       the producer's filters. As an example:
+
+           // Create a new filter
+           filter = mlt_factory_filter( "invert", NULL );
+       
+           // Get the second 'clip' in the playlist
+           producer = mlt_playlist_get_clip( 1 );
+       
+           // Get the service object of the clip
+           service = mlt_producer_service( producer );
+       
+           // Attach the filter
+           mlt_service_attach( producer, filter );
+       
+           // Close the filter
+           mlt_filter_close( filter );
+       
+       Even the playlist itself can have an attached filter:
+
+           // Create a new filter
+           filter = mlt_factory_filter( "watermark", "+Hello.txt" );
+       
+           // Get the service object of the playlist
+           service = mlt_playlist_service( playlist );
+       
+           // Attach the filter
+           mlt_service_attach( service, filter );
+       
+           // Close the filter
+           mlt_filter_close( filter );
+       
+       And, of course, the playlist, being a producer, can be cut up and placed on 
+       another playlist, and filters can be attached to those cuts or on the new 
+       playlist itself and so on ad nauseum.
+
+       The main advantage of attached filters is that they remain attached and don't 
+       suffer from the maintenance problems associated with items being inserted and 
+       displacing calculated in/out points - this being a major issue if you 
+       exclusively use the connect or insert detached filters in a multitrack field 
+       (described below).
+
+
+Introducing the Mix:
+
+       The mix is the simplest way to introduce transitions between adjacent clips
+       on a playlist.
+
+       Consider the following playlist:
+
+       +-+----------------------+----------------------------+-+
+       |X|A                     |B                           |X|
+       +-+----------------------+----------------------------+-+
+
+       Let's assume that the 'X' is a 'black clip' of 50 frames long.
+
+       When you play this out, you'll get a 50 frames of black, abrupt cut into
+       A, followed by an abrupt cut into B, and finally into black again.
+
+       The intention is to convert this playlist into something like:
+
+       +-+---------------------+-+------------------------+-+
+       |X|A                    |A|B                       |B|
+       |A|                     |B|                        |X|
+       +-+---------------------+-+------------------------+-+
+
+       Where the clips which refer to 2 clips represent a transition. Notice that
+       the representation of the second playlist is shorter than the first - this is
+       to be expected - a single transition of 50 frames between two clips will 
+       reduce the playtime of the result by 50 frames. 
+
+       This is done via the use of the mlt_playlist_mix method. So, assuming you get 
+       a playlist as shown in the original diagram, to do the first mix, you could do
+       something like:
+
+           // Create a transition
+           mlt_transition transition = mlt_factor_transition( "luma", NULL );
+
+           // Mix the first and second clips for 50 
+           mlt_playlist_mix( playlist, 0, 50, transition );
+
+           // Close the transition
+           mlt_transition_close( transition );
+
+       This would give you the first transition, subsequently, you would apply a similar
+       technique to mix clips 1 and 2. Note that this would create a new clip on the 
+       playlist, so the next mix would be between 3 and 4.
+
+       As a general hint, to simplify the requirement to know the next clip index,
+       you might find the following simpler:
+
+           // Get the number of clips on the playlist
+           int i = mlt_playlist_count( );
+
+           // Iterate through them in reverse order
+           while ( i -- )
+           {
+               // Create a transition
+               mlt_transition transition = mlt_factor_transition( "luma", NULL );
+
+               // Mix the first and second clips for 50 
+               mlt_playlist_mix( playlist, i, 50, transition );
+
+               // Close the transition
+               mlt_transition_close( transition );
+           }
+       
+       There are other techniques, like using the mlt_playlist_join between the 
+       current clip and the newly created one (you can determine if a new clip was 
+       created by comparing the playlist length before and after the mix call).
+
+       Internally, the mlt_playlist_mix call generates a tractor and multitrack as 
+       described below. Like the attached filters, the mix makes life very simple
+       when you're inserting items into the playlist.
+
+       Also note that it allows a simpler user interface - instead of enforcing the
+       use of a complex multitrack object, you can do many operations on a single
+       track. Thus, additional tracks can be used to introduce audio dubs, mixes
+       or composites which are independently positioned and aren't affected by 
+       manipulations on other tracks. But hey, if you want a bombastic, confusing
+       and ultimately frustrating traditional NLE experience, that functionality
+       is provided too ;-).
+
+
+Practicalities and Optimisations:
+
+       In the previous two sections I've introduced some powerful functionality 
+       designed to simplify MLT usage. However, a general issue comes into this -
+       what happens when you introduce a transition between two cuts from the same
+       bit of video footage?
+
+       Anyone who is familiar with video compression will be aware that seeking 
+       isn't always without consequence from a performance point of view. So if
+       you happen to require two frames from the same clip for a transition, the
+       processing is going to be excessive and the result will undoubtedly be very
+       unpleasant, especially if you're rendering in realtime...
+
+       So how do we get round this?
+
+       Actually, it's very simple - you invoke mlt_producer_optimise on the top 
+       level object after a modification and MLT will determine how to handle it.
+       Internally, it determines the maximum number of overlapping instances 
+       throughout the object and creates clones and assigns clone indexes as
+       required.
+
+       In the mix example above, you can simply call:
+
+           // Optimise the playlist
+           mlt_producer_optimise( mlt_playlist_producer( playlist ) );
+       
+       after the mix calls have be done. Note that this is automatically applied
+       to deserialised westleys.
+
+
+Multiple Tracks and Transitions:
+
+       MLT's approach to multiple tracks is governed by two requirements:
+
+       1) The need for a consumer and producer to communicate with one another via
+       a single frame;
+       2) The desire to be able to serialise and manipulate a 'network' (or filter
+       graph if you prefer).
+
+       We can visualise a multitrack in the way that an NLE presents it:
+
+          +-----------------+                          +-----------------------+
+       0: |a1               |                          |a2                     |
+          +---------------+-+--------------------------+-+---------------------+
+       1:                 |b1                            |
+                          +------------------------------+
+
+       The overlapping areas of track 0 and 1 would (presumably) have some kind of
+       transition - without a transition, the frames from b1 and b2 would be shown 
+       during the areas of overlap (ie: by default, the higher numbered track takes 
+       precedence over the lower numbered track). 
+
+       MLT has a multitrack object, but it is not a producer in the sense that it
+       can be connected directly to a consumer and everything will work correctly.
+       A consumer would treat it precisely as it would a normal producer, and, in
+       the case of the multitrack above, you would never see anything from track 1
+       other than the transitions between the clips - the gap between a1 and a2 
+       would show test frames.
+
+       This happens because a consumer pulls one frame from the producer it's 
+       connected to while a multitrack will provide one frame per track.
+       Something, somewhere, must ensure that all frames are pulled from the
+       multitrack and elect the correct frame to pass on.
+
+       Hence, MLT provides a wrapper for the multitrack, which is called a
+       'tractor', and its the tractors task to ensure that all tracks are pulled
+       evenly, the correct frame is output and that we have 'producer like'
+       behaviour.
+
+       Thus, a multitrack is conceptually 'pulled' by a tractor as shown here:
+
+       +----------+
+       |multitrack|
+       | +------+ |    +-------+
+       | |track0|-|--->|tractor|
+       | +------+ |    |\      |
+       |          |    | \     |
+       | +------+ |    |  \    |
+       | |track1|-|--->|---o---|--->
+       | +------+ |    |  /    |
+       |          |    | /     |
+       | +------+ |    |/      |
+       | |track2|-|--->|       |
+       | +------+ |    +-------+
+       +----------+
+
+       With a combination of the two, we can now connect multitracks to consumers.
+       The last non-test card will be retrieved and passed on. 
+
+       The tracks can be producers, playlists, or even other tractors. 
+
+       Now we wish to insert filters and transitions between the multitrack and the
+       tractor. We can do this directly by inserting filters directly between the
+       tractor and the multitrack, but this involves a lot of connecting and
+       reconnecting left and right producers and consumers, and it seemed only fair
+       that we should be able to automate that process. 
+
+       So in keeping with our agricultural theme, the concept of the 'field' was 
+       born. We 'plant' filters and transitions in the field and the tractor pulls 
+       the multitrack (think of a combine harvester :-)) over the field and 
+       produces a 'bail' (sorry - kidding - frame :-)).
+
+       Conceptually, we can see it like this:
+
+       +----------+
+       |multitrack|
+       | +------+ |    +-------------+    +-------+
+       | |track0|-|--->|field        |--->|tractor|
+       | +------+ |    |             |    |\      |
+       |          |    |   filters   |    | \     |
+       | +------+ |    |     and     |    |  \    |
+       | |track1|-|--->| transitions |--->|---o---|--->
+       | +------+ |    |             |    |  /    |
+       |          |    |             |    | /     |
+       | +------+ |    |             |    |/      |
+       | |track2|-|--->|             |--->|       |
+       | +------+ |    +-------------+    +-------+
+       +----------+
+
+       So, we need to create the tractor first, and from that we obtain the
+       multitrack and field objects. We can populate these and finally 
+       connect the tractor to a consumer.
+
+       In essence, this is how it looks to the consumer:
+
+       +-----------------------------------------------+
+       |tractor          +--------------------------+  |
+       | +----------+    | +-+    +-+    +-+    +-+ |  |
+       | |multitrack|    | |f|    |f|    |t|    |t| |  |
+       | | +------+ |    | |i|    |i|    |r|    |r| |  |
+       | | |track0|-|--->| |l|- ->|l|- ->|a|--->|a|\|  |
+       | | +------+ |    | |t|    |t|    |n|    |n| |  |
+       | |          |    | |e|    |e|    |s|    |s| |\ |
+       | | +------+ |    | |r|    |r|    |i|    |i| | \|
+       | | |track1|-|- ->| |0|--->|1|--->|t|--->|t|-|--o--->
+       | | +------+ |    | | |    | |    |i|    |i| | /|
+       | |          |    | | |    | |    |o|    |o| |/ |
+       | | +------+ |    | | |    | |    |n|    |n| |  |
+       | | |track2|-|- ->| | |- ->| |--->|0|- ->|1|/|  |
+       | | +------+ |    | | |    | |    | |    | | |  |
+       | +----------+    | +-+    +-+    +-+    +-+ |  |
+       |                 +--------------------------+  |
+       +-----------------------------------------------+
+
+       An example will hopefully clarify this. 
+       
+       Let's assume that we want to provide a 'watermark' to our hello world 
+       example. We have already extended the example to play multiple clips,
+       and now we will place a text based watermark, reading 'Hello World' in 
+       the top left hand corner:
+
+       mlt_producer create_tracks( int argc, char **argv )
+       {
+           // Create the tractor
+           mlt_tractor tractor = mlt_tractor_new( );
+
+           // Obtain the field
+           mlt_field field = mlt_tractor_field( tractor );
+       
+           // Obtain the multitrack
+           mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );
+       
+           // Create a composite transition
+           mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );
+       
+           // Create track 0
+           mlt_producer track0 = create_playlist( argc, argv );
+       
+           // Create the watermark track - note we NEED fezzik for scaling here
+           mlt_producer track1 = mlt_factory_producer( "fezzik", "pango" );
+       
+           // Get the length of track0
+           mlt_position length = mlt_producer_get_playtime( track0 );
+       
+           // Set the properties of track1
+           mlt_properties properties = mlt_producer_properties( track1 );
+           mlt_properties_set( properties, "text", "Hello\nWorld" );
+           mlt_properties_set_position( properties, "in", 0 );
+           mlt_properties_set_position( properties, "out", length - 1 );
+           mlt_properties_set_position( properties, "length", length );
+           mlt_properties_set_int( properties, "a_track", 0 );
+           mlt_properties_set_int( properties, "b_track", 1 );
+
+           // Now set the properties on the transition
+           properties = mlt_transition_properties( transition );
+           mlt_properties_set_position( properties, "in", 0 );
+           mlt_properties_set_position( properties, "out", length - 1 );
+       
+           // Add our tracks to the multitrack
+           mlt_multitrack_connect( multitrack, track0, 0 );
+           mlt_multitrack_connect( multitrack, track1, 1 );
+       
+           // Now plant the transition
+           mlt_field_plant_transition( field, transition, 0, 1 );
+
+           // Close our references
+           mlt_producer_close( track0 );
+           mlt_producer_close( track1 );
+           mlt_transition_close( transition );
+
+           // Return the tractor
+           return mlt_tractor_producer( tractor );
+       }
+
+       Now all we need do is to replace these lines in the main function:
+
+           // Create a playlist
+           mlt_producer world = create_playlist( argc, argv );
+
+       with:
+
+           // Create a watermarked playlist
+           mlt_producer world = create_tracks( argc, argv );
+
+       and we have a means to play multiple clips with a horribly obtrusive
+       watermark - just what the world needed, right? ;-)
+
+       Incidentally, the same thing could be achieved with the more trivial
+       watermark filter inserted between the producer and the consumer.
+
+
+SECTION 3 - STRUCTURE AND DESIGN
+--------------------------------
+
+Class Hierarchy:
+
+       The mlt framework consists of an OO class hierarchy which consists of the
+       following public classes and abstractions:
+
+       mlt_properties
+         mlt_frame
+         mlt_service
+           mlt_producer
+             mlt_playlist
+             mlt_tractor
+           mlt_filter
+           mlt_transition
+           mlt_consumer
+       mlt_deque
+       mlt_pool
+       mlt_factory
+
+       Each class defined above can be read as extending the classes above and to
+       the left.
+
+       The following sections describe the properties, stacking/queuing and memory 
+       pooling functionality provided by the framework - these are key components 
+       and a basic understanding of these is required for the remainder of the
+       documentation.
+
+
+mlt_properties:
+
+       The properties class is the base class for the frame and service classes.
+
+       It is designed to provide an efficient lookup table for various types of
+       information, such as strings, integers, floating points values and pointers
+       to data and data structures. 
+
+       All properties are indexed by a unique string. 
+       
+       The most basic use of properties is as follows:
+
+           // 1. Create a new, empty properties set;
+           mlt_properties properties = mlt_properties_new( );
+
+           // 2. Assign the value "world" to the property "hello";
+           mlt_properties_set( properties, "hello", "world" );
+
+           // 3. Retrieve and print the value of "hello";
+           printf( "%s\n", mlt_properties_get( properties, "hello" ) );
+
+           // 4. Reassign "hello" to "world!";
+           mlt_properties_set( properties, "hello", "world!" );
+
+           // 5. Retrieve and print the value of "hello";
+           printf( "%s\n", mlt_properties_get( properties, "hello" ) );
+
+           // 6. Assign the value "0" to "int";
+           mlt_properties_set( properties, "int", "0" );
+
+           // 7. Retrieve and print the integer value of "int";
+           printf( "%d\n", mlt_properties_get_int( properties, "int" ) );
+
+           // 8. Assign the integer value 50 to "int2";
+           mlt_properties_set_int( properties, "int2", 50 );
+
+           // 9. Retrieve and print the double value of "int2";
+           printf( "%s\n", mlt_properties_get( properties, "int2" ) );
+
+       Steps 2 through 5 demonstrate that the "name" is unique - set operations on 
+       an existing "name" change the value. They also free up memory associated to 
+       the previous value. Note that it also possible to change type in this way
+       too.
+
+       Steps 6 and 7 demonstrate that the properties object handles deserialisation
+       from strings. The string value of "0" is set, the integer value of 0 is
+       retrieved.
+
+       Steps 8 and 9 demonstrate that the properties object handles serialisation
+       to strings.
+
+       To show all the name/value pairs in a properties, it is possible to iterate
+       through them:
+
+           for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+               printf( "%s = %s\n", mlt_properties_get_name( properties, i ),
+                                    mlt_properties_get_value( properties, i ) );
+
+       Note that properties are retrieved in the order in which they are set. 
+
+       Properties are also used to hold pointers to memory. This is done via the
+       set_data call:
+
+       uint8_t *image = malloc( size );
+       mlt_properties_set_data( properties, "image", image, size, NULL, NULL );
+
+       In this example, we specify that the pointer can be retrieved from
+       properties by a subsequent request to get_data:
+
+           image = mlt_properties_get_data( properties, "image", &size );
+
+       or:
+
+           image = mlt_properties_get_data( properties, "image", NULL );
+
+       if we don't wish to retrieve the size.
+
+       Two points here:
+
+       1) The allocated memory remains after the properties object is closed unless
+          you specify a destructor. In the case above, this can be done with:
+
+          mlt_properties_set_data( properties, "image", image, size, free, NULL );
+
+          When the properties are closed, or the value of "image" is changed, the 
+          destructor is invoked.
+       
+       2) The string value returned by mlt_properties_get is NULL. Typically, you
+          wouldn't wish to serialise an image as a string, but other structures
+          might need such functionality - you can specify a serialiser as the last
+          argument if required (declaration is char *serialise( void * )).
+
+       Properties also provides some more advanced usage capabilities. 
+       
+       It has the ability to inherit all serialisable values from another properties 
+       object:
+
+           mlt_properties_inherit( this, that );
+
+       It has the ability to mirror properties set on this on another set of
+       properties:
+
+           mlt_properties_mirror( this, that );
+
+       After this call, all serialisable values set on this are passed on to that.
+
+
+mlt_deque:
+
+       Stacks and queues are essential components in the MLT framework. Being of a
+       lazy disposition, we elected to implement a 'Double Ended Queue' (deque) -
+       this encapsulates the functionality of both.
+
+       The API of the deque is defined as follows:
+
+       mlt_deque mlt_deque_init( );
+       int mlt_deque_count( mlt_deque this );
+       int mlt_deque_push_back( mlt_deque this, void *item );
+       void *mlt_deque_pop_back( mlt_deque this );
+       int mlt_deque_push_front( mlt_deque this, void *item );
+       void *mlt_deque_pop_front( mlt_deque this );
+       void *mlt_deque_peek_back( mlt_deque this );
+       void *mlt_deque_peek_front( mlt_deque this );
+       void mlt_deque_close( mlt_deque this );
+
+       The stacking operations are used in a number of places:
+
+       * Reverse Polish Notation (RPN) image and audio operations
+       * memory pooling
+
+       The queuing operations are used in:
+       
+       * the consumer base class;
+       * consumer implementations may require further queues.
+
+
+mlt_pool:
+
+       The MLT framework provides memory pooling capabilities through the mlt_pool
+       API. Once initilialised, these can be seen as a straightforward drop in
+       replacement for malloc/realloc/free functionality.
+
+       The background behind this API is that malloc/free operations are
+       notoriously inefficient, especially when dealing with large blocks of memory
+       (such as an image). On linux, malloc is optimised for memory allocations
+       less than 128k - memory blocks allocated of these sizes or less are retained
+       in the process heap for subsequent reuse, thus bypassing the kernel calls
+       for repeated allocation/frees for small blocks of memory. However, blocks of
+       memory larger than that require kernel calls and this has a detrimental
+       impact on performance.
+
+       The mlt_pool design is simply to hold a list of stacks - there is one stack
+       per 2^n bytes (where n is between 8 and 31). When an alloc is called, the
+       requested size is rounded to the next 2^n, the stack is retrieved for that
+       size, and an item is popped or created if the stack is empty. 
+
+       Each item has a 'header', situated immediately before the returned address - 
+       this holds the 'stack' to which the item belongs.
+
+       When an item is released, we retrieve the header, obtain the stack and push
+       it back.
+
+       Thus, from the programmers point of view, the API is the same as the
+       traditional malloc/realloc/free calls:
+
+       void *mlt_pool_alloc( int size );
+       void *mlt_pool_realloc( void *ptr, int size );
+       void mlt_pool_release( void *release );
+
+
+mlt_frame:
+
+       A frame object is essentially defined as:
+
+       +------------+
+       |frame       |
+       +------------+
+       | properties |
+       | image stack|
+       | audio stack|
+       +------------+
+
+       The life cycle of a frame can be represented as follows:
+
+       +-----+----------------------+-----------------------+---------------------+
+       |Stage|Producer              |Filter                 |Consumer             |
+       +-----+----------------------+-----------------------+---------------------+
+       | 0.0 |                      |                       |Request frame        |
+       +-----+----------------------+-----------------------+---------------------+
+       | 0.1 |                      |Receives request       |                     |
+       |     |                      |Request frame          |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 0.2 |Receives request      |                       |                     |
+       |     |Generates frame for   |                       |                     |
+       |     |current position      |                       |                     |
+       |     |Increments position   |                       |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 0.3 |                      |Receives frame         |                     |
+       |     |                      |Updates frame          |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 0.4 |                      |                       |Receives frame       |
+       +-----+----------------------+-----------------------+---------------------+
+
+       Note that neither the filter nor the consumer have any conception of
+       'position' until they receive a frame. Speed and position are properties of
+       the producer, and they are assigned to the frame object when the producer
+       creates it.
+
+       Step 0.3 is a critical one here - if the filter determines that the frame is
+       of interest to it, then it should manipulate the image and/or audio stacks
+       and properties as required.
+
+       Assuming that the filter deals with both image and audio, then it should
+       push data and methods on to the stacks which will deal with the processing. 
+       This can be done with the mlt_frame_push_image and audio methods. In order for 
+       the filter to register interest in the frame, the stacks should hold:
+
+       image stack:
+       [ producer_get_image ] [ data1 ] [ data2 ] [ filter_get_image ]
+
+       audio stack:
+       [ producer_get_audio ] [ data ] [ filter_get_audio ]
+
+       The filter_get methods are invoked automatically when the consumer invokes a
+       get_image on the frame. 
+
+       +-----+----------------------+-----------------------+---------------------+
+       |Stage|Producer              |Filter                 |Consumer             |
+       +-----+----------------------+-----------------------+---------------------+
+       | 1.0 |                      |                       |frame_get_image      |
+       +-----+----------------------+-----------------------+---------------------+
+       | 1.1 |                      |filter_get_image:      |                     |
+       |     |                      | pop data2 and data1   |                     |
+       |     |                      | frame_get_image       |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 1.2 |producer_get_image    |                       |                     |
+       |     |  Generates image     |                       |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 1.3 |                      |Receives image         |                     |
+       |     |                      |Updates image          |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 1.4 |                      |                       |Receives image       |
+       +-----+----------------------+-----------------------+---------------------+
+
+       Obviously, if the filter isn't interested in the image, then it should leave
+       the stack alone, and then the consumer will retrieve its image directly from
+       the producer.
+
+       Similarly, audio is handled as follows:
+
+       +-----+----------------------+-----------------------+---------------------+
+       |Stage|Producer              |Filter                 |Consumer             |
+       +-----+----------------------+-----------------------+---------------------+
+       | 2.0 |                      |                       |frame_get_audio      |
+       +-----+----------------------+-----------------------+---------------------+
+       | 2.1 |                      |filter_get_audio:      |                     |
+       |     |                      | pop data              |                     |
+       |     |                      | frame_get_audio       |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 2.2 |producer_get_audio    |                       |                     |
+       |     |  Generates audio     |                       |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 2.3 |                      |Receives audio         |                     |
+       |     |                      |Updates audio          |                     |
+       +-----+----------------------+-----------------------+---------------------+
+       | 2.4 |                      |                       |Receives audio       |
+       +-----+----------------------+-----------------------+---------------------+
+
+       And finally, when the consumer is done with the frame, it should close it.
+
+       Note that a consumer may not evaluate both image and audio for any given
+       frame, especially in a realtime environment. See 'Realtime Considerations'
+       below.
+
+       By default, a frame has the following properties:
+
+       +------------------+------------------------------------+------------------+
+       |Name              |Description                         |Values            |
+       +------------------+------------------------------------+------------------+
+       |_position         |The producers frame position        |0 to n            |
+       +------------------+------------------------------------+------------------+
+       |_speed            |The producers speed                 |double            |
+       +------------------+------------------------------------+------------------+
+       |image             |The generated image                 |NULL or pointer   |
+       +------------------+------------------------------------+------------------+
+       |alpha             |The generated alpha mask            |NULL or pointer   |
+       +------------------+------------------------------------+------------------+
+       |width             |The width of the image              |                  |
+       +------------------+------------------------------------+------------------+
+       |height            |The height of the image             |                  |
+       +------------------+------------------------------------+------------------+
+       |normalised_width  |The normalised width of the image   |720               |
+       +------------------+------------------------------------+------------------+
+       |normalised_height |The normalised height of the image  |576 or 480        |
+       +------------------+------------------------------------+------------------+
+       |progressive       |Indicates progressive/interlaced    |0 or 1            |
+       +------------------+------------------------------------+------------------+
+       |top_field_first   |Indicates top field first           |0 or 1            |
+       +------------------+------------------------------------+------------------+
+       |audio             |The generated audio                 |NULL or pointer   |
+       +------------------+------------------------------------+------------------+
+       |frequency         |The frequency of the audio          |                  |
+       +------------------+------------------------------------+------------------+
+       |channels          |The channels of the audio           |                  |
+       +------------------+------------------------------------+------------------+
+       |samples           |The samples of the audio            |                  |
+       +------------------+------------------------------------+------------------+
+       |aspect_ratio      |The sample aspect ratio of the image|double            |
+       +------------------+------------------------------------+------------------+
+       |test_image        |Used to indicate no image available |0 or 1            |
+       +------------------+------------------------------------+------------------+
+       |test_audio        |Used to indicate no audio available |0 or 1            |
+       +------------------+------------------------------------+------------------+
+
+       The consumer can attach the following properties which affect the default
+       behaviour of a frame:
+
+       +------------------+------------------------------------+------------------+
+       |test_card_producer|Synthesise test images from here    |NULL or pointer   |
+       +------------------+------------------------------------+------------------+
+       |consumer_aspect_  |Apply this aspect ratio to the test |double            |
+       |ratio             |card producer                       |                  |
+       +------------------+------------------------------------+------------------+
+       |rescale.interp    |Use this scale method for test image|"string"          |
+       +------------------+------------------------------------+------------------+
+
+       While most of these are mainly self explanatory, the normalised_width and
+       normalised_height values require a little explanation. These are required
+       to ensure that effects are consistently handled as PAL or NTSC, regardless 
+       of the consumers or producers width/height image request. 
+
+       The test_image and audio flags are used to determine when images and audio
+       should be synthesised.
+
+       Additional properties may be provided by the producer implementation, and
+       filters, transitions and consumers may add additional properties to
+       communicate specific requests. These are documented in modules.txt.
+
+       The complete API for the mlt frame is as follows:
+
+       mlt_frame mlt_frame_init( );
+       mlt_properties mlt_frame_properties( mlt_frame this );
+       int mlt_frame_is_test_card( mlt_frame this );
+       int mlt_frame_is_test_audio( mlt_frame this );
+       double mlt_frame_get_aspect_ratio( mlt_frame this );
+       int mlt_frame_set_aspect_ratio( mlt_frame this, double value );
+       mlt_position mlt_frame_get_position( mlt_frame this );
+       int mlt_frame_set_position( mlt_frame this, mlt_position value );
+       int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+       uint8_t *mlt_frame_get_alpha_mask( mlt_frame this );
+       int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+       int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image );
+       mlt_get_image mlt_frame_pop_get_image( mlt_frame this );
+       int mlt_frame_push_frame( mlt_frame this, mlt_frame that );
+       mlt_frame mlt_frame_pop_frame( mlt_frame this );
+       int mlt_frame_push_service( mlt_frame this, void *that );
+       void *mlt_frame_pop_service( mlt_frame this );
+       int mlt_frame_push_audio( mlt_frame this, void *that );
+       void *mlt_frame_pop_audio( mlt_frame this );
+       void mlt_frame_close( mlt_frame this );
+
+
+mlt_service:
+
+       The service base class extends properties and allows 0 to m inputs and 0 to
+       n outputs and is represented as follows:
+
+           +-----------+
+       - ->|           |- ->
+       - ->|  Service  |- ->
+       - ->|           |
+           +-----------+
+           | properties|
+           +-----------+
+
+       Descendents of service impose restrictions on how inputs and outputs can be 
+       connected and will provide a basic set of properties. Typically, the service 
+       instance is encapsulated by the descendent in order for it to ensure that
+       its connection rules are followed.
+
+       A service does not define any properties when constructed. It should be
+       noted that producers, filters and transitions my be serialised (say, via the
+       westley consumer), and care should be taken to distinguish between
+       serialisable and transient properties. The convention used is to prefix
+       transient properties with an underscore.
+
+       The public interface is defined by the following functions:
+
+       int mlt_service_init( mlt_service this, void *child );
+       mlt_properties mlt_service_properties( mlt_service this );
+       int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index );
+       int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+       void mlt_service_close( mlt_service this );
+
+       Typically, only direct descendents of services need invoke these methods and
+       developers are encouraged to use those extensions when defining new services. 
+
+
+mlt_producer:
+
+       A producer has 0 inputs and 1 output:
+
+           +-----------+
+           |           |
+           | Producer  |--->
+           |           |
+           +-----------+
+           | service   |
+           +-----------+
+
+       A producer provides an abstraction for file readers, pipes, streams or any
+       other image or audio input. 
+
+       When instantiated, a producer has the following properties:
+
+       +------------------+------------------------------------+------------------+
+       |Name              |Description                         |Values            |
+       +------------------+------------------------------------+------------------+
+       |mlt_type          |The producers type                  |mlt_producer      |
+       +------------------+------------------------------------+------------------+
+       |_position         |The producers frame position        |0 to n            |
+       +------------------+------------------------------------+------------------+
+       |_speed            |The producers speed                 |double            |
+       +------------------+------------------------------------+------------------+
+       |fps               |The output frames per second        |25 or 29.97       |
+       +------------------+------------------------------------+------------------+
+       |in                |The in point in frames              |0 to length - 1   |
+       +------------------+------------------------------------+------------------+
+       |out               |The out point in frames             |in to length - 1  |
+       +------------------+------------------------------------+------------------+
+       |length            |The length of the input in frames   |0 to n            |
+       +------------------+------------------------------------+------------------+
+       |aspect_ratio      |aspect_ratio of the source          |0 to n            |
+       +------------------+------------------------------------+------------------+
+       |eof               |end of clip behaviour               |"pause" or "loop" |
+       +------------------+------------------------------------+------------------+
+       |resource          |Constructor argument (ie: file name)|"<resource>"      |
+       +------------------+------------------------------------+------------------+
+
+       Additional properties may be provided by the producer implementation.
+
+       The public interface is defined by the following functions:
+
+       mlt_producer mlt_producer_new( );
+       int mlt_producer_init( mlt_producer this, void *child );
+       mlt_service mlt_producer_service( mlt_producer this );
+       mlt_properties mlt_producer_properties( mlt_producer this );
+       int mlt_producer_seek( mlt_producer this, mlt_position position );
+       mlt_position mlt_producer_position( mlt_producer this );
+       mlt_position mlt_producer_frame( mlt_producer this );
+       int mlt_producer_set_speed( mlt_producer this, double speed );
+       double mlt_producer_get_speed( mlt_producer this );
+       double mlt_producer_get_fps( mlt_producer this );
+       int mlt_producer_set_in_and_out( mlt_producer this, mlt_position in, mlt_position out );
+       mlt_position mlt_producer_get_in( mlt_producer this );
+       mlt_position mlt_producer_get_out( mlt_producer this );
+       mlt_position mlt_producer_get_playtime( mlt_producer this );
+       mlt_position mlt_producer_get_length( mlt_producer this );
+       void mlt_producer_prepare_next( mlt_producer this );
+       void mlt_producer_close( mlt_producer this );
+
+
+mlt_filter:
+
+       The public interface is defined by the following functions:
+
+       int mlt_filter_init( mlt_filter this, void *child );
+       mlt_filter mlt_filter_new( );
+       mlt_service mlt_filter_service( mlt_filter this );
+       mlt_properties mlt_filter_properties( mlt_filter this );
+       mlt_frame mlt_filter_process( mlt_filter this, mlt_frame that );
+       int mlt_filter_connect( mlt_filter this, mlt_service producer, int index );
+       void mlt_filter_set_in_and_out( mlt_filter this, mlt_position in, mlt_position out );
+       int mlt_filter_get_track( mlt_filter this );
+       mlt_position mlt_filter_get_in( mlt_filter this );
+       mlt_position mlt_filter_get_out( mlt_filter this );
+       void mlt_filter_close( mlt_filter );
+
+
+mlt_transition:
+
+       The public interface is defined by the following functions:
+
+       int mlt_transition_init( mlt_transition this, void *child );
+       mlt_transition mlt_transition_new( );
+       mlt_service mlt_transition_service( mlt_transition this );
+       mlt_properties mlt_transition_properties( mlt_transition this );
+       int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track );
+       void mlt_transition_set_in_and_out( mlt_transition this, mlt_position in, mlt_position out );
+       int mlt_transition_get_a_track( mlt_transition this );
+       int mlt_transition_get_b_track( mlt_transition this );
+       mlt_position mlt_transition_get_in( mlt_transition this );
+       mlt_position mlt_transition_get_out( mlt_transition this );
+       mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame );
+       void mlt_transition_close( mlt_transition this );
+
+
+mlt_consumer:
+
+       The public interface is defined by the following functions:
+
+       int mlt_consumer_init( mlt_consumer this, void *child );
+       mlt_service mlt_consumer_service( mlt_consumer this );
+       mlt_properties mlt_consumer_properties( mlt_consumer this );
+       int mlt_consumer_connect( mlt_consumer this, mlt_service producer );
+       int mlt_consumer_start( mlt_consumer this );
+       mlt_frame mlt_consumer_get_frame( mlt_consumer this );
+       mlt_frame mlt_consumer_rt_frame( mlt_consumer this );
+       int mlt_consumer_stop( mlt_consumer this );
+       int mlt_consumer_is_stopped( mlt_consumer this );
+       void mlt_consumer_close( mlt_consumer );
+
+
+Specialised Producers:
+
+       There are two major types of specialised producers - playlists and tractors.
+
+       The following sections describe these.
+
+
+mlt_playlist:
+
+       mlt_playlist mlt_playlist_init( );
+       mlt_producer mlt_playlist_producer( mlt_playlist this );
+       mlt_service mlt_playlist_service( mlt_playlist this );
+       mlt_properties mlt_playlist_properties( mlt_playlist this );
+       int mlt_playlist_count( mlt_playlist this );
+       int mlt_playlist_clear( mlt_playlist this );
+       int mlt_playlist_append( mlt_playlist this, mlt_producer producer );
+       int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out );
+       int mlt_playlist_blank( mlt_playlist this, mlt_position length );
+       mlt_position mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index );
+       int mlt_playlist_current_clip( mlt_playlist this );
+       mlt_producer mlt_playlist_current( mlt_playlist this );
+       int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index );
+       int mlt_playlist_insert( mlt_playlist this, mlt_producer producer, int where, mlt_position in, mlt_position out );
+       int mlt_playlist_remove( mlt_playlist this, int where );
+       int mlt_playlist_move( mlt_playlist this, int from, int to );
+       int mlt_playlist_resize_clip( mlt_playlist this, int clip, mlt_position in, mlt_position out );
+       void mlt_playlist_close( mlt_playlist this );
+
+mlt_tractor:
diff --git a/docs/inigo.txt b/docs/inigo.txt
new file mode 100644 (file)
index 0000000..854c1c1
--- /dev/null
@@ -0,0 +1,401 @@
+Inigo Documentation
+
+Copyright (C) 2004-2009 Ushodaya Enterprised Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2009-01-21
+
+
+INIGO
+-----
+
+Preamble:
+
+       inigo was developed as a test tool for the MLT framework. It can be thought
+       of as a powerful, if somewhat obscure, multitrack command line oriented 
+       video editor.
+
+       The following details the usage of the tool and as a result, provides a lot 
+       of insight into the workings of the MLT framework.
+
+
+Usage:
+
+    inigo [options] [producer [name=value]* ]+
+    Options:
+      -attach filter[:arg] [name=value]*       Attach a filter to the output
+      -attach-cut filter[:arg] [name=value]*   Attach a filter to a cut
+      -attach-track filter[:arg] [name=value]* Attach a filter to a track
+      -attach-clip filter[:arg] [name=value]*  Attach a filter to a producer
+      -audio-track | -hide-video               Add an audio-only track
+      -blank frames                            Add blank silence to a track
+      -consumer id[:arg] [name=value]*         Set the consumer (sink)
+      -debug                                   Set the logging level to debug
+      -filter filter[:arg] [name=value]*       Add a filter to the current track
+      -group [name=value]*                     Apply properties repeatedly
+      -help                                    Show this message
+      -join clips                              Join multiple clips into one cut
+      -mix length                              Add a mix between the last two cuts
+      -mixer transition                        Add a transition to the mix
+      -null-track | -hide-track                Add a hidden track
+      -profile name                            Set the processing settings
+      -progress                                Display progress along with the position
+      -remove                                  Remove the most recent cut
+      -repeat times                            Repeat the last cut
+      -query                                   List all of the registered services
+      -query "consumers" | "consumer"=id       List consumers or show info about one
+      -query "filters" | "filter"=id           List filters or show info about one
+      -query "producers" | "producer"=id       List producers or show info about one
+      -query "transitions" | "transition"=id   List transitions or show info about one
+      -serialise [filename]                    Write the commands to a text file
+      -silent                                  Do not display position/transport help
+      -split relative-frame                    Split the last cut into two cuts
+      -swap                                    Rearrange the last two cuts
+      -track                                   Add a track
+      -transition id[:arg] [name=value]*       Add a transition
+      -verbose                                 Set the logging level to verbose
+      -version                                 Show the version and copyright message
+      -video-track | -hide-audio               Add a video-only track
+
+
+General rules:
+
+       1. Order is incredibly important;
+
+       2. Error checking on command line parsing is weak;
+
+       3. Please refer to services.txt for details on services available;
+
+       4. The MLT framework, from which inigo has inherited its naming convention,
+       is very mlt-centric. Producers produce MLT frame objects and consumers 
+       consume MLT frame objects.  The distinction is important - a DV producer 
+       does not produce DV, it produces MLT frames from a DV source, and similarly
+       a DV consumer does not consume DV, it consumes MLT frames and produces DV
+       frames.
+
+
+Terminology:
+
+       'Producers' typically refer to files but may also indicate devices (such as
+       dv1394 input or video4linux). Hence, the more generic term is used [the more 
+       generic usage is out of scope for now...].
+
+       'Filters' are frame modifiers - they always guarantee that for every frame 
+       they receive, they output *precisely* one frame.  Never more, never less, 
+       ever. Nothing says that a filter cannot generate frames though
+       
+       'Transitions' collect frames from two tracks (a and b) and output 1 
+       modified frame on their 'a track', and 1 unmodified frame on their 'b track'.
+       Never more, never less, ever.
+       
+       'Consumers' collect frames from a producer, do something with them and
+       destroy them.
+       
+       Collectively, these are known as 'services'. 
+       
+       All services have 'properties' associated to them. These are typically
+       defaulted or evaluated and may be overriden on a case by case basis.
+       
+       All services except consumers obey in and out properties.
+       
+       Consumers have no say in the flow of frames [though they may give the
+       illusion that they do]. They get frames from a connected producer, use them, 
+       destroy them and get more.
+
+
+Basics:
+
+       To play a file with the default SDL PAL consumer, usage is:
+       
+       $ inigo file
+
+       Note that 'file' can be anything that inigo has a known 'producer' mapping 
+       for (so this can be anything from .dv to .txt).
+
+       You can also specify the producer directly, for example:
+
+       $ inigo avformat:file.mpeg
+
+       Would force the direct use of avformat for loading the file.
+
+
+Properties:
+
+       Properties can be assigned to the producer by adding additional name=value
+       pairs after the producer:
+       
+       $ inigo file in=50 out=100 something="something else"
+       
+       Note that while some properties have meaning to all producers (for example:
+       in, out and length are guaranteed to be valid for all, though typically, 
+       length is determined automatically), the validity of others are dependent on 
+       the producer - however, properties will always be assigned and silently 
+       ignored if they won't be used.
+
+
+Multiple Files:
+
+       Multiple files of different types can be used:
+       
+       $ inigo a.dv b.mpg c.png
+       
+       Properties can be assigned to each file:
+       
+       $ inigo a.dv in=50 out=100 b.mpg out=500 c.png out=500
+
+       MLT will take care of 'normalising' the output of a producer to ensure
+       that the consumer gets what it needs. So, in the case above, the mlt
+       framework will ensure that images are rescaled and audio resampled to meet
+       the requirements of your configuration (which, by default, will be PAL).
+       See 'Appendix A: Normalisation Rules' below.
+
+
+Filters:
+
+       Filters are frame modifiers - they can change the contents of the audio or
+       the images associated to a frame.
+
+       $ inigo a.dv -filter greyscale
+
+       As with producers, properties may be specified on filters too.
+       
+       Again, in and out properties are common to all, so to apply a filter to a
+       range of frames, you would use something like:
+       
+       $ inigo a.dv -filter greyscale in=0 out=50
+       
+       Again, filters have their own set of rules about properties and will
+       silently ignore properties that do not apply.
+
+
+Groups:
+
+       The -group switch is provided to force default properties on the following
+       'services'. For example:
+       
+       $ inigo -group in=0 out=49 clip*
+       
+       would play the first 50 frames of all clips that match the wild card
+       pattern.
+       
+       Note that the last -group settings also apply to the following filters, 
+       transitions and consumers, so:
+       
+       $ inigo -group in=0 out=49 clip* -filter greyscale
+       
+       is *probably not* what you want (ie: the greyscale filter would only be 
+       applied to the first 50 frames).
+       
+       To shed the group properties, you can use any empty group:
+       
+       $ inigo -group in=0 out=49 clip* -group -filter greyscale
+
+
+Attached Filters:
+
+       As described above, the -filter switch applies filters to an entire track. To
+       localise filters to a specific clip on a track, you have to know information
+       about the lengths of the clip and all clips leading up to it. In practise, 
+       this is horrifically impractical, especially at a command line level (and not
+       even that practical from a programing point of view...).
+
+       The -attach family of switches simplify things enormously. By default, -attach
+       will attach a filter to the last service created, so:
+
+       $ inigo clip1.dv clip2.dv -attach greyscale clip3.dv
+
+       would only apply the filter to clip2.dv. You can further narrow down the area of
+       the effect by specifying in/out points on the attached filter.
+
+       This might seem simple so far, but there is a catch... consider the following:
+
+       $ ingo clip1.dv -attach watermark:+hello.txt -attach invert
+
+       The second attached filter is actually attached to the watermark. You might 
+       think, yay, nice (and it is :-)), but, it might not be what you want. For example
+       you might want to attach both to clip1.dv. To do that, you can use:
+
+       $ ingo clip1.dv -attach-cut watermark:+hello.txt -attach-cut invert
+
+       As you shall see below, there are still another couple of gotchas associated to 
+       -attach, and even another variant :-).
+
+
+Mixes:
+
+       The -mix switch provides the simplest means to introduce transitions between
+       adjacent clips.
+
+       For example:
+
+       $ inigo clip1.dv clip2.dv -mix 25 -mixer luma -mixer mix:-1
+
+       would provide both an audio and video transition between clip1 and clip2.
+
+       This functionality supercedes the enforced use of the -track and -transition
+       switches from earlier versions of inigo and makes life a lot easier :-).
+
+       These can be used in combination, so you can for example do a fade from black
+       and to black using the following:
+
+       $ inigo colour:black out=24 clip1.dv -mix 25 -mixer luma \
+               colour:black out=24 -mix 25 -mixer luma 
+       
+       while this may not be immediately obvious, consider what's happening as the 
+       command line is being parsed from left to right:
+
+       Input:                  Track
+       ----------------------- -----------------------------------------------------
+       colour:black out=24     [black]
+       clip1.dv                [black][clip1.dv]
+       -mix 25                 [black+clip1.dv][clip1.dv]
+       -mixer luma             [luma:black+clip1.dv][clip1.dv]
+       colour:black out=24     [luma:black+clip1.dv][clip1.dv][black]
+       -mix 25                 [luma:black+clip1.dv][clip1.dv][clip1.dv+black]
+       -mixer luma             [luma:black+clip1.dv][clip1.dv][luma:clip1.dv+black]
+
+       Obviously, the clip1.dv instances refer to different parts of the clip, but 
+       hopefully that will demonstrate what happens as we construct the track.
+
+       You will find more details on the mix in the framework.txt.
+
+
+Mix and Attach:
+
+       As noted, -attach normally applies to the last created service - so, you can 
+       attach a filter to the transition region using:
+
+       $ inigo clip1.dv clip2.dv -mix 25 -mixer luma -attach watermark:+Transition.txt
+
+       Again, nice, but take care - if you want the attached filter to be associated
+       to the region following the transition, use -attach-cut instead.
+
+
+Splits, Joins, Removes and Swaps:
+
+       COMPLEX - needs simplification....
+
+
+Introducing Tracks and Blanks:
+
+       So far, all of the examples have shown the definition of a single
+       playlist, or more accurately, track.
+
+       When multiple tracks exist, the consumer will receive a frame
+       from the 'highest numbered' track that is generating a non-blank
+       frame.
+       
+       It is best to visualise a track arrangement, so we'll start with
+       an example:
+       
+       $ inigo a.dv -track b.dv in=0 out=49
+       
+       This can be visualised as follows:
+       
+       +------------------+
+       |a                 |
+       +-------+----------+
+       |b      |
+       +-------+
+
+       Playout will show the first 50 frames of b and the 51st frame shown will be
+       the 51st frame of a.
+
+       This rule also applies to audio only producers on the second track, for
+       example, the following would show the video from the a track, but the audio
+       would come from the second track:
+
+       $ inigo a.dv -track b.mp3 in=0 out=49
+
+       To have the 51st frame be the first frame of b, we can use the -blank switch:
+       
+       $ inigo a.dv out=49 -track -blank 49 b.dv
+       
+       Which we can visualise as:
+       
+       +-------+
+       |a      |
+       +-------+-------------------+
+               |b                  |
+               +-------------------+
+       
+       Now playout will continue as though a and b clips are on the
+       same track (which on its own, is about as useful as reversing the 
+       process of slicing bread).
+
+
+Transitions:
+
+       Where tracks become useful is in the placing of transitions.
+       
+       Here we need tracks to overlap, so a useful multitrack
+       definition could be given as:
+       
+       $ inigo a.dv out=49 \
+               -track \
+               -blank 24 b.dv \
+               -transition luma in=25 out=49 a_track=0 b_track=1
+       
+       Now we're cooking - our visualisation would be something like:
+       
+       +-------+
+       |a      |
+       +---+---+--------------+
+           |b                 |
+           +------------------+
+       
+       Playout will now show the first 25 frames of a and then a fade
+       transition for 25 frames between a and b, and will finally
+       playout the remainder of b.
+
+
+Reversing a Transition:
+
+       When we visualise a track definition, we also see situations
+       like:
+       
+       +-------+              +----------+
+       |a1     |              |a2        |
+       +---+---+--------------+----+-----+
+           |b                      |
+           +-----------------------+
+       
+       In this case, we have two transitions, a1 to b and b to a2. 
+       
+       In this scenario, we define a command line as follows:
+       
+       $ inigo a.dv out=49 -blank 49 a2.dv \
+               -track \
+               -blank 24 b.dv out=99 \
+               -transition luma in=25 out=49 a_track=0 b_track=1 \
+               -transition luma in=100 out=124 reverse=1 a_track=0 b_track=1
+
+
+Serialisation:
+
+       Inigo has a built in serialisation mechanism - you can build up
+       your command, test it via any consumer and then add a -serialise
+       file.inigo switch to save it.
+       
+       The saved file can be subsequently used as a clip by either
+       miracle or inigo. Take care though - paths to files are saved as
+       provided on the command line....
+
+       A more expressive serialisation can be obtained with the westley consumer
+       - this will provide an xml document which can be used freely in inigo and
+       miracle.
+
+       See westley.txt for more information.
+
+
+Missing Features:
+
+       Some filters/transitions should be applied on the output frame regardless
+       of which track it comes from - for example, you might have a 3rd text 
+       track or a watermark which you want composited on every frame, and of 
+       course, there's the obscure filter.... 
+       
+       inigo only supports this in two invocations - as a simple example:
+
+       $ inigo a.dv -track -blank 100 b.dv -consumer westley:basic.westley
+       $ inigo basic.westley -filter watermark:watermark.png
+
diff --git a/docs/install.txt b/docs/install.txt
new file mode 100644 (file)
index 0000000..b888322
--- /dev/null
@@ -0,0 +1,187 @@
+Installation Documentation
+
+Copyright (C) 2004 Ushodaya Enterprises Limited
+Author: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-04-13
+
+
+INSTALL
+-------
+
+       This document provides a description of the MLT project installation and
+       organisation.
+
+
+Directories
+-----------
+
+       The directory heirarchy is defined as follows:
+
+       + demo                  - A selection of samples to show off capabilities.
+       + docs                  - Location of all documentation
+       + src                   - All project source is provided here
+           + framework         - The mlt media framework
+           + modules           - All services are defined here
+               + avformat      - libavformat dependent services
+               + bluefish      - Bluefish dependent services (*)
+               + core          - Independent MLT services
+               + dv            - libdv dependent services
+               + fezzik        - A giant (meta) service to load and normalise media
+               + gtk2          - pango and pixbuf dependent services
+               + mainconcept   - mainconcept dependent services (*)
+               + normalize     - audio normalisation functions (**)
+               + plus          - throwaway silliness
+               + resample      - libresample dependent services (**)
+               + sdl           - SDL dependent services
+               + vorbis        - vorbis dependenent services
+               + westley       - Nice and clever XML services
+               + xine          - Xine-derived sources (**)
+           + albino            - A simple console (protocol level) example (**)
+           + inigo             - A media playing test application (**)
+           + humperdink        - A terminal-based example client (**)
+           + miracle           - The server implementation (**)
+           + tests             - Reserved for regression and unit tests
+           + valerie           - Client API to access the server
+
+       Additional subdirectories may be nested below those shown and should be
+       documented in their parent.
+
+       (*)  Not posted to CVS due to licensing issues.
+       (**) Contains GPL dependencies or code.
+
+
+Dependencies
+------------
+
+       The MLT core is dependent on:
+
+       * a C99 compliant C compiler
+       * posix threading
+       * standard posix libraries
+
+       The MLT applications and libraries provided are all dependent on the core.
+
+       The modules have the following dependencies:
+
+       ----------- ----------------------------------------------------------
+       MODULE      DESCRIPTION
+       ----------- ----------------------------------------------------------
+       avformat    Provided from ffmpeg CVS and compiled as a shared library.
+                   URL: http://ffmpeg.sf.net
+       ----------- ----------------------------------------------------------
+       bluefish    Bluefish hardware and software development kit
+                   URL: http://www.bluefish444.com
+       ----------- ----------------------------------------------------------
+       dv          libdv 0.102 or later.
+                   URL: http://libdv.sf.net
+       ----------- ----------------------------------------------------------
+       gtk2        GTK2 and associated dependencies.
+                   URL: http://www.gtk.org
+       ----------- ----------------------------------------------------------
+       mainconcept Mainconcept MPEG and DVCPRO Release SDKs.
+                   URL: http://www.mainconcept.com
+       ----------- ----------------------------------------------------------
+       resample    libsamplerate 0.15 or later 
+                   URL: http://www.mega-nerd.com/SRC/ (GPL)
+       ----------- ----------------------------------------------------------
+       sdl         SDL 1.2 or later.
+                   URL: http://www.libsdl.org
+       ----------- ----------------------------------------------------------
+       vorbis      libvorbis 1.0.1 or later.
+                   URL: http://www.vorbis.com/
+       ----------- ----------------------------------------------------------
+       westley     libxml2 2.5 or later.
+                   URL: http://www.xmlsoft.org/
+       ----------- ----------------------------------------------------------
+
+
+Configuration
+-------------
+
+       Configuration is triggered from the top level directory via a 
+       ./configure script.
+
+       Each source bearing subdirectory shown above have their own configure
+       script which are called automatically from the top level.
+
+       Typically, new modules can be introduced without modification to the 
+       configure script and arguments are accepted and passed through to all 
+       subdirectories.
+
+       More information on usage is found by running:
+
+           ./configure --help
+
+       NB: This script must be run to register new services after a CVS checkout
+       or subsequent update.
+
+       
+Compilation
+-----------
+
+       Makefiles are generated during configuration and these are based on
+       a per directory template which must be provided by the developer.
+
+
+Testing
+-------
+
+       To execute the mlt tools without installation, or to test a new version
+       on a system with an already installed mlt version, you should run:
+
+       . setenv
+
+       NB: This applies to your current shell only and it assumes sh or bash.
+
+
+Installation
+------------
+
+       The install is triggered by running make install from the top level
+       directory.
+       
+       The framework produces a single shared object which is installed in 
+       $prefix/lib/ and public header files which are installed in
+       $prefix/include/mlt/framework.
+
+       Valerie produces a single shared object which is installed in 
+       $prefix/lib/ and public header which are installed in 
+       $prefix/include/mlt/valerie.
+
+       Miracle produces a single exectuable which is installed in
+       $prefix/bin/, a library in $prefix/lib and associated header files in
+       $prefix/include.
+
+       The modules produce a shared object per module and update text files
+       containing a list of modules provided by this build. These are installed 
+       in $prefix/share/mlt/modules. It is at the discretion of the module to 
+       install additional support files. 
+       
+       To allow the development of external components, mlt-config and scripts 
+       are generated and installed in $prefix/bin.
+
+       After install, only those modules listed are usable by the server. No
+       module is loaded unless explicitly requested via server configuration
+       or usage.
+
+       External modules are also placed in this $prefix/share/mlt/modules, and the
+       installation of those must modify the text file accordingly before they
+       will be considered at runtime.
+
+
+Development
+-----------
+
+       All compilation in the project has {top-level-dir}/src on the include path. 
+       All headers are included as:
+
+           #include <framework/file.h>
+       
+       All external modules have {prefix}/include/mlt on the include path. All 
+       headers should also be included as:
+
+           #include <framework/file.h>
+
+       This allows migration of source between external and internal modules. 
+       The configuration and Makefile template requirements will require
+       attention though.
diff --git a/docs/policies.txt b/docs/policies.txt
new file mode 100644 (file)
index 0000000..f2fb033
--- /dev/null
@@ -0,0 +1,57 @@
+Open Source Development Policies and Procedures for MLT
+by Dan Dennedy
+
+Policies
+--------
+
+Any contribution to the "core" module must assign copyright to Ushodaya
+Enterprises Limited because they need license control over that module.
+
+The framework and valerie client libraries are covered under LGPL. Miracle,
+inigo, albino, and humperdink applications are covered under GPL. Modules
+should strive to be LGPL to make them available through the framework as LGPL.
+
+Comments in the framework and valerie header files must be C-style, not C++.
+
+Coding Style:
+There are not a lot of rules, but we prefer brackets on their own line,
+indents using tabs, liberal usage of spaces in statements and expressions, and
+no hard line length. The code in src/framework serves as a good example.
+
+Commit messages must be prefaced with the name of the changed files. This makes
+the Subversion log more useful as a ChangeLog. For example,
+    docs/policies.txt: added policy about commit message
+
+Increment the version number in ./configure AND mlt.h on the first commit after
+a release as well as just prior to a new release. This way we can track if
+someone is using an unreleased version from the source code repository.
+
+Do not write messages to stdout. stdout is reserved for writing streams that
+can be redirected: e.g. raw DV in consumer_libdv. I recommended to use the
+mlt_log API. For something quick and dirty, use stderr.
+
+
+Procedures
+----------
+
+Update services.txt when you add or update a service.
+
+Setting Copyright on Appropriated Code:
+You do not want to be accused of copying someone's code and changing copyright
+or license without permission. The license is straightforward: you must retain
+the original author's license unless you receive explicit permission. There are
+a few ways to approach the copyright declaration depending upon the
+intermingling and changes. If you heavily comingle original and new code or
+lightly modifiy the original code, you can retain the original's copyright
+including the years, and then add your copyright for the current year. If you
+can separate the MLT integration from the core subroutines, then you can put
+the core subroutines into a separate file with the original copyright and just
+copyright the MLT integration code as your own. However, if you have heavily
+modified the original code beyond nearly all recognition, you can copyright it
+as your own and attribute the original author as inspiration.
+
+Bug Reporting:
+First preference is to use the SourceForge tracker:
+http://sourceforge.net/tracker/?group_id=96039&atid=613414
+Second preference is in the mailing list:
+mlt-devel@lists.sourceforge.net
diff --git a/docs/services.txt b/docs/services.txt
new file mode 100644 (file)
index 0000000..63d6c5a
--- /dev/null
@@ -0,0 +1,1582 @@
+Service Documentation
+
+Authors: Charles Yates <charles.yates@pandora.be>
+         Dan Dennedy <dan@dennedy.org>
+Last Revision: $Date$
+
+
+SERVICES
+--------
+
+       Services marked as "(Proprietary)" are not distributed with the LGPL
+       version of mlt.
+
+Producers
+---------
+
+       avformat
+
+           Description
+
+               ffmpeg libavformat based producer for video and audio.
+
+           Constructor Argument
+
+               'file' - a filename specification or URL in the form:
+                        [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]]
+                        For example, video4linux:/dev/video1?width:320&height:240
+                        Note: on the bash command line, & must be escaped as '\&'.
+                        Also, note the use of ':' instead of '=' for parameters.
+                        Use 'ffmpeg -formats' to see a list of supported protocols 
+                        and formats.
+                        
+           Details
+           
+               Format parameters only appear to be useful with 'video4linux' or
+               'audio_device' formats. For 'video4linux' the parameters are
+               width, height, frame_rate, frame_rate_base, and standard (ntsc|pal).
+               For 'audio_device' the parameters are channels and sample_rate.
+
+           Initialisation Properties
+
+               int video_index - index of video stream to use (-1 is off)
+               int audio_index - index of audio stream to use (-1 is off)
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               string resource - file location
+               double source_fps - the framerate of the resource
+               double aspect_ratio - sample aspect ratio of the resource
+                                   - this is determined on every frame read
+
+           Dependencies
+
+               ffmpeg
+
+           Known Bugs
+
+               Audio sync discrepancy with some content.
+               Not all libavformat supported formats are seekable.
+               Fails to play beyond first frame of video of sources with PTS not
+               starting at 0 (video4linux).
+
+       fezzik
+
+           Description
+
+               A friendly giant that likes to rhyme and throw rocks
+
+           Constructor Argument
+
+               'file'    - a filename specification:
+                         [{mlt-service}:]{resource} | {mlt-service}
+                       - can also be the name of a producer service that can
+                         accept the resource specified post construction.
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+               + all producer initialising properties
+
+           Read Only Properties
+
+               string resource - file location
+               + all producer read only properties
+
+           Details
+
+               This producer has two roles:
+
+               1. it handles the mappings of all file names to the other
+               producers;
+               2. it attaches normalising filters (rescale, resize and resample)
+               to the producers (when necessary).
+
+               This producer simplifies many aspects of use. Essentially, it
+               ensures that a consumer will receive images and audio precisely as
+               they request them. 
+
+           Dependencies
+
+               all.
+
+           Known Bugs
+
+               None.
+
+       
+       colour
+       
+           Description
+           
+               A simple colour generator.
+               
+           Constructor Argument
+           
+               colour - A colour value is a hexadecimal representation of RGB plus
+                        alpha channel as 0xrrggbbaa.
+                      - Also colours can be the words: white, black, red, green,
+                        or blue.
+                      - The default colour is black.
+                      
+           Initialisation Properties
+           
+               none
+               
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               none
+               
+           Known Bugs
+           
+               none
+
+       
+       libdv
+
+           Description
+
+               libdv based decoder for video and audio.
+
+           Constructor Argument
+
+               'file'    - produce a/v from file
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               string resource - file location
+               double fps - output frames per second
+               int length - duration of resource (in frames)
+               
+           Mutable Properties
+           
+               string quality - one of "best," "fast" or anything else chooses
+                                medium.
+
+           Dependencies
+
+               libdv.
+
+           Known Bugs
+
+               DVCPRO is incorrectly identified as 16:9 aspect ratio. You must use
+               libdv from CVS or a post 0.101 release. 
+
+       mcdv (Proprietary)
+
+           Description
+
+               MainConcept based dv decoder for video and audio.
+
+           Constructor Argument
+
+               'file'    - produce a/v from file
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               string resource - file location
+               double fps - output frames per second
+               int length - duration of resource (in frames)
+
+           Dependencies
+
+               MainConcept DV or DVCPRO SDK, libdv.
+               "dv_sdk" installed parallel to mlt.
+
+           Known Bugs
+
+               None
+
+       mcmpeg (Proprietary)
+
+           Description
+
+               MainConcept based mpeg decoder for video and audio.
+
+           Constructor Argument
+
+               'file'    - produce a/v from file
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               string resource - file location
+               double fps - output frames per second
+               double aspect_ratio - sample aspect ratio of video
+               int length - duration of resource (in frames)
+               
+           Dependencies
+
+               MainConcept MPEG SDK.
+               "mpeg_sdk_release" installed parallel to mlt.
+
+           Known Bugs
+
+               None.
+
+       noise
+
+           Description
+
+               White noise producer
+
+           Constructor Argument
+
+               none
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               string resource - file location
+               double fps - output frames per second
+               double aspect_ratio - sample aspect ratio of video
+               int length - duration of resource (in frames)
+               
+           Dependencies
+
+               none
+
+           Known Bugs
+
+               none
+
+       pango
+
+           Description
+
+               A title generator that uses the Pango international text layout
+               and Freetype2 font renderer.
+
+           Constructor Argument
+
+               string file - a text file containing Pango markup, see:
+               http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
+                           - requires xml-like encoding special chars from: 
+                             <, >, &   -to-   &lt;, &gt;, &amp;
+                             
+           Details
+           
+               Supplying a filename with extension ".txt" causes the Fezzik 
+               producer to load with pango. If the filename begins with "+" the 
+               pango producer interprets the filename as pango text. This is a
+               shortcut to embed titles in inigo commands. For westley, it is 
+               recommended that you embed the title text in the property value.
+               
+               Pango has builtin scaling. It will rescale the originally rendered
+               title to whatever the consumer requests. Therefore, it will lose
+               its aspect ratio if so requested, and it is up to the consumer to
+               request a proper width and height that maintains the image aspect.
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               string markup - a string containing Pango markup see:
+               http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
+                             - requires xml-like encoding special chars from: 
+                               <, >, &   -to-   &lt;, &gt;, &amp;
+               string fgcolour - an RGBA colour specification of the text 
+                                 (i.e. 0xrrggbbaa)
+               string bgcolour - an RGBA colour of the background rectangle
+               string align - paragraph alignment: left, centre, right
+                            - also, numbers 0, 1 and 2 can be used respectively.
+               int pad - the number of pixels to pad the background rectangle 
+                         beyond edges of text. default 0.
+               string markup - see constructor argument
+               string text - non-markup string in UTF-8 encoding (can contain 
+                             markup chars un-encoded)
+               string font - the default typeface to use when not using markup.
+                             default "Sans 48". FreeType2 renders at 72 dpi.
+               string encoding - the text encoding type of the input if not UTF-8.
+                               - see 'iconv --list' for a list of possible inputs.
+                       int weight - the weight of the font (default is 400)
+
+           Read Only Properties
+
+               string resource - the text/markup file or "pango" if no file.
+               int real_width - the original, unscaled width of the rendered title.
+               int real_height - the original, unscaled height of the title.
+               int width - the last requested scaled image width.
+               int height - the last requested scaled image height.
+
+           Dependencies
+
+               libpango-1.0, libpangoft2-1.0, libfreetype, libgdk_pixbuf-2.0, 
+               libglib-2.0, libgobject-2.0, libgmodule-2.0, libfontconfig.
+
+           Known Bugs
+
+               The foreground and background Pango markup span attributes are not
+               supported.
+               Word wrapping is not supported.
+
+       pixbuf
+
+           Description
+
+               A still graphics to video generator using gdk-pixbuf
+
+           Constructor Argument
+
+               'file' - The name of a graphics file loadable by
+                        a gdk-pixbuf loader. see /usr/lib/gdk-pixbuf/loaders
+                        definitely png, jpeg, tiff, pnm, and xpm
+                      - If "%" in filename, the filename is used with sprintf
+                        generate a filename from a counter for multi-file/flipbook 
+                        animation. The file sequence ends when numeric 
+                        discontinuity >100.
+                      - If filename contains "/.all.", suffix with an extension to 
+                        load all pictures with matching extension from a directory.
+                      - If filename contains the string "<svg", then pixbuf tries
+                        to load the filename as inline SVG XML, which is convenient
+                        for inigo commands.
+                        
+           Details
+           
+               Pixbuf has builtin scaling. It will rescale the originally rendered
+               title to whatever the consumer requests. Therefore, it will lose
+               its aspect ratio if so requested, and it is up to the consumer to
+               request a proper width and height that maintains the image aspect.
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+               int begin  - when using an image sequence, this sets the starting
+                            number.
+
+           Mutable Properties
+
+               int ttl - how long (in frames) to repeat each picture in file 
+                         sequences. default is 25.
+
+           Read Only Properties
+
+               string resource - file location. See Constructor Argument above.
+               int real_width - the original, unscaled width of the rendered title.
+               int real_height - the original, unscaled height of the title.
+               int width - the last requested scaled image width.
+               int height - the last requested scaled image height.
+
+           Dependencies
+
+               libgdk_pixbuf-2.0, libglib-2.0, libgobject-2.0, libgmodule-2.0
+
+           Known Bugs
+
+               XXX: in/out settings are incorrectly handled.
+
+       ppm
+       
+           Description
+
+               Reads a stream of contiguous PPM images.
+               
+           Constructor Argument
+
+               command - a shell command to run something that produces ppm
+                         streams on stdout.
+               
+           Initialisation Properties
+
+               none
+               
+           Read Only Properties
+
+               string resource - the command
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               Since it uses pipes, it is not compatible with bluefish.
+
+       westley
+
+           Description
+
+               Construct a service network from an XML description.
+               See docs/westley.txt.
+
+           Constructor Argument
+
+               URL - an XML text file containing westley XML (schema/DTD pending)
+                   - Since westley files can be parameterised, the URL syntax is:
+                     {file-name}[?{param-name}{'='|':'}{param-value}[&{param-name}{'='|':'}{param-value}...]]
+                     A colon is allowed instead of an equal sign to pacify inigo,
+                     who tokenises anything with an equal sign as a property 
+                     setting. Also, when running inigo from the shell, beware of
+                     the '?' and shell filename expansion. You can surround the URL
+                     with single quotations to prevent expansion. Finally, fezzik
+                     will fail to match the filename when you use parameters, so
+                     preface the url with 'westley:' to force fezzik to load with
+                     the westley service.
+                     
+           Read Only Properties
+
+               string resource - file location
+
+           Dependencies
+
+               libxml2
+
+           Known Bugs
+
+               Non-referenced producers and playlists are not destroyed until the 
+               network is destroyed.
+               A referenced producer or playlist must appear before the reference.
+               A filter that occurs before a producer has been defined causes a 
+               segfault.
+
+       vorbis
+
+           Description
+
+               OGG Vorbis file reader.
+
+           Constructor Argument
+
+               'file' - file to use (only .ogg supported at the moment)
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               double fps - this is fixed at 25 for PAL currently
+
+           Dependencies
+
+               libvorbisfile
+
+           Known Bugs
+
+               Fixed frame size (PAL audio chunks).
+               Doesn't cover ogg files with multiple, differing sections.
+
+Filters
+-------
+
+       brightness
+       
+           Description
+           
+               Shift the luma component using a constant value.
+               
+           Constructor Argument
+           
+               start - the constant floating point numeric value to apply.
+                     - the default is 0.
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+               double start - see Constructor Argument above.
+               double end - the ending adjustment value. the filter interpolates
+                            between the start and end adjustments over the 
+                            duration of the effect.
+                            
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               none
+               
+           Known Bugs
+           
+               Does not go completely to black or white.
+               
+       
+       channelcopy
+       
+           Description
+           
+               Copy audio from one channel to another channel.
+               
+           Constructor Argument
+           
+               to - the 0-indexed channel to copy into, default is 1.
+               
+           Mutable Properties
+           
+               int to - see above
+               int from - the channel from which to copy, default is 0.
+               
+           Dependencies
+           
+               none
+               
+           Known Bugs
+           
+               none
+               
+
+
+       deinterlace
+       
+           Description
+
+               Deinterlace a frame consisting of two fields using bob, weave,
+               greedy, one-field, and linear blend methods. This code is 
+               appropriated from the Xine XV video output plugin.
+           
+           Constructor Argument
+
+               method - a string containing the deinterlace method: bob, weave,
+                        greedy, onefield, or linearblend. The default is
+                        linearblend.
+           
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+               
+           Read Only Properties
+
+               none
+           
+           Mutable Properties
+           
+               string method
+               
+           Details
+           
+               If the frame properties "progressive" or "consumer_progressive"
+               are non-zero, then the filter is not applied. Also, if applied,
+               this sets the frame property "progressive" to 1.
+
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               Not a bug, but it only provides fair quality.
+               
+           
+       gamma
+       
+           Description
+
+               Adjust image luma using a non-linear power-law curve
+               
+           Constructor Argument
+
+               gamma - a floating point value. The default is 1.0, or none.
+           
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               double gamma - the exponential factor of the power-law curve
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               none
+               
+       
+       greyscale
+       
+           Description
+
+               Convert colour image to greyscale
+               
+           Constructor Argument
+
+               none
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               none
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               none
+           
+       gtkrescale
+
+           Description
+
+               Scale the producer video frame size to match the consumer.
+               This filter is designed for use as a Fezzik normaliser.
+
+           Constructor Argument
+
+               interpolation - the rescaling method, one of:
+                   nearest (lowest quality, fastest),
+                   tiles,
+                   bilinear (default; good quality, moderate speed),
+                   hyper (best quality, slowest).
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               string interpolation - see constructor argument above
+
+               If a property "consumer_aspect_ratio" exists on the frame, then
+               rescaler normalises the producer's aspect ratio and maximises the
+               size of the frame, but may not produce the consumer's requested
+               dimension. Therefore, this option works best in conjunction with the
+               resize filter. This behavior can be disabled by another service by
+               either removing the property, setting it to zero, or setting
+               frame property "distort" to 1.
+
+           Dependencies
+
+               libgdk_pixbuf-2.0, libglib-2.0, libgobject-2.0, libgmodule-2.0
+
+           Known Bugs
+
+               none
+
+       jackrack
+       
+           Description
+           
+               Creates Jack ports and runs a JackRack project to process audio
+                       through a stack of LADSPA filters.
+               
+           Constructor Argument
+           
+               src - a JackRack file
+                       
+               Details
+               
+                       If you are using a consumer that uses ALSA, then you should start
+                       jackd with the dummy driver: jackd -d dummy.
+                       I also recommend using a period size of 2048: -p 2048.
+                       
+                       jackd -ddummy -r48000 -p2048
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+                       
+                       The following can be used without a rack file in order to connect
+                       filter_jackrack to a running instance of JackRack:
+                       string out_1 - Jack port to connect MLT's output port (JackRack's input)
+                       string out_2 - Jack port to connect MLT's output port (JackRack's input)
+                       string in_1 - Jack port to connect MLT's input port (JackRack's output)
+                       string in_2 - Jack port to connect MLT's input port (JackRack's output)
+               
+           Read Only Properties
+           
+               none
+               
+           Mutable Properties
+
+                       none
+               
+           Dependencies
+           
+               Jack, LADSPA, glib-2.0, libxml2
+               
+           Known Bugs
+           
+                       Currently, due to timing and synchronisation issues, the audio
+                       is distorted with repeated samples and latency clicks.
+               no encapsulated resampling and jack runs at a fixed frequency
+           
+       ladspa
+       
+           Description
+           
+               Runs a JackRack project to process audio through a stack of
+                       LADSPA filters without using Jack.
+               
+           Constructor Argument
+           
+               src - a JackRack file
+                       
+               Details
+
+                       Due to audio integrity issues with the jackrack filter, this
+                       filter is better for runtime, while jackrack is more suitable
+                       for prototyping a rack file using the Jack Rack GUI.
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+                       
+           Read Only Properties
+           
+               none
+               
+           Mutable Properties
+
+                       none
+               
+           Dependencies
+           
+               Jack, LADSPA, glib-2.0, libxml2
+                       Jack is still required because this coexists with the jackrack 
+                       filter.
+               
+           Known Bugs
+           
+               no encapsulated resampling and jack runs at a fixed frequency
+           
+       luma
+       
+           Description
+           
+               Applies a luma transition between the current and next frames.
+               Useful for transitions from a slideshow created using producer
+               pixbuf.
+               
+           Constructor Argument
+           
+               file - a luma wipe
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+               
+           Read Only Properties
+           
+               none
+               
+           Mutable Properties
+           
+               int period - how long to wait between success iterations of the
+                            transition. For best results set this to a multiple
+                            of ttl used in pixbuf. The default is 24.
+                            
+               luma. - all properties beginning with "luma." are passed to the
+                       encapsulated luma transition. For example, luma.out controls
+                       the duration of the wipe.
+               
+           Dependencies
+           
+               transition_luma and its dependencies
+               
+           Known Bugs
+           
+               none
+           
+       mcrescale
+
+           Description
+
+               Scale the producer video frame size to match the consumer.
+               This filter is designed for use as a Fezzik normaliser.
+
+           Constructor Argument
+
+               interpolation - the rescaling method, one of:
+                   nearest (lowest quality, fastest),
+                   bilinear (default; good quality, moderate speed),
+                   hyper (best quality, slowest).
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               string interpolation - see constructor argument above
+
+               If a property "consumer_aspect_ratio" exists on the frame, then
+               rescaler normalises the producer's aspect ratio and maximises the
+               size of the frame, but may not produce the consumer's requested
+               dimension. Therefore, this option works best in conjunction with the
+               resize filter. This behavior can be disabled by another service by
+               either removing the property, setting it to zero, or setting
+               frame property "distort" to 1.
+
+           Dependencies
+
+               the mainconcept rescaling sdk.
+
+           Known Bugs
+
+               none
+       
+       mirror
+       
+           Description
+           
+               Provides various mirror and image reversing effects.
+
+           Constructor Argument
+           
+               mirror - horizontal, vertical, diagonal, flip, flop
+               
+           Initialisation Properties
+           
+               int reverse - being 0 or 1
+               int in - in point
+               int out - out point
+               
+           Read Only Properties
+           
+               none
+               
+           Mutable Properties
+           
+               none
+
+           Dependencies
+           
+               none
+               
+           Known Bugs
+           
+               none
+
+
+       mono
+       
+           Description
+           
+               Mix all channels of audio into a mono signal and output it as
+                       N channels.
+
+           Constructor Argument
+           
+               channels - the number of output channels (default 2)
+               
+           Initialisation Properties
+           
+               none
+               
+           Read Only Properties
+           
+               none
+               
+           Mutable Properties
+           
+               none
+
+           Dependencies
+           
+               none
+               
+           Known Bugs
+           
+               none
+
+
+       obscure
+       
+           Description
+
+               Obscuring filter.
+               
+           Constructor Argument
+
+               none
+               
+           Initialisation Properties
+           
+               string start - in the format X,Y:WxH[:PWxPY]
+                            - PWxPY is the size of the averaging region in pixels.
+               string end - in the format X,Y:WxH[:PWxPY]
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               none
+               
+           Dependencies
+
+               none
+
+           Known Bugs
+
+               none
+
+       region
+       
+           Description
+           
+               Apply one or more filters to a region of the video image. The region
+               can be shaped as well using the alpha channel of another producer.
+               
+           Constructor Argument
+           
+               resource - A file whose alpha channel will "shape"  the region.
+                    - The string "circle" is a shortcut but it requires pixbuf with
+                      the librsvg loader. The circle is automatically stretched
+                      to the region to create an ellipse.
+       
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+               plus and region transition properties
+               
+           Read Only Properties
+           
+               see the region transition
+               
+           Dependencies
+           
+               transition_region
+               
+           Known Bugs
+           
+               "circle" is unpredictable in the absence of the librsvg pixbuf loader.
+               
+
+       resample
+
+           Description
+
+               Adjust an audio stream's sampling rate, and duplicate channels if 
+               producer provides less than consumer requested.
+               
+               This filter is automatically invoked by Fezzik for the sake of
+               normalisation over inputs and with the consumer.
+
+           Constructor Argument
+
+               frequency - a numeric value for the new sample rate
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               int frequency - the target sample rate
+
+           Dependencies
+
+               libresample
+
+           Known Bugs
+
+               Assumes 2 channels during libsamplerate initialisation. Untested
+               with >2 channels.
+
+       rescale
+
+           Description
+
+               Scale the producer video frame size to match the consumer.
+               This filter is designed for use as a Fezzik normaliser.
+
+           Constructor Argument
+
+               None.
+
+           Initialisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               If a property "consumer_aspect_ratio" exists on the frame, then
+               rescaler normalises the producer's aspect ratio and maximises the
+               size of the frame, but may not produce the consumer's requested
+               dimension. Therefore, this option works best in conjunction with the
+               resize filter. This behavior can be disabled by another service by
+               either removing the property, setting it to zero, or setting
+               frame property "distort" to 1.
+
+           Dependencies
+
+               none
+
+           Known Bugs
+
+               none but... it only implements a nearest neighbour scaling - it is
+                       used as the base class for the gtkrescale and mcrescale filters.
+
+       resize
+       
+           Description
+
+               Image scaling and padding and field order adjustment.
+                   
+           Details
+           
+               Normally resize is used to pad the producer's
+               output to what the consumer has requested after an upstream rescale
+               filter first scales the image to maximise usage of the image area.
+               This filter also adjusts the field order to lower field first
+               if the frame property "top_field_first" has been set to 1.
+               Therefore, when done, it sets the top_field_first to 0.
+               This filter is automatically invoked by Fezzik as part of image
+               sample aspect ratio normalisation.
+               
+           Constructor Argument
+
+               scale - "affine" to use affine transform scaling, otherwise
+                        center and pad.
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+
+           Read Only Properties
+
+               none
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               Assumes lower field first output.
+
+       volume
+
+           Description
+
+               Adjust an audio stream's volume level 
+                               - based on the 'normalize' utility
+               
+           Constructor Argument
+
+               gain - a string containing one of:
+                    - a floating point value of the gain adjustment
+                    - a numeric value with the suffix "dB" to adjust in terms of decibels
+                    - "normalise" to normalise the volume to the target amplitude -12dBFS
+               
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+               int window - the number of video frames over which to smooth normalisation.
+                            defaults to 75.
+
+           Mutable Properties
+
+               string gain - same as constructor argument above
+
+               string normalise - normalise the volume to the amplitude:
+                   - a numeric value with the suffix "dB" to set amplitude in decibels
+                   - a floating point value of the relative volume
+                   - an unspecified value uses the default -12dBFS
+
+               string limiter - limit all samples above:
+                   - a numeric value with the suffix "dB"
+                   - a floating point value ( dB = 20 * log10(x) )
+                   - an unspecified value uses the default -6dBFS
+
+               double max_gain - a floating point or decibel value of the maximum gain that
+                   can be applied during normalisation.
+                   - an unspecified value uses the default 20dB
+
+               string end - a gain value just like the gain property above.
+                            This causes the gain to be interpolated from 'gain' to 'end'
+                            over the duration.
+
+               int window - the size of the normalising smoothing buffer in video frame units.
+                          - the smoothing buffer prevents erratic gain changes.
+                          - the default value is 75 video frames. 
+
+               gain can be applied as a factor to the normalise amplitude!
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+
+               none
+
+       watermark
+
+           Description
+
+               Add a watermark to the frames.
+
+           Constructor Argument
+
+               resource - the producer to use (ie: a .png)
+
+           Initialisation Properties
+
+               string resource - the producer to use
+               string factory - producer required for the resource ('fezzik')
+               string geometry - composite geometry
+               string distort - control scaling
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               none
+
+           Dependencies
+
+               mlt core modules and optionally, fezzik
+
+           Known Bugs
+
+               none
+
+Transitions
+-----------
+
+       composite
+       
+           Description
+
+               A key-framable alpha-channel compositor for two frames.
+               
+           Details
+           
+               Performs dissolves and luma wipes in addition to alpha compositing.
+               
+               By default, the aspect ratio of the B frame is respected and the
+               size portion of the geometry specification simply defines a 
+               bounding rectangle.
+           
+               This performs field-based rendering unless the A frame property
+               "progressive" or "consumer_progressive" or the transition property
+               "progressive" is set to 1.
+               
+           Constructor Argument
+
+                       none[*]
+
+           Initialisation Properties
+               
+               int in - in point
+               int out - out point
+               string factory - The name of a factory service used as a non-PGM
+                                producer loader. The default is fezzik.
+       
+           Read Only Properties
+
+               none
+
+           Mutable Properties
+
+
+                       string geometry - key frame specification
+                                                       - this is a ; delimited form of the deprecated start,
+                                                         key[n], end properties
+               int progressive - set to 1 to disable field-based rendering.
+               string distort - when set, causes the B frame image to fill the WxH
+                                completely with no regard to B's aspect ratio.
+               string halign - when not distorting, set the horizontal alignment
+                               of B within the geometry rectangle.
+                             - one of: left (0), centre (1), or right (2).
+                             - the default is left.
+               string valign - when not distorting, set the vertical alignment of 
+                               B within the geometry rectangle.
+                             - one of: top (0), middle (1), or bottom (2).
+                             - the default is top.
+               string luma - the luma map file name. If not supplied, a dissolve.
+               double softness - only when using a luma map, how soft to make the
+                                 edges between A and B. 0.0 = no softness. 1.0 =
+                                 too soft.
+               Any property starting with "luma." is passed to the non-PGM luma
+                   producer.
+
+               Deprecated Properties
+
+               string start - a geometry specification as X,Y:WxH[!][:mix]
+                            - X, Y, W, H are assumed to pixel units unless they
+                              have the suffix '%'
+                            - '!' is a shortcut to specify distort, see below.
+                            - mix is always a 2 digit percentage, defaults to 100.
+                            - default is "85%,5%:10%x10%"
+               string end - the ending size and position.
+               string key[F] - X,Y:WxH[:mix] - set a key frame for geometry between
+                               the in and out. F is a frame number and can be
+                               negative to make it relative to the out point.
+
+           Dependencies
+
+               none
+           
+           Known Bugs
+
+               Assumes lower field first during field rendering.
+
+       luma
+       
+           Description
+
+               A generic dissolve and wipe transition processor.
+
+           Details
+
+               luma gets its name
+               from how it uses a grayscale "map" file. As the luma value varies
+               over time, a threshold filter is applied to the map to determine
+               what parts of frame A vs. frame B to show. It reads PGM files
+               up to 16 bits! Alternatively, it can use the first frame from any
+               producer that outputs yuv, but it will be limited to the luma
+               gamut of 220 values.
+               This performs field-based rendering unless the A frame property
+               "progressive" or "consumer_progressive" or the transition property
+               "progressive" is set to 1.
+           
+           Constructor Argument
+
+               string resource - the luma map file name - either PGM or any other
+                                 producable video. 
+                               - If not supplied, a dissolve.
+
+           Initialisation Properties
+           
+               int in - in point
+               int out - out point
+               string factory - The name of a factory service used as a non-PGM
+                                producer loader. The default is Fezzik.
+
+           Mutable Properties
+
+               string resource - same as above
+               double softness - only when using a luma map, how soft to make the
+                                 edges between A and B. 0.0 = no softness. 1.0 =
+                                 too soft.
+               int reverse - reverse the direction of the transition.
+               Any property starting with "producer." is passed to the non-PGM luma
+                   producer.
+               
+           Read Only Properties
+
+               none
+               
+           Dependencies
+
+               none
+               
+           Known Bugs
+               
+               Assumes lower field first output.
+
+       mix
+
+           Description
+
+               An two stream audio mixer.
+
+           Constructor Argument
+
+               start - see below
+
+           Initalisation Properties
+
+               int in - in point
+               int out - out point
+
+           Mutable Properties
+
+               double start - the mix level to apply to the second frame.
+                            - any negative value causes an automatic crossfade from 0 to 1.
+               double end - the ending value of the mix level. mix level will be interpolated
+                            from start to end over the in-out range.
+               int reverse - set to 1 to reverse the direction of the mix.
+
+           Read Only Properties
+
+               none
+
+           Dependencies
+
+               none
+
+           Known Bugs
+
+               Samples from the longer of the two frames are discarded.
+               
+       
+       region
+       
+           Description
+           
+               Apply zero or more filters to B frame as it is composited onto
+               a region of the A frame. The "shape" of the region can be defined
+               by the alpha channel of a third producer.
+               
+           Constructor Argument
+
+               resource - a shape producer
+           
+           Initialisation Properties
+           
+               string resource - nothing is rectangle, "circle" is a pixbuf-
+                   generated SVG circle, anything else is loaded by the factory.
+               string factory - the service that creates the shape producer.
+                              - the default is fezzik.
+               string filter[N] - one or more filters to apply. All filter
+                                  properties are passed using the same filter "key"
+               Any property starting with "composite." is passed to the 
+                   encapsulated composite transition.
+               
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               transition_composite
+               
+           Known Bugs
+           
+               none
+               
+
+Consumers
+---------
+
+       avformat
+       
+           Description
+           
+               Multiformat transcoding consumer.
+
+           Constructor Argument
+
+               string target - the filename to write to, e.g. test.mpeg.
+
+           Initialisation Properties
+           
+               int buffer - the number of frames to buffer, minimum 1, default 25.
+               string rescale - a rescale method, see the Filters/rescale.
+               int progressive - indicates whether to use progressive or field-
+                                 based rendering, default 0 (off).
+               
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               libavformat
+               
+           Known Bugs
+           
+               Plenty.
+
+       bluefish (Proprietary)
+       
+           Description
+
+               BlueFish444 audio and video output module.
+           
+           Constructor Argument
+
+               card - a numeric card id starting at 1, default is 1.
+               
+           Initialisation Properties
+
+               string standard - "PAL" (default) or "NTSC"
+                               - default is based upon MLT_NORMALISATION 
+                                 environment variable, which defaults to PAL.
+               int frames - the number of DMA video frames. default is 8.
+                            minimum is 2. maximum on my system is 11.
+               int buffer - the number of frames to buffer within MLT, minimum 1, 
+                            default 25.
+               string rescale - a rescale method, see the Filters/rescale.
+               
+           Read Only Properties
+
+               none
+           
+           Dependencies
+
+               BlueVelvet SDK installed parallel to mlt in "bluefish."
+           
+           Known Bugs
+
+               Does not work with any service that uses pipes!
+               
+               If mlt crashes, you might need to reload the BlueDriver kernel 
+               module due to unreleased DMA buffers.
+               
+               Only supports 2 channel audio at the moment.
+           
+       libdv
+       
+           Description
+           
+               libdv dv producer.
+
+           Constructor Argument
+
+               string target - the filename to write to, e.g. /dev/dv1394.
+
+           Initialisation Properties
+           
+               int buffer - the number of frames to buffer, minimum 1, default 25.
+               string rescale - a rescale method, see the Filters/rescale.
+           
+           Mutable Properties
+           
+               int progressive - indicates whether to use progressive or field-
+                                 based rendering, default 0 (off).
+               
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               libdv
+               
+           Known Bugs
+           
+               none
+
+       mcmpeg
+       
+           Description
+           
+               Mainconcept MPEG encoder.
+
+           Constructor Argument
+
+               string target - the filename to write to.
+
+           Initialisation Properties
+           
+               int buffer - the number of frames to buffer, minimum 1, default 25.
+               string rescale - a rescale method, see the Filters/rescale.
+               string format - vcd [default], svcd or dvd provide base settings
+               int motion_search_type - 0 to 16 - reduces quality/cpu usage
+               int gop - group of picture size (default: format dependent)
+
+           Mutable Properties
+           
+               int progressive - indicates whether to use progressive or field-
+                                 based rendering, default 0 (off).
+               
+           Read Only Properties
+           
+               none
+               
+           Dependencies
+           
+               Mainconcept MPEG SDK
+               
+           Known Bugs
+           
+               none
+
+       sdl
+
+           Description
+
+               Simple DirectMedia Layer audio and video output module.
+               
+           Constructor Argument
+
+               string video_standard - "PAL" (default), "NTSC", or "WxH"
+               
+           Initialisation Properties
+           
+               int buffer - the number of frames to buffer, minimum 1, default 25.
+               string rescale - a rescale method, see the Filters/rescale.
+                              - Hint: "none" works very good with SDL output.
+               
+           Mutable Properties
+
+               double volume - audio level factor
+               int video_off - if 1, disable video output
+               int audio_off - if 1, disable audio output
+                       int resize - TODO
+               int progressive - indicates whether to use progressive or field-
+                                 based rendering, default 0 (off).
+               int audio_buffer - size of the sdl audio buffer (default: 1024)
+               
+           Read Only Properties
+
+               none
+               
+           Dependencies
+
+               libSDL-1.2, libasound, libaudio, libXt, libX11, libXext, libSM, libICE
+           
+           Known Bugs
+
+               none
+
+       westley
+
+           Description
+
+               Serialise the service network to XML.
+               See docs/westley.txt for more information.
+               
+           Constructor Argument
+
+               resource - the name of a file in which to store the XML.
+                         - stdout is default.
+               
+           Initialisation Properties
+
+               string resource - same as above.
+               
+           Dependencies
+           
+               libxml2
+               
+           Known Bugs
+
+               Untested arbitrary nesting of multitracks and playlists.
+               Property "id" is generated as service type followed by number if
+               no property named "id" exists, but it fails to guarantee uniqueness.
diff --git a/docs/testing-20040110.txt b/docs/testing-20040110.txt
new file mode 100644 (file)
index 0000000..14c8dcd
--- /dev/null
@@ -0,0 +1,35 @@
+On 1/10/2004, Dan Dennedy ran the testing.txt against mlt albino and miracle.
+
+
+NOTE: Discrepancies cited here may have impact on related functionality.
+
+
+General
+------------------------------------------------------------------------------
+Server side error checks and related response error codes are not stringently enforced.
+
+
+Not Implemented
+------------------------------------------------------------------------------
+NLS
+USET points=ignore
+USET eof=terminate
+
+
+Incorrect Behaviour
+------------------------------------------------------------------------------
+
+
+Different Intentional Behaviour
+------------------------------------------------------------------------------
+
+Different forced Behaviour
+------------------------------------------------------------------------------
+killall miracle does not work when the SDL consumer is in use. requires killall -HUP
+
+MLT Bugs
+------------------------------------------------------------------------------
+Please check the services.txt doc for known bugs related to MLT components.
+
+
+
diff --git a/docs/testing.txt b/docs/testing.txt
new file mode 100644 (file)
index 0000000..2886989
--- /dev/null
@@ -0,0 +1,599 @@
+Miracle Test Procedure
+
+Copyright (C) 2003 Ushodaya Enterprised Limited
+Author: Dan Dennedy <dan@dennedy.org>
+Last Revision: 2004-03-20
+
+
+NOTE: THIS DOCUMENT REQUIRES REVISION TO NEW, EXPECTED BEHAVIOR FROM MIRACLE.
+
+Tests are divided into 9 sections:
+
+   1. Command Line Usage
+   2. Unit Management
+   3. Server Configuration
+   4. Simple Playback
+   5. Multi-unit Playback
+   6. Unit Configuration
+   7. Advanced Playback
+   8. Bus Reset
+   9. Server Side Queuing
+
+Each section contains many tests which I've divided into a minimum of two lines:
+
+n.m action to carry out
+--> expected result
+
+Further lines may appear to show the actual results when they deviate from what
+I expected or if there are special cases to consider.
+
+Sequential tests are indicated as:
+
+n.m.o action to carry out
+--> expected result
+
+It is suggested that you run top during the testing and note cpu hikes
+or any excessive memory usage related to an operation.
+
+
+0. Introduction
+---------------
+
+The tests following are by no means exhaustive, but they should cover typical
+use cases - creativity is encouraged with more cases being added where necessary.
+This document should also be maintained to dictate actual state, especially with
+regard to a final release. Unit test cases are encouraged, but are excluded from
+this document.
+
+It is important to carry out the full test cycle when preparing a final release.
+In this situation, please resist the temptation to bug fix a given test case and
+resume the tests from that point onward - it is better to repeat from the
+beginning (but you can of course employ common sense in this situation).
+
+Before starting the final tests, please delete/backup your current
+/etc/dv139d.conf file. This (more or less) ensures that tests are carried out
+for a virgin install.
+
+
+1. Command Line Usage
+---------------------
+
+Run these from the top level project directory
+
+1.1.0 Start miracle in interactive mode: src/miracle/miracle -test
+--> miracle starts interactively and reports:
+(5) Starting server on 5250.
+(5) miracle version 0.0.1 listening on port 5250
+
+1.1.1 Stop the server by pressing Ctrl-C
+--> miracle returns the following and returns control to the console:
+(5) miracle version 0.0.1 server terminated.
+
+1.2.2 Start miracle as a daemon: src/miracle/miracle
+--> control returns to the console
+
+1.2.3 Verify miracle is running: ps ax
+--> several miracle processes are running
+
+1.2.4 Verify successful miracle startup using syslog: sudo tail /var/log/syslog
+--> miracle: miracle version 0.0.1 listening on port 5250
+
+1.2.5 Verify connectivity on port 5250: telnet localhost 5250
+--> 100 VTR Ready
+
+1.2.6 Test clean disconnect: BYE
+--> Connection closed by foreign host.
+
+1.2.7 Stop the daemon: killall miracle
+--> no errors
+
+1.2.8 Verify a clean server shutdown: sudo tail /var/log/syslog
+--> miracle: shutdown complete.
+
+1.3.0 Start miracle on a different port: src/miracle/miracle -port 5260
+
+1.3.1 Verify successful miracle startup using syslog: sudo tail /var/log/syslog
+--> miracle: miracle version 0.0.1 listening on port 5260
+
+1.3.2 Verify connectivity on port 5260: telnet localhost 5260
+--> 100 VTR Ready
+
+1.3.3 Test clean disconnect: BYE
+--> Connection closed by foreign host.
+
+1.3.4 Stop the daemon: killall miracle
+--> no errors
+
+
+2. Unit Management
+------------------
+
+Start the miracle server and connect to it with telnet or a protocol-
+level client (albino). 
+
+2.1 List the AV/C devices on the bus: NLS
+--> 201 OK
+--> one or more lines listing devices with GUID in second column
+
+2.2 Add a device as a miracle unit: UADD {sdl, bluefish}
+--> 201 OK
+--> U0
+
+2.3 List the units: ULS
+--> 201 OK
+--> U0 ?? {sdl, bluefish} 1
+--> It is important that the last column is '1' to indicate it is online.
+
+2.4 List the units: ULS
+--> 201 OK
+--> U0 ?? {sdl, bluefish} 1
+
+2.5 Attempt unit commands for a unit that does not exist: LOAD U6 foo
+--> 403 Unit not found
+
+2.6 Attempt unit commands without specifying a unit: PLAY
+--> 402 Argument missing
+
+2.7 Attempt unit commands for a unit: PLAY U0
+--> 200 OK
+
+2.8.0 Load a clip into an unit: LOAD U0 test.dv
+--> 200 OK
+
+2.7.1 Verify the status of the unit: USTA U0
+--> 202 OK
+--> 0 online "test.dv" 0 1000 25.00 0 ...
+--> only the first 3 columns are relevant in this test
+
+
+3. Server Configuration
+-----------------------
+
+Start miracle if not already started from a previous test.
+
+3.1 Get the hard-coded default root property value: GET root
+--> 202 OK
+--> /
+
+3.2 List the files and subdirectories at the root: CLS /
+--> 201 OK
+--> "bin/"
+--> ...
+
+3.3 Change the server root to a place where clips are stored: e.g.,
+    SET root=/tmp
+--> 200 OK
+
+3.4 Get the new value of the root property value: GET root
+--> 202 OK
+--> /tmp/
+--> Notice that if you did not use a trailing slash in step 2.3, one is
+    added for you and appears in this step. This is normal and correct.
+
+3.5 List the files and subdirectories at the root: CLS /
+--> 201 OK
+--> zero or more lines listing subdirectories followed by files.
+
+3.6 Try to set a property that does not exist: SET foo=bar
+--> 405 Argument value out of range
+
+3.7 Try to set no property or value: SET
+--> 402 Argument missing
+
+3.8 Attempt a bogus command: FOO
+--> 400 Unknown command
+
+XXX 3.9 Attempt the incorrect case for a command: get root
+XXX --> 400 Unknown command
+
+3.10 Attempt case insensitivity of property key: GET Root
+--> 202 OK
+--> /tmp/
+
+
+4. Simple Playback
+-------------------
+
+Start miracle or restart if already started.
+Add an online unit.
+Set the server root property if desired.
+
+4.1.0 Load a clip into the unit: LOAD U0 test.dv
+--> 200 OK
+
+4.1.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 stopped "test.dv" 0 1000 25.00 0 ...
+--> Only the first 3 columns are relevant in this test.
+--> The remaining columns are only relevant to the tester.
+
+4.2.0 Play the clip: PLAY U0
+--> 200 OK
+--> Verify audio and video output
+
+4.2.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 1739 1000 25.00 0 ...
+--> Only the first 3 columns are relevant in this test.
+--> The remaining columns are only relevant to the tester.
+
+4.3.0 Pause playback: PAUSE U0
+--> 200 OK
+--> Verify video continues, but audio is muted.
+
+4.3.1 Check the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 1739 0 25.00 0 ...
+--> The fifth column --------^ should be 0; it indicates speed.
+
+4.3.2 Stop playback: STOP U0
+--> 200 OK
+--> The analog video output stops
+
+4.3.3 Pause playback: PAUSE U0
+--> 200 OK
+--> Analog video starts again, but it is held on the same frame
+    paused in 4.3.0.
+
+4.3.4 Stop playback: STOP U0
+--> 200 OK
+--> The analog video signal ceases.
+
+4.3.5 Rewind the unit: REW U0
+--> 200 OK
+
+4.3.6 Play the unit: PLAY U0
+--> 200 OK
+--> Analog audio and video are produced from the beginning of the file.
+
+4.4 Stop the server during playback and ensure clean shutdown.
+
+
+5. Multi-unit Playback
+-----------------------
+
+Start miracle or restart if already started.
+Add *2* online units.
+Set the server root property if desired.
+
+5.1.0 Load a clip into one unit: LOAD U0 test.dv
+--> 200 OK
+
+5.1.1 Load a clip into the other unit: LOAD U1 test.dv
+--> 200 OK
+
+5.1.2 Start playing one unit: PLAY U0
+--> 200 OK
+--> Verify audio and video output
+
+5.1.3 Start playing the other unit: PLAY U1
+--> 200 OK
+--> Verify audio and video output of both units
+
+5.2 Verify independence of units by pausing one of them: PAUSE U0
+--> 200 OK
+--> Verify video continues, but audio is muted on the first unit only.
+
+5.3 Stop the server during multi-unit playback and ensure clean shutdown.
+
+
+6. Advanced Playback
+--------------------
+
+Start miracle or restart if already started.
+Add *2* online units.
+Set the server root property if desired.
+
+Trick play modes:
+
+6.1.0 Load a clip: LOAD U0 test.dv
+--> 200 OK
+
+6.1.1 Start playback by pausing on the first frame: PAUSE U0
+--> 200 OK
+--> analog video starts, but audio is muted.
+
+6.1.2 Play fast forward: FF U0
+--> 200 OK
+--> verify video is playing fast in the forward direction.
+
+6.1.3 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 219 2000 25.00 0 ...
+--> The important column is --^, indicates speed
+
+6.1.4 Play fast reverse: REW U0
+--> 200 OK
+--> verify analog video output is fast in the reverse direction.
+
+6.1.5 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 4621 -2000 25.00 0 ...
+--> The important column is ---^, negative mean reverse
+
+6.1.6 Play slow forward: PLAY U0 500
+--> 200 OK
+--> Verify the analog video output is slow in the forward direction.
+
+6.1.7 Play reverse normal speed: PLAY U0 -1000
+--> 200 OK
+--> Verify the analog video output is at a normal speed in the reverse direction.
+--> Audio output is reverse, but not the field order of video
+
+Loading while playing:
+
+6.2.0 Stop the unit (might be playing): STOP U0
+--> 200 OK
+
+6.2.1 Rewing the unit: REW U0
+--> 200 OK
+
+6.2.2 Start playing: PLAY U0
+--> 200 OK
+--> verify analog audio and video output
+
+6.2.3 Load another clip: LOAD test002.dv
+--> 200 OK
+--> playback seamlessly switches to the new clip
+--> verify the analog appearance of the video makes a clean switch
+
+6.2.4 Load another clip, this time with in and out points:
+      LOAD test.dv 100 500 (whatever works for your test footage)
+--> 200 OK
+--> verify the analog appearance of the video makes a clean switch
+
+6.2.5 Get unit status: USTA U0
+--> 202 OK
+--> 0 playing "test.dv" 403 1000 25.00 100 ...
+--> verify position -----^ is beyond --^ in point, last column is the out
+    point specified in the previous step.
+
+Edit points:
+
+6.3.0 Load a clip: LOAD U0 test.dv
+--> 200 OK
+
+6.3.1 Pause the playback unit: PAUSE U0
+--> 200 OK
+
+6.3.2 Set the in point: SIN U0 100
+--> 200 OK
+--> verify the frame displayed in analog video out changes
+
+6.3.4 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 100 ...
+--> verify position ---^ and in ---^
+
+6.3.5 Change the mode of the unit to not restrict playback to the edit
+      region: USET U0 points=ignore
+--> 200 OK
+
+6.3.6 Jump to a frame before the in frame: GOTO U0 50
+--> 200 OK
+
+6.3.7 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 50 0 25.00 100 ...
+--> position ----------^ preceeds -^ (in)
+
+6.3.8 Set the unit mode to restrict playback to the edit region: USET U0 points=use
+--> 200 OK
+--> verify frame on analog video output changes
+
+6.3.9 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 100 ...
+--> verify position ---^ and in ---^
+
+6.3.10 Clear the in point: SIN U0 -1
+--> 200 OK
+
+6.3.11 Get the unit status: USTA U0
+--> 202 OK
+--> 0 paused "test.dv" 100 0 25.00 0 ...
+--> verify the in point is reset --^
+
+The above sequence should be repeated in a similar manner for the out point
+using the SOUT command.
+
+Transfer:
+
+6.4.0 Load a clip into the first unit: LOAD U0 test.dv
+--> 200 OK
+
+6.4.1 Load a clip into the second unit: LOAD U1 test002.dv
+--> 200 OK
+
+6.4.2 Start playing the first unit: PLAY U0
+--> 200 OK
+--> verify audio and video analog output
+
+6.4.3 Set an in point on the clip in the second unit: SIN U1 100
+--> 200 OK
+
+6.4.4 Play the second unit: PLAY U1
+--> 200 OK
+--> note the beginning footage
+
+6.4.5 Transfer the clip from the second to the first unit: XFER U1 U0
+--> 200 OK
+--> verify a clean switch on the analog audio and video output of the first unit.
+--> upon transfer it should play the same footage previewed in step 6.4.4
+
+6.4.5 Get the first unit's status: USTA U0
+--> 202 OK
+--> 0 playing "test002.dv" 963 1000 29.97 100 2502
+--> note the in point set from U1 ---------^
+
+
+7. Unit Configuration
+---------------------
+
+Start miracle or restart if already started.
+Add an online unit.
+Set the server root property if desired.
+
+7.1.0 Load a short clip: LOAD U0 test.dv
+--> 200 OK
+
+7.1.1 Play a clip: PLAY U0
+--> 200 OK
+--> Wait until it gets to the end, and it should pause on the last frame.
+
+7.1.2 Make the clip start looping: USET U0 eof=loop
+--> 200 OK
+--> verify the clip starts playing from the beginning and loops
+
+7.2.0 Set the in point: SIN U0 10
+--> 200 OK
+--> playback pauses at in point (verify with USTA U0)
+
+7.2.1 Set the out point: SOUT U0 200
+--> 200 OK
+--> playback pauses at in point (verify with USTA U0)
+
+7.2.2 Start playing again: PLAY U0
+--> 200 OK
+--> verify playback loops between in and out points
+
+7.3 Tell the unit to ignore the edit points: USET U0 points=ignore
+--> 200 OK
+--> verify playback loops over entire video file
+
+7.4 Get the current value of the points property: UGET U0 points
+--> 202 OK
+--> ignore
+
+
+9. Server Side Queuing
+----------------------
+
+Only one unit is used for these test cases, and
+users are encouraged to test with multiple units online. It is assumed that a
+number of dv files are available for use in the servers ROOT directory - this
+document assumes that they are named test001.dv and up.
+
+9.1.0 Start miracle in interactive mode and add a unit (all tests will assume U0)
+--> server started with unit 0 available
+
+9.1.1 Obtain a miracle shell (via telnet or albino).
+--> 100 VTR (if reported by the client)
+
+9.1.2 Load a clip with LOAD U0 test001.dv and PAUSE U0
+--> 200 OK
+
+9.1.3 List the clips with LIST U0
+--> 201 OK
+--> 1
+--> 0 "test001.dv" 0 6999 7000 7000 25.00
+--> The 1 on the second line denotes the number of times the list has been changed
+    via user commands (known as the 'generation' number).
+--> The third line and beyond reports the clip index (from 0 to n), file loaded, in point,
+    out point, real size of the file and the calculated size (out - in + 1 ).
+
+9.1.4 Check the unit status with USTA U0
+--> 202 OK
+--> 0 paused "test001.dv" 0 0 25.00 0 6999 7000 "test001.dv" 0 0 6999 7000 1 1 0
+--> The last two fields indicate the generation number and current clip resp.
+
+9.1.5 Append a clip with APND U0 test002.dv followed by LIST U0
+--> 201 OK
+--> 2
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 2 and current clip of 0
+
+9.1.6 Move clip 1 to clip 0 with MOVE U0 1 0 followed by LIST U0
+--> 201 OK
+--> 3
+--> 0 "test002.dv" 0 6999 7000 7000
+--> 1 "test001.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 3 and current clip of 1
+
+9.1.7 Move clip 0 to clip 1 with MOVE U0 0 1 followed by LIST U0
+--> 201 OK
+--> 4
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 4 and current clip of 0
+--> Note that the order in which you run 9.1.6 and 9.1.7 shouldn't matter as the
+    result will be identical
+
+9.1.8 Change the position to the next clip with GOTO U0 0 +1
+--> 200 OK
+--> Check that USTA U0 reports a generation of 4 and current clip of 1
+
+9.1.9 Remove all but the playing clip with CLEAN U0 followed by LIST U0
+--> 201 OK
+--> 5
+--> 0 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 5 and current clip of 0
+
+9.1.10 Insert test001.dv back into the list using INSERT U0 test001.dv and run LIST U0
+--> 201 OK
+--> 6
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 6 and current clip of 1
+
+9.1.11 Insert test003.dv at position 2 using INSERT U0 test001.dv 3 and run LIST U0
+--> 201 OK
+--> 7
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 0 6999 7000 7000
+--> 2 "test003.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 7 and current clip of 1
+
+9.1.12 Change the in point of the current clip using SIN U0 5000 and run LIST U0
+--> 201 OK
+--> 8
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 8 and current clip of 1
+
+9.1.13 Change the out point of the following clip using SOUT U0 5000 +1 and run LIST U0
+--> 201 OK
+--> 9
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 0 5000 7000 5001
+--> Check that USTA U0 reports a generation of 9 and current clip of 2
+
+9.1.14 Change the in point of the current clip to 1000 using SIN U0 1000 and run LIST U0
+--> 201 OK
+--> 10
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 1000 5000 7000 4001
+--> Check that USTA U0 reports a generation of 10 and current clip of 2
+
+9.1.15 Ignore the in/out points by running USET U0 points=ignore and run LIST U0
+--> 201 OK
+--> 11
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 7000
+--> 2 "test003.dv" 1000 5000 7000 7000
+--> Check that USTA U0 reports a generation of 11 and current clip of 2
+
+9.1.16 Turn the in/out points on again by running USET U0 points=use and run LIST U0
+--> 201 OK
+--> 12
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> 2 "test003.dv" 1000 5000 7000 4001
+--> Check that USTA U0 reports a generation of 12 and current clip of 2
+
+9.1.17 Remove the current clip using REMOVE U0 and run LIST U0
+--> 201 OK
+--> 13
+--> 0 "test001.dv" 0 6999 7000 7000
+--> 1 "test002.dv" 5000 6999 7000 2000
+--> Check that USTA U0 reports a generation of 13 and current clip of 0
+
+9.1.17 Remove the next clip using REMOVE U0 +1 and run LIST U0
+--> 201 OK
+--> 14
+--> 0 "test001.dv" 0 6999 7000 7000
+--> Check that USTA U0 reports a generation of 14 and current clip of 0
diff --git a/docs/valerie.txt b/docs/valerie.txt
new file mode 100644 (file)
index 0000000..9a90c86
--- /dev/null
@@ -0,0 +1,861 @@
+Valerie API Documentation 
+
+Copyright (C) 2004 Ushodaya Enterprised Limited 
+Author: Charles Yates <charles.yates@pandora.be> 
+Last Revision: 2004-03-20
+
+
+TABLE OF CONTENTS
+-----------------
+
+       0. Overview 
+       0.1. Intended Audience
+       0.2. Terminology
+       1. Definition of a Parser 
+       1.1. Construction of a Local Parser 
+       1.2. Construction of a Remote Parser
+       1.3. Using the Parser
+       1.4. Closing the Parser
+       2. The High Level Parser Wrapper
+       2.1. Connecting
+       2.2. valerie_error_code
+       2.3. Using the High Level Wrapper
+       2.4. Obtaining Directory Contents
+       2.5. Obtaining the Node List
+       2.6. Obtaining the Unit List
+       2.7. Unit Status Information
+       2.8. Server Side Queuing APIs
+       2.9. Accessing the Low Level Parser Directly
+       2.10. Cleaning up
+       2.11. Examples
+       3. The Low Level Parser API
+       3.1. Executing a Command
+       3.2. Interpreting valerie_response
+       3.3. Accessing Unit Status
+       APPENDIX A - COMPILATION AND LINKING
+       APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING
+       APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING
+       APPENDIX D - REFERENCES
+       
+
+0. Overview 
+-----------
+
+       This document details how applications interface to DVCP functionality. 
+
+
+0.1. Intended Audience
+----------------------
+
+       This document draws heavily upon the DVCP design (1) and assumes a basic 
+       knowledge of the functionality provided by the DVCP core. 
+
+       It is aimed at developers who wish to use or maintain the API.
+
+
+0.2. Terminology
+----------------
+
+       The API is designed to allow client applications the ability to communicate
+       to a standalone miracle server or entirely embed the DVCP core in an 
+       instance of a client application. 
+
+       The distinction between the two is defined by the construction of the 
+       'parser'. 
+
+       This 'parser' can be used to issue DVCP commands and receive responses and 
+       a 'high level parser wrapper' is provided to simplify the usage and 
+       decouple the application from the DVCP command set.
+
+
+1. Definition of a Parser 
+------------------------- 
+
+       The parser provides a low level API which allows text DVCP commands to be 
+       executed with responses being returned to the caller. Commands and 
+       responses are ASCII formatted text. 
+
+       Two parsers are provided - local and remote. 
+
+       The local parser is the physical implementation which takes commands and 
+       executes them. 
+
+       The remote parser is a network abstraction that forwards commands to a 
+       miracle instance that hosts a local parser. 
+
+
+1.1. Construction of a Local Parser 
+----------------------------------- 
+
+       To construct a local parser you must have:
+
+           #include <miracle/miracle_local.h>
+
+       and code to initialise the parser is as follows:
+
+           valerie_parser parser = miracle_parser_init_local( );
+
+       See Appendix A for compilation and linking details.
+
+
+1.2. Construction of a Remote Parser
+------------------------------------
+
+       To construct a remote parser you must have:
+
+           #include <valerie/valerie_remote.h>
+
+       and code to initialise the parser is as follows:
+
+           valerie_parser parser = valerie_parser_init_remote( "server", port );
+
+       See Appendix A for compilation and linking details.
+
+
+1.3. Using the Parser
+---------------------
+
+       Although the parser can be used directly to send commands and receive 
+       responses, this low level usage puts the onus on the developer to parse the 
+       responses in a meaningful way. 
+
+       Although this usage is not strictly forbidden by applications, it is 
+       discouraged as construction of commands and meaningful parsing of responses 
+       leads to the clients being unnecessarily dependent on the servers input and 
+       output. 
+
+       As a result, a higher level Parser Wrapper API is provided - this API 
+       encapsulates the command construction and response parsing.
+
+       The following 2 sections provide details on these modes of access.
+
+
+1.4. Closing the Parser
+-----------------------
+
+       Regardless of use, it is the constructors responsibility to close the 
+       parser before it goes out of scope. This is done via:
+
+           valerie_parser_close( parser );
+
+
+2. The High Level Parser Wrapper
+--------------------------------
+
+       The recommended way to access the parser, is via the valerie API. To use 
+       this API, you must have:
+
+           #include <valerie/valerie.h>
+
+       and code to construct the wrapper is:
+
+           valerie dv = valerie_init( parser );
+
+       Note that either remote or local parsers can be used here and there is no 
+       difference in usage, though some error returns will not be applicable to 
+       both. 
+
+       It is recommended that applications honour and deal with the error returns 
+       of both as this allows applications to interchange parsers.
+
+       Also note that valerie is not threadsafe, so you should not use the same 
+       structure in multiple threads. The correct solution to this is to create a
+       valerie per thread - you may safely use the same parser for each thread ie:
+
+           /* valerie for the application */
+           valerie dv = valerie_init( parser );
+           /* valerie for the status handling thread. */
+           valerie dv_status = valerie_init( parser );
+
+       For the purposes of simplification, the remainder of this section assumes 
+       that a remote parser is in use. 
+
+
+2.1. Connecting
+---------------
+
+       Once constructed, the next thing to do is 'connect':
+
+           valerie_error_code error = valerie_connect( dv );
+
+       This function call initialises the parser (ie: if it's remote, it 
+       establishes a connection to the server, or if it's local, it initialises 
+       the state of the units and supporting objects).
+
+       Note that if you have multiple valerie instances on the same parser you 
+       should only connect one of the instances.
+
+
+2.2. valerie_error_code
+----------------------
+
+       All but a couple of the functions that make up the valerie API return a 
+       valerie_error_code.
+
+       These are defined as follows:
+
+           valerie_ok = 0,
+           valerie_malloc_failed,
+           valerie_unknown_error,
+           valerie_no_response,
+           valerie_invalid_command,
+           valerie_server_timeout,
+           valerie_missing_argument,
+           valerie_server_unavailable,
+           valerie_unit_creation_failed,
+           valerie_unit_unavailable,
+           valerie_invalid_file,
+           valerie_invalid_position
+
+       In most cases, it is sufficient to check on a return of valerie_ok.
+
+       To obtain a textual description of a particular error, you can use:
+
+           char *valerie_error_description( valerie_error_code );
+
+
+2.3. Using the High Level Wrapper
+---------------------------------
+
+       The following code snippet assumes that dv is an initialised and connected 
+       valerie structure:
+
+           valerie_error_code error = valerie_unit_play( dv, 0 );
+           if ( error == valerie_ok )
+               fprintf( stderr, "Unit 0 is now playing\n" );
+           else
+               fprintf( stderr, "Play on unit 0 failed: %s\n",
+                            valerie_error_description( error ) );
+
+       The complete interface to valerie is listed in Appendix B of this document.
+
+
+2.4. Obtaining Directory Contents
+--------------------------------
+
+       To obtain a list of files and subdirectories in a given directory relative 
+       to the ROOT property of the server, DVCP provides the CLS command. 
+
+       A valid execution of CLS would be something like:
+
+           CLS "/Stuff"
+
+       would provide a response formatted as follows:
+
+           201 OK
+           "More Stuff/"
+           "file0001.dv" 15552000
+           "file0002.dv" 15552000
+
+       with a trailing empty line.
+
+       The first line indicates the error value, the second line shows an example 
+       of a subdirectory and the 3rd and 4th line lists two files that happen to 
+       exist in the directory.
+
+       valerie provides a high level view on this which automatically parses the 
+       response from the server correctly via the valerie_dir structures and 
+       related functions. 
+
+       An example of use is as follows:
+       
+           valerie_dir dir = valerie_dir_init( dv, "/Stuff" );
+           valerie_error_code error = valerie_dir_get_error_code( dir );
+           if ( error == valerie_ok )
+           {
+               if ( valerie_dir_count( dir ) > 0 )
+               {
+                   valerie_dir_entry_t entry;
+                   int index = 0;
+                   for ( index = 0; index < valerie_dir_count( dir ); index ++ )
+                   {
+                       valerie_dir_get( dir, index, &entry );
+                       if ( entry.dir )
+                           printf( "<%s>\n", entry.name );
+                       else
+                           printf( "%30s %8d", entry.name, entry.size );
+                   }
+               }
+               else
+               {
+                   fprintf( stderr, "Directory is empty\n" );
+               }
+           }
+           else
+           {
+               fprintf( stderr, "Directory listing failed: %s\n",
+                                valerie_error_description( error ) );
+           }
+           valerie_dir_close( dir );
+
+       Note that entry.name provides the name of the file or directory without the 
+       directory prefix. As a convenience, entry.full provides the prefixed name, 
+       so you could subsequently use:
+
+           error = valerie_unit_load( dv, 0, entry.full );
+
+       to load unit 0 with an entry.
+
+
+2.5. Obtaining the Node List
+----------------------------
+
+       Currently not defined by miracle.
+
+2.6. Obtaining the Unit List
+----------------------------
+
+       To obtain a list of defined units, DVCP provides the ULS command.
+       
+       A valid execution of ULS would be:
+       
+           ULS
+       
+       and would provide a response formatted as follows:
+       
+           201 OK
+           U0 00 sdl:360x288 1
+       
+       with a trailing empty line.
+       
+       The fields of each record in the response dictate unit, node, mlt consumer and 
+       online status respectively.
+       
+       valerie provides a high level view on this which automatically parses the 
+       response from the server correctly via the valerie_units structures and 
+       related functions. 
+       
+       An example of use is as follows:
+       
+           valerie_units units = valerie_units_init( dv );
+           valerie_error_code error = valerie_units_get_error_code( units );
+           if ( error == valerie_ok )
+           {
+               if ( valerie_units_count( units ) > 0 )
+               {
+                   valerie_unit_entry_t entry;
+                   int index = 0;
+                   for ( index = 0; index < valerie_units_count( units ); index ++ )
+                   {
+                       valerie_units_get( units, index, &entry );
+                       printf( "U%d %02d %s %s\n", 
+                               entry.unit,
+                               entry.node,
+                               entry.guid,
+                               entry.online ? "online" : "offline" );
+                   }
+               }
+               else
+               {
+                   fprintf( stderr, "Unit list is empty\n" );
+               }
+           }
+           else
+           {
+               fprintf( stderr, "Unit listing failed: %s\n",
+                                valerie_error_description( error ) );
+           }
+           valerie_units_close( units );
+       
+
+2.7. Unit Status Information
+----------------------------
+
+       There are two methods for a client to obtain unit status information.
+       
+       The first is via the DVCP USTA command, which would normally be accessed 
+       via: 
+
+           USTA U0
+       
+       and would provide a response formated as follows:
+       
+           202 OK
+           0 playing "a.dv" 58 1000 25.00 0 6999 7000 "a.dv" 157 0 6999 7000 1 4 0
+       
+       with no trailing empty line.
+       
+       The entries in the record are:
+       
+       * Unit
+       * State (undefined, offline, not_loaded, stopped, playing, 
+                paused, disconnected [when server dies])
+       * Name of Clip
+       * Position in clip
+       * Speed * 1000
+       * Frames per second
+       * Start of clip (in point)
+       * End of clip (out point)
+       * Length of clip
+       * Read ahead clip
+       * Read ahead position 
+       * Read ahead clip in
+       * Read ahead clip out
+       * Read ahead clip length
+       * Seekable flag
+       * Playlist generation
+       * Clip index
+       
+       Again, valerie provides a high level means for obtaining this via the 
+       valerie_unit_status function and valerie_status structures:
+       
+           valerie_status_t status;
+           valerie_error_code error = valerie_unit_status( dv, 0, &status );
+           if ( error == valerie_ok )
+           {
+               switch( status.status )
+               {
+                   case unit_offline:
+                       printf( "offline   " );
+                       break;
+                   case unit_undefined:
+                       printf( "undefined " );
+                       break;
+                   case unit_not_loaded:
+                       printf( "unloaded  " );
+                       break;
+                   case unit_stopped:
+                       printf( "stopped   " );
+                       break;
+                   case unit_playing:
+                       printf( "playing   " );
+                       break;
+                   default:
+                       printf( "unknown   " );
+                       break;
+               }
+       
+               printf( "%06lld %06lld %06lld %s\n", status.in,
+                                                    status.position,
+                                                    status.out,
+                                                    status.clip );
+           }
+           else
+           {
+               fprintf( stderr, "Unit status failed: %s\n",
+                                valerie_error_description( error ) );
+           }
+       
+       The second approach for obtaining a units status is via automatic 
+       notification.
+       
+       This is done via the valerie_notifier API. To obtain the notifier from the 
+       high level API, you can use:
+       
+           valerie_notifier notifier = valerie_get_notifier( dv );
+       
+       To obtain the last status associated to a unit, you can use:
+       
+           int unit = 1;
+           valerie_status_t status;
+           valerie_notifier_get( notifier, &status, unit );
+       
+       To wait for the next status from any unit, you can use:
+       
+           valerie_notifier_wait( notifier, &status );
+       
+       If you wish to trigger the action associated to your applications wait 
+       handling of a particular unit, you can use:
+       
+           valerie_notifier_get( notifier, &status, unit );
+           valerie_notifier_put( notifier, &status );
+       
+       See Examples below for details on this.
+
+       The complete list of fields in the status structure are:
+
+           int unit;
+           unit_status status;
+           char clip[ 2048 ];
+           int64_t position;
+           int speed;
+           double fps;
+           int64_t in;
+           int64_t out;
+           int64_t length;
+           char tail_clip[ 2048 ];
+           int64_t tail_position;
+           int64_t tail_in;
+           int64_t tail_out;
+           int64_t tail_length;
+           int seekable;
+           int generation;
+           int clip_index;
+       
+       You will always receive a status record for every frame output.
+
+       The read ahead information is provided for client side queuing. Client side
+       queuing assumes that uset eof=pause is applied to the unit. A client can 
+       detect when the previously scheduled clip is played out by using the read 
+       ahead information and schedule the next clip. While this mode of operation
+       is still supported, it is recommended that new clients use the server side
+       queuing mechanism which is described in the following section.
+       
+
+2.8. Server Side Queueing APIs
+------------------------------
+
+       This section describes the APIs available to provide server side queueing. 
+       
+       The concept is that each unit maintains its own playlist, containing multiple
+       clips. Associated to the playlist is a generation number which is incremented 
+       on each modification to the playlist. The current playlist generation is
+       provided in the status record in order for a client to know when to refresh
+       its presentation of the list. The status record also indicates which clip is 
+       currently active.
+       
+       Actions that can be carried out on the playlist are summarised as:
+       
+       * list - list all the clips and associated in/out points and size
+       * loading a clip - a load will wipe the current list and replace it with the
+         specified clip
+       * appending a clip - append will always place the specified clip at the end 
+         of the playlist
+       * inserting a clip - insert will place a new clip at the specified position 
+         in the playlist
+       * moving a clip - move will allow clips can be moved in the playlist
+       * removing a clip - remove will remove the specified clip from the playlist
+       * clean - clean will remove all but the playing clip from the playlist
+       
+       Additionally, the following existing actions are clip aware:
+       
+       * goto allows you to move the current play position to a specific clip position
+       * set in/out points allows you to modify clip in and out points
+       
+       Backward compatability has been maintained by the addition of a clip-aware 
+       family of APIs which have the naming convention of valerie_unit_clip_*.
+       
+       These are listed in Appendix B.
+       
+       The following shows an example of obtaining the clips queued on unit 0:
+       
+           valerie_list list = valerie_list_init( dv, 0 );
+           valerie_list_entry_t entry;
+           int index;
+           
+           printf( "Generation = %d\n", list->generation );
+           for ( index = 0; index < valerie_list_count( list ); index ++ )
+           {
+               valerie_list_get( list, index, &entry );
+               printf( "%d %s %d %d %d %d\n", 
+                       entry.clip, 
+                       entry.full,
+                       entry.in,
+                       entry.out,
+                       entry.max,
+                       entry.size );
+           }
+           valerie_list_close( list );
+           
+       To load a clip on unit 0:
+       
+           valerie_unit_load( dv, 0, "/path/clip.dv" );
+           
+       To append a clip on unit 0:
+       
+           valerie_unit_append( dv, 0, "/path/clip.dv", -1, -1 );
+           
+       Note that the last two arguments specify the in and out points of the clip
+       with -1 denoting dfaults of the entirety of the file.
+       
+       To insert a clip at position 0 on unit 0, we can use the following:
+       
+           valerie_unit_clip_insert( dv, 0, clip_absolute, 0, "/path/clip.dv", -1, -1 );
+           
+       The 3rd and 4th arguments here are common to all the valerie_unit_clip functions.
+       They take the form of either [clip_absolute, n] to indicate an absolute clip
+       index, or [clip_relative, n] to indicate a clip index relative to the 
+       currently playing clip.
+       
+       So, to insert a clip immediately before the currently playing clip, we can
+       use:
+       
+           valerie_unit_clip_insert( dv, 0, clip_relative, -1, "/path/clip.dv", -1, -1 );
+           
+       To move the current clip to the next position in the list:
+       
+           valerie_unit_clip_move( dv, 0, clip_relative, 0, clip_relative, 1 );
+           
+       To remove a specific clip:
+       
+           valerie_unit_clip_remove( dv, 0, clip_absolute, index );
+           
+       To remove all but the currently playing clip:
+       
+           valerie_unit_clean( dv, 0 );
+           
+       To goto the first frame in the first clip, you can use:
+       
+           valerie_unit_clip_goto( dv, 0, clip_absolute, 0, 0 );
+           
+       To set the in and out points on the current clip:
+       
+           valerie_unit_clip_set_in( dv, 0, clip_relative, 0, 0 );
+           valerie_unit_clip_set_out( dv, 0, clip_relative, 0, 1000 );
+           
+       A more complete example of use of the server side can queuing can be found
+       at:
+       
+           http://users.pandora.be/acp/rugen
+           
+       The demo client provided with valerie is used for retaining backward 
+       compatability with the client side queuing API.
+
+       
+2.9. Accessing the Low Level Parser Directly
+--------------------------------------------
+
+       The low level parser and its associated structures can be accessed directly 
+       from the high level API, but is very occasionally actually needed.
+       
+       The methods are provided via a pair of high level methods:
+       
+           valerie_error_code error = valerie_execute( dv, 1024, "USTA U%d", unit );
+           valerie_response response = valerie_get_last_response( dv );
+           int index = 0;
+           for ( index = 0; index < valerie_response_count( response ); index ++ )
+               printf( "%d: %s\n", index, valerie_response_get_line( response,index ) );
+       
+       More details on the valerie_response structure can be found in section 3 of this
+       document.
+       
+
+2.10. Cleaning up
+-----------------
+
+       Before the valerie and parser go out of scope, you need to run:
+       
+           valerie_close( dv );
+           valerie_parser_close( parser );
+       
+       Note that you should close all valerie instances before closing the parser.
+       
+
+2.11. Examples
+--------------
+
+       Please refer to albino and humperdink source for examples provided with
+       the project. Additional examples can be found via google with gdv1394 and
+       poldo.
+
+
+3. The Low Level Parser API
+---------------------------
+
+       The low level parser API provides a very simple mechanism for constructing 
+       commands and receiving responses.
+       
+       As described in section 2, a parser is constructed as local or remote and 
+       this is sufficient for constructing the low level parser.
+       
+
+3.1. Executing a Command
+------------------------
+
+       All commands can be executed via the single variable argument function
+       valerie_parser_executef and this function returns a valerie_response, ie:
+       
+           valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir );
+       
+       Note that no carriage return/line feed is required (adding this is 
+       erroneous).
+       
+       It is the receiver of the response who is responsible for closing it.
+       
+           valerie_response_close( response );
+       
+
+3.2. Interpreting valerie_response
+-----------------------------
+
+       The response received can be NULL, but it is safe to call:
+       
+           int error = valerie_response_get_error_code( response );
+       
+       which will return:
+       
+       * -1 if response is NULL, 
+       * -2 if there is no content to the response, 
+       * 0 if the responses first line does not correspond to a valid DVCP response
+       * or the DVCP protocol error code returned on the first line of the response
+       
+       A simple use of a valerie_response structure is as follows:
+       
+           valerie_response response = valerie_parser_executef( parser, "CLS \"%s\"", dir );
+           int error = valerie_response_get_error_code( response );
+           if ( error >= 0 )
+           {
+               int index = 0;
+               for ( index = 0; index < valerie_response_count( response ); index ++ )
+                   printf( "%3d: %s\n", index, valerie_response_get_line( response, index ) );
+           }
+           else
+           {
+               /* interpret error */
+           }
+           valerie_response_close( response );
+       
+       Note that it is safe to call valerie_response_close regardless of the error 
+       condition indicated.
+       
+
+3.3. Accessing Unit Status
+--------------------------
+
+       As with the high level parser, there are two alternatives to obtain unit 
+       status information - either via the USTA DVCP command or via the 
+       valerie1394_notifier.
+       
+       The latter is the recommended way for any applications which wish to extract
+       meaningful information from the status while avoiding the requirement to 
+       duplicate the parsing process in a specific client.
+       
+       The notifier can be obtained by:
+       
+       valerie_notifier notifier = valerie_parser_get_notifier( parser );
+       
+       The use of the notifier with the low level parser is identical to that 
+       dictated in Section 2 - to obtain the last status associated to a unit, 
+       you can use: 
+
+           int unit = 1;
+           valerie_status_t status;
+           valerie_notifier_get( notifier, &status, unit );
+           
+       To wait for the next status from any unit, you can use:
+       
+           valerie_notifier_wait( notifier, &status );
+       
+
+APPENDIX A - COMPILATION AND LINKING
+------------------------------------
+
+       Compilation flags are:
+
+           -I <prefix>/include
+
+       where prefix defaults to /usr/local.
+
+       Linking flags for a client are:
+
+           -L <prefix>/lib/ -lvalerie
+
+       Or for a local parser:
+
+           -L <prefix>/lib/ -lmiracle
+
+       Note that you never need both libs.
+
+
+APPENDIX B - COMPLETE HIGH LEVEL PARSER WRAPPER API LISTING
+-----------------------------------------------------------
+
+       valerie valerie_init( valerie_parser );
+       
+       valerie_error_code valerie_connect( valerie );
+       
+       valerie_error_code valerie_set( valerie, char *, char * );
+       valerie_error_code valerie_get( valerie, char *, char *, int );
+       
+       valerie_error_code valerie_unit_add( valerie, char * );
+       valerie_error_code valerie_unit_load( valerie, int, char * );
+       valerie_error_code valerie_unit_load_clipped( valerie,int,char *,long,long );
+       valerie_error_code valerie_unit_load_back( valerie, int, char * );
+       valerie_error_code valerie_unit_load_back_clipped(valerie,int,char *,long,long)
+       valerie_error_code valerie_unit_play( valerie, int );
+       valerie_error_code valerie_unit_play_at_speed( valerie, int, int );
+       valerie_error_code valerie_unit_stop( valerie, int );
+       valerie_error_code valerie_unit_pause( valerie, int );
+       valerie_error_code valerie_unit_rewind( valerie, int );
+       valerie_error_code valerie_unit_fast_forward( valerie, int );
+       valerie_error_code valerie_unit_step( valerie, int, int );
+       valerie_error_code valerie_unit_goto( valerie, int, int );
+       valerie_error_code valerie_unit_set_in( valerie, int, int );
+       valerie_error_code valerie_unit_set_out( valerie, int, int );
+       valerie_error_code valerie_unit_clear_in( valerie, int );
+       valerie_error_code valerie_unit_clear_out( valerie, int );
+       valerie_error_code valerie_unit_clear_in_out( valerie, int );
+       valerie_error_code valerie_unit_set( valerie, int, char *, char * );
+       valerie_error_code valerie_unit_get( valerie, int, char * );
+       
+       valerie_error_code valerie_unit_status( valerie, int, valerie_status );
+       valerie_notifier valerie_get_notifier( valerie );
+       
+       valerie_dir valerie_dir_init( valerie, char * );
+       valerie_error_code valerie_dir_get( valerie_dir, int, valerie_dir_entry );
+       int valerie_dir_count( valerie_dir );
+       void valerie_dir_close( valerie_dir );
+       
+       valerie_nodes valerie_nodes_init( valerie );
+       valerie_error_code valerie_nodes_get(valerie_nodes,int,valerie_node_entry);
+       int valerie_nodes_count( valerie_nodes );
+       void valerie_nodes_close( valerie_nodes );
+       
+       valerie_units valerie_units_init( valerie );
+       valerie_error_code valerie_units_get(valerie_units,int,valerie_unit_entry);
+       int valerie_units_count( valerie_units );
+       void valerie_units_close( valerie_units );
+       
+       valerie_response valerie_get_last_response( valerie );
+       
+       valerie_error_code valerie_execute( valerie, size_t, char *, ... );
+       
+       void valerie_close( valerie );
+       
+       Notifier Functions
+       ------------------
+       
+       void valerie_notifier_get( valerie_notifier, valerie_status, int );
+       void valerie_notifier_put( valerie_notifier, valerie_status );
+       int valerie_notifier_wait( valerie_notifier, valerie_status );
+       void valerie_notifier_close( valerie_notifier );
+       
+       Server Side Queuing
+       -------------------
+
+       valerie_list valerie_list_init( valerie, int )
+       valerie_error_code valerie_list_get_error_code( valerie_list )
+       valerie_error_code valerie_list_get( valerie_list, int, valerie_list_entry )
+       int valerie_list_count( valerie_list )
+       void valerie_list_close( valerie_list )
+       
+       valerie_error_code valerie_unit_clean( valerie dv, int unit )
+       valerie_error_code valerie_unit_append( valerie dv, int unit, char *file, int in, int out )
+       valerie_error_code valerie_unit_remove_current_clip( valerie dv, int unit )
+       
+       valerie_error_code valerie_unit_clip_goto( valerie dv, int unit, valerie_clip_offset offset, int clip, int position )
+       valerie_error_code valerie_unit_clip_set_in( valerie dv, int unit, valerie_clip_offset offset, int clip, int in )
+       valerie_error_code valerie_unit_clip_set_out( valerie dv, int unit, valerie_clip_offset offset, int clip, int in )
+       valerie_error_code valerie_unit_clip_move( valerie dv, int unit, valerie_clip_offset offset, int src, valerie_clip_offset offset, int dest )
+       valerie_error_code valerie_unit_clip_remove( valerie dv, int unit, valerie_clip_offset offset, int clip )
+       valerie_error_code valerie_unit_clip_insert( valerie dv, int unit, valerie_clip_offset offset, int clip, char *file, int in, int out )
+
+       
+
+APPENDIX C - COMPLETE LOW LEVEL PARSER API LISTING
+--------------------------------------------------
+
+       valerie_response valerie_parser_connect( valerie_parser );
+       valerie_response valerie_parser_execute( valerie_parser, char * );
+       valerie_response valerie_parser_executef( valerie_parser, char *, ... );
+       valerie_response valerie_parser_run( valerie_parser, char * );
+       valerie_notifier valerie_parser_get_notifier( valerie_parser );
+       void valerie_parser_close( valerie_parser );
+       
+       valerie_response valerie_response_init( );
+       valerie_response valerie_response_clone( valerie_response );
+       int valerie_response_get_error_code( valerie_response );
+       char *valerie_response_get_error_string( valerie_response );
+       char *valerie_response_get_line( valerie_response, int );
+       int valerie_response_count( valerie_response );
+       void valerie_response_set_error( valerie_response, int, char * );
+       int valerie_response_printf( valerie_response, size_t, char *, ... );
+       int valerie_response_write( valerie_response, char *, int );
+       void valerie_response_close( valerie_response );
+       
+
+APPENDIX D - REFERENCES
+-----------------------
+
+       (1) doc/dvcp.txt - DVCP protocol
+       (2) doc/testing.txt - Test procedures
diff --git a/docs/westley.txt b/docs/westley.txt
new file mode 100644 (file)
index 0000000..1f42538
--- /dev/null
@@ -0,0 +1,574 @@
+Westley Documentation
+
+Copyright (C) 2004 Ushodaya Enterprised Limited
+Authors: Charles Yates <charles.yates@pandora.be>
+Last Revision: 2004-03-20
+
+
+WESTLEY
+-------
+
+Preamble:
+
+       Westley is the MLT projects XML serialisation/deserialisation format -
+       as such, it closely mirrors the internal structure of the MLT API.
+
+       If you just want to go straight to the DTD, then see 
+       mlt/src/modules/westley/westley.dtd, which gets installed at 
+       $(prefix)/share/mlt/modules/westley.dtd. Currently, the westley parser is
+       non-validating.
+
+
+Introduction:
+
+       A westley document is essentially a list of 'producers' - a producer is
+       an mlt object which generates mlt frames (images and associated audio
+       samples). 
+       
+       There are 3 types of producer:
+       
+       * Basic Producers - these are typically file or device oriented feeds; 
+       * Playlists - these are arrangements of multiple producers;
+       * Multitracks - these are the fx encapsulators.
+
+       In the mlt model, producers are created and attached to 'consumers' -
+       consumers are software playback components (such as SDL), or wrappers for
+       hardware drivers (such as bluefish) or even the westley serialising
+       consumer itself (the latter doesn't receive frames - it merely
+       interrogates the connected producer for its configuration).
+
+       Although westley was defined as a serialisation mechanism for instantiated 
+       MLT components, this document will concentrate on the hand authoring of 
+       westley documents.
+
+
+Rules:
+
+       As shall become apparent through the remainder of this document, the basic
+       tenet of westley authoring is to organise the document in the following
+       manner:
+
+       1) create producer elements for each unique media clip in the project;
+       2) create playlists for each track;
+       3) create a multitrack and specify filters and transitions;
+       4) adding global filters.
+
+       While other uses of westley exist, the approach taken here is to maximise
+       efficiency for complex projects. 
+
+
+Basic Producers:
+
+       The simplest westley document is:
+
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.dv</property>
+         </producer>
+       </westley>
+
+       The westley wrapping is of course superfluous here - loading this document
+       with MLT is identical to loading the clip directly. 
+
+       Of course, you can specify additional properties. For example, consider an
+       MPEG file with multiple soundtracks - you could define a westley document to
+       ensure that the second audio track is loaded:
+
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.mpeg</property>
+           <property name="audio_track">1</property>
+         </producer>
+       </westley>
+
+       NB: This relies on the mpeg being handled by the avformat producer, rather
+       than the mcmpeg one. See services.txt for more details.
+
+       A more useful example comes with the pango producer for a text producer.
+
+       TODO: pango example...
+
+       Notes:
+
+       1) It is better not to specify in/out points when defining basic producers
+       as these can be specified in the playlists. The reasoning is that in/out
+       restricts the amount of the clip available, and could lead to the same clip
+       being loaded multiple times if you need different regions of the clip
+       elsewhere;
+       2) A westley can be specified as a resource, so westleys can naturally
+       encapsulate other westleys.
+
+
+Playlists:
+
+       Playlists provide a 'collection' structure for producers. These can be used
+       to define 'tracks' in the multitrack approach, or simple playlists for
+       sequential, single track playout.
+
+       As an example, the following defines two basic producers and a playlist with 3
+       items:
+
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.dv</property>
+         </producer>
+         <producer id="producer1">
+           <property name="resource">clip2.dv</property>
+         </producer>
+         <playlist id="playlist0">
+           <entry producer="producer0" in="0" out="2999"/>
+           <entry producer="producer1" in="0" out="999"/>
+           <entry producer="producer0" in="3000" out="6999"/>
+         </playlist>
+       </westley>
+
+       Here we see how the playlist defines the in/out points of the basic
+       producers.
+
+       Notes:
+
+       1) All in/out points are absolute frame positions relative to the producer
+       being appended to the playlist;
+       2) Westley documents are currently authored for a specific normalisation;
+       3) The last 'producer' in the document is the default for play out;
+       4) Playlists can reference the same producer multiple times. In/out regions
+       do not need to be contiguous - duplication and skipping is acceptable.
+
+
+Interlude - Introducing Multitracks:
+
+       So far we've defined basic producers and playlists/tracks - the tractor is
+       the element that allows us to arrange our tracks and specify filters and
+       transitions. Similarly to a playlist, a tractor is a container.
+
+       Note that MLT doesn't see a filter or a transition as a producer in the
+       normal sense - filters and transitions are passive when it comes to seeking.
+       Internally, seeks are carried out on the producers. This is an important
+       point - MLT does not follow a traditional graph oriented model.
+
+       Visualising an MLT tractor and it's interaction with the consumer will
+       assist here:
+
+       +----------------------------------------------+
+       |tractor                                       |
+       | +----------+    +-+    +-+    +-+    +-+     |
+       | |multitrack|    |f|    |f|    |t|    |t|     |
+       | | +------+ |    |i|    |i|    |r|    |r|     |
+       | | |track0|-|--->|l|- ->|l|- ->|a|--->|a|\    |
+       | | +------+ |    |t|    |t|    |n|    |n| \   |
+       | |          |    |e|    |e|    |s|    |s|  \  |
+       | | +------+ |    |r|    |r|    |i|    |i|   \ |    +--------+
+       | | |track1|-|- ->|0|--->|1|--->|t|--->|t|-----|--->|consumer|
+       | | +------+ |    | |    | |    |i|    |i|   / |    +--------+
+       | |          |    | |    | |    |o|    |o|  /  |         ^
+       | | +------+ |    | |    | |    |n|    |n| /   |         |
+       | | |track2|-|- ->| |- ->| |--->|0|- ->|1|/    |         |
+       | | +------+ |    | |    | |    | |    | |     |         |
+       | +----------+    +-+    +-+    +-+    +-+     |         |
+       +----------------------------------------------+         |
+              ^                                                 |
+              |                                                 |
+       +-----------+                                            |
+       |APPLICATION|--------------------------------------------+
+       +-----------+
+
+       Internally, all frames from all tracks pass through all the filters and
+       transitions - these are told which tracks to deal and which regions of the
+       tracks to work on. 
+
+       Note that the application communicates with the producer - it can alter
+       playback speed, position, or even which producer is connected to which
+       consumer. 
+       
+       The consumer receives the first non-blank frame (see below). It has no say
+       in the order in which gets them (the sdl consumer when used with inigo might 
+       appear to be an exception - it isn't - it simply has a route back to the 
+       application to allow the application to interpret key presses).
+
+
+Tractors:
+
+       To create a multitrack westley, we can use two playlists and introduce a
+       tractor. For the purposes of demonstration, I'll add a filter here too:
+
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.dv</property>
+         </producer>
+         <producer id="producer1">
+           <property name="resource">clip2.dv</property>
+         </producer>
+         <playlist id="playlist0">
+           <entry producer="producer0" in="0" out="2999"/>
+           <blank length="1000"/>
+           <entry producer="producer0" in="3000" out="6999"/>
+         <playlist id="playlist1">
+           <blank length="3000"/>
+           <entry producer="producer1" in="0" out="999"/>
+         </playlist>
+         <tractor id="tractor0">
+           <multitrack>
+             <track producer="playlist0"/>
+             <track producer="playlist1"/>
+           </multitrack>
+           <filter>
+             <property name="track">0</property>
+             <property name="mlt_service">greyscale</property>
+           </filter>
+         </tractor>
+       </westley>
+       
+       Here we see that blank frames are inserted into the first playlist and a
+       blank is provided at the beginning of the second - this can be visualised in
+       the traditional timeline widget as follows:
+
+       +-------+   +-------------+
+       |a      |   |a            |
+       +-------+---+-------------+
+               |b  |
+               +---+
+       
+       Adding the filter on the top track, gives us:
+
+       +-------+   +-------------+
+       |a      |   |a            |
+       +-------+---+-------------+
+       |greyscale                |
+       --------+---+-------------+
+               |b  |
+               +---+
+       
+       Note that it's only applied to the visible parts of the top track.
+
+       The requirement to apply a filter to the output, as opposed to a specific 
+       track leads us to the final item in the Rules section above. As an example, 
+       let's assume we wish to watermark all output, then we could use the 
+       following:
+
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.dv</property>
+         </producer>
+         <producer id="producer1">
+           <property name="resource">clip2.dv</property>
+         </producer>
+         <playlist id="playlist0">
+           <entry producer="producer0" in="0" out="2999"/>
+           <blank length="1000"/>
+           <entry producer="producer0" in="3000" out="6999"/>
+         <playlist id="playlist1">
+           <blank length="3000"/>
+           <entry producer="producer1" in="0" out="999"/>
+         </playlist>
+         <tractor id="tractor0">
+           <multitrack>
+             <track producer="playlist0"/>
+             <track producer="playlist1"/>
+           </multitrack>
+           <filter>
+             <property name="track">0</property>
+             <property name="mlt_service">greyscale</property>
+           </filter>
+         </tractor>
+         <tractor id="tractor1">
+           <multitrack>
+             <track producer="tractor0"/>
+           </multitrack>
+           <filter>
+             <property name="mlt_service">watermark</property>
+             <property name="resource">watermark1.png</property>
+           </filter>
+         </tractor>
+       </westley>
+       
+       Here we employ another tractor and we define a single track (being the
+       tractor we previously defined) and apply a watermarking filter there.
+
+       This is simply provided as an example - the watermarking functionality could
+       be better handled at the playout stage itself (ie: as a filter automatically
+       placed between all producers and the consumer).
+
+       Tracks act like "layers" in an image processing program like the GIMP. The 
+       bottom-most track takes highest priority and higher layers are overlays 
+       and do not appear unless there are gaps in the lower layers or unless
+       a transition is applied that merges the tracks on the specifed region.
+       Practically speaking, for A/B video editing it does not mean too much, 
+       and it will work as expected; however, as a general rule apply any CGI
+       (graphic overlays with pixbuf or titles with pango) on tracks higher than
+       your video tracks. Also, this means that any audio-only tracks that are
+       lower than your video tracks will play rather than the audio from the video
+       clip. Remember, nothing is affected like mixing or compositing until one
+       applies a transition or appropriate filter.
+       
+       <westley>
+         <producer id="producer0">
+           <property name="resource">clip1.dv</property>
+         </producer>
+         <playlist id="playlist0">
+           <entry producer="producer0"/>
+         </playlist>
+         <producer id="producer1">
+           <property name="resource">clip2.mpeg</property>
+         </producer>
+         <playlist id="playlist1">
+           <blank length="50"/>
+           <entry producer="producer1"/>
+         </playlist>
+         <tractor id="tractor0" in="0" out="315">
+           <multitrack id="multitrack0">
+             <track producer="playlist0"/>
+             <track producer="playlist1"/>
+           </multitrack>
+           <transition id="transition0" in="50" out="74">
+             <property name="a_track">0</property>
+             <property name="b_track">1</property>
+             <property name="mlt_service">luma</property>
+           </transition>
+           <transition id="transition1" in="50" out="74">
+             <property name="a_track">0</property>
+             <property name="b_track">1</property>
+             <property name="mlt_service">mix</property>
+             <property name="start">0.0</property>
+             <property name="end">1.0</property>
+           </transition>
+         </tractor>
+       </westley>
+
+       A "luma" transition is a video wipe processor that takes a greyscale bitmap
+       for the wipe definition. When one does not specify a bitmap, luma performs
+       a dissolve. The "mix" transition does an audio mix, but it interpolates
+       between the gain scaling factors between the start and end properties - 
+       in this example, from 0.0 (none of track B) to 1.0 (all of track B). 
+       Because the bottom track starts out with a gap specified using the <blank>
+       element, the upper track appears during the blank segment. See the demos and
+       services.txt to get an idea of the capabilities of the included transitions.
+
+Flexibility:
+
+       The information presented above is considered the MLT Westley "normal"
+       form. This is the output generated by the westley consumer, for example,
+       when used with inigo. It is the output generated when you use the
+       "Westley to File" consumer in the demo script, which beginners will find
+       most useful for learning to use westley XML. This section describes 
+       alternative forms the westley producer accepts.
+
+       First of all, the normal form is more of a linear format with producers
+       and playlists defined prior to their usage in a multitrack. Westley
+       also accepts a hierarchical format with producers as children of tracks
+       or playlist entries and with playlists as children of tracks:
+
+       <westley>
+         <tractor>
+           <multitrack>
+             <track>
+               <playlist>
+                 <entry>
+                   <producer>
+                     <property name="resource">clip1.dv</property>
+                   </producer>
+                 </entry>
+               </playlist>
+             </track>
+           </multitrack>
+         </tractor>
+       </westley>
+
+       Obviously, this example is meant to demonstrate hierarchy and not effective
+       use of playlist or multitrack!
+
+       Secondly, as part of error handling, westley is forgiving if you fail to
+       supply <tractor>, <track>, and <entry> where one can be understood. This
+       affords an abbreviated syntax that is less verbose and perhaps less 
+       intimidating for a human to read and understand. One can simplify the
+       above example as:
+
+       <westley>
+         <multitrack>
+           <playlist>
+             <producer>
+               <property name="resource">clip1.dv</property>
+             </producer>
+           </playlist>
+         </multitrack>
+       </westley>
+
+       Yes, filters and transitions can be added to the above example after the
+       closing multitrack tag (</multitrack>) because it is still enclosed within
+       the westley body tags.
+
+       If you specify in and out on a producer and it has been enclosed within
+       an <entry> or <playlist>, then the edit points apply to the playlist
+       entry and not to the producer itself. This facilitates re-use of media:
+
+       <playlist>
+         <producer id="clip1" in="25" out="78">
+           <property name="resource">clip1.dv</property>
+         </producer>
+         <entry producer="clip1" in="119" out="347"/>
+       </playlist>
+
+       In the above example, the producer attribute of the entry element is 
+       a reference to the preceding producer. All references must follow the 
+       definition. The edit points supplied on the producer above will not affect
+       the entry that references it below because westley knows the clip is a 
+       playlist entry and optimises this situation. The advantage is that one
+       does not need to determine every clip to be included ahead of time 
+       and specify them outside the context of the mutlitrack timeline.
+
+       This form of authoring will be easier for many to visualise as a non-linear 
+       editor's timeline. Here is a more complex example:
+
+       <westley>
+         <multitrack>
+           <playlist>
+             <producer id="foo" in="100" out="149">
+               <property name="resource">clip2.mpeg</property>
+             </producer>
+             <blank length="25"/>
+             <entry producer="foo" in="10" out="59"/>
+           </playlist>
+           <playlist>
+             <blank length="25"/>
+             <producer id="bar" in="100" out="199">
+               <property name="resource">clip3.mpeg</property>
+             </producer>
+             <entry out="99" producer="bar"/>
+           </playlist>
+         </multitrack>
+         <filter mlt_service="greyscale" track="0"/>
+         <transition mlt_service="luma" in="25" out="49" a_track="0" b_track="1"/>
+         <transition mlt_service="luma" in="75" out="99" a_track="0" b_track="1">
+           <property name="reverse" value="1"/>
+         </transition>
+       </westley>
+
+       Did you notice something different in the last example? Properties can be 
+       expressed using XML attributes on the element as well. However, only 
+       non-service-specific properties are supported in this way. For example,
+       "mlt_service" is available to any producer, filter, or transition. However,
+       "resource" is actually service-specific. Notice the syntax of the last 
+       property, on the last transition. Westley accepts property values using 
+       the "value" attribute as well as using element text.
+
+       We have seen a few different ways of expressing property values. There are
+       a couple more for properties that can accept XML data. For example, the
+       GDK pixbuf producer with librsvg can handle embedded SVG, and the Pango
+       producer can handle embedded Pango markup. You can enclose the embedded XML
+       using a CDATA section:
+
+       <property name="resource"><![CDATA[ <svg>...</svg> ]]></property>
+
+       Please ensure the opening CDATA tag immediately follows the opening
+       property tag and that the section closing tag immediately precedes the
+       closing property tag.
+
+       However, westley can also accept inline embedded XML:
+
+       <property name="resource">
+         <svg>
+         </svg>
+       </property>
+
+       Currently, there is no namespace handling so a conflict will occur only on 
+       any embedded XML that contains an element named "property" because 
+       westley collects embedded XML until it reaches a closing property tag.
+       
+       
+Entities and Parameterisation:
+       
+       The westley producer parser supports XML entities. An example:
+       
+       <?xml version="1.0"?>
+       <!DOCTYPE westley [
+           <!ENTITY msg "Hello world!">
+       ]>
+       <westley>
+         <producer id="producer0">
+           <property name="mlt_service">pango</property>
+           <property name="text">&msg;</property>
+         </producer>
+       </westley>
+       
+       If you are embedding another XML document into a property value not using
+       a CNODE section, then any DOCTYPE section must be relocated before any of
+       the xml elements to be well-formed. See demo/svg.westley for an example.
+       
+       Entities can be used to parameterise westley! Using the above example, the
+       entity declared serves as the default value for &msg;. The entity content
+       can be overridden from the resource property supplied to the westley
+       producer. The syntax is the familiar, url-encoded query string used with
+       HTTP, e.g.: file?name=value&name=value...
+       
+       There are a couple of rules of usage. The Miracle LOAD command and inigo
+       command line tool require you to preface the URL with "westley:" because
+       the query string destroys the filename extension matching peformed by
+       Fezzik. Also, inigo looks for '=' to tokenise property settings. Therefore, 
+       one uses ':' between name and value instead of '='. Finally, since inigo
+       is run from the shell, one must enclose the URL within single quotes to 
+       prevent shell filename expansion, or similar.
+       
+       Needless to say, the ability to parameterise westley XML compositions is
+       an extremely powerful tool. An example for you to play with is available in 
+       demo/entity.westley. Try overriding the name from inigo:
+       inigo 'westley:entity.westley?name:Charlie'
+       
+       Technically, the entity declaration is not needed in the head of the XML
+       document if you always supply the parameter. However, you run the risk
+       of unpredictable behviour without one. Therefore, it is safest and a best
+       practice to always supply an entity declaration. It is improves the 
+       readability as one does not need to search for the entity references to 
+       see what parameters are available.
+       
+
+Tips and Technique:
+
+       If one finds the above hierarchical, abbreviated format intuitive,
+       start with a simple template and fill and extend as needed:
+           <westley>
+             <multitrack>
+               <playlist>
+               </playlist>
+               ...add a playlist for each track...
+             </multitrack>
+             ...add filters and transitions...
+           </westley>
+           
+       By using a playlist for each track, it is easier to iteratively add new 
+       clips and blank regions as you develop the project. You will not have to
+       use <track> or later add <playlist> when necessary.
+       
+       A more advanced template that allows sequencing multitracks is:
+           <playlist>
+             <entry>
+               <multitrack>
+                 <playlist>
+                 </playlist>
+                 ...add a playlist for each track...
+               </multitrack>
+               ...add filters and transitions...
+             </entry>
+             
+             <entry>
+               <multitrack>
+                 <playlist>
+                 </playlist>
+                 ...add a playlist for each track...
+               </multitrack>
+               ...add filters and transitions...
+             </entry>
+           </playlist>
+
+       If you end up making a collection of templates for various situations, then
+       consider using XML Entities to make the template more effective by moving
+       anything that should parameterised into an entity.
+
+       If you want to have a silent, black background for audio and video fades,
+       then make the top track simply <producer mlt_service="colour"/>. Then,
+       use composite and volume effects. See the "Fade from/to black/silence"
+       demo for an example (demo/mlt_fade_black).
+       
+       If you apply the reverse=1 property to a transition like "luma," then
+       be careful because it also inherently swaps the roles of A and B tracks.
+       Therefore, you need to might need to swap the a_track and b_track values
+       if it did not turn out the way you expected. See the "Clock in and out"
+       for an example (demo/mlt_clock_in_and_out).
diff --git a/mlt-config-template b/mlt-config-template
new file mode 100644 (file)
index 0000000..607d178
--- /dev/null
@@ -0,0 +1,32 @@
+export package=framework
+export field=0
+
+while [ "$1" != "" ]
+do
+       case $1 in
+               --help )                field=0 ;;
+               --version )             field=-1 ;;
+               --prefix )              field=-2 ;;
+               --prefix=* )    prefix="${i#--prefix=}" ;;
+               --cflags )              field=2 ;;
+               --libs )                field=3 ;;
+               --list )                field=1; package="" ;;
+               * )                             package=$1 ;;
+       esac
+       shift
+done
+
+if [ "$field" = "0" ]
+then   echo "Usage: mlt-config [ --version ] | [ --prefix=dir ] [ [ package ] [ --cflags ] [ --libs ] ]"
+elif [ "$field" = "-1" ]
+then   echo $version
+elif [ "$field" = "-2" ]
+then   config=`which mlt-config`
+               dir=`dirname $config`
+               dir=`dirname $dir`
+               echo $dir
+elif [ -f "$prefix/share/mlt/packages.dat" ]
+then grep "^$package" $prefix/share/mlt/packages.dat | cut -f $field
+else echo mlt-config cannot find package $package.
+fi
+echo >&2 "mlt-config is deprecated. Please use pkg-config instead."
diff --git a/mlt-framework.pc.in b/mlt-framework.pc.in
new file mode 100644 (file)
index 0000000..5748867
--- /dev/null
@@ -0,0 +1,7 @@
+
+Name: mlt-framework
+Description: MLT multimedia framework
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
diff --git a/mlt-miracle.pc.in b/mlt-miracle.pc.in
new file mode 100644 (file)
index 0000000..593055e
--- /dev/null
@@ -0,0 +1,7 @@
+
+Name: mlt-miracle
+Description: MLT Miracle server API
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
diff --git a/mlt-valerie.pc.in b/mlt-valerie.pc.in
new file mode 100644 (file)
index 0000000..7750ba3
--- /dev/null
@@ -0,0 +1,7 @@
+
+Name: mlt-valerie
+Description: MLT Valerie client API
+Version: ${version}
+Requires:
+Libs: -L${libdir} ${libs}
+Cflags: ${cflags}
diff --git a/profiles/Makefile b/profiles/Makefile
new file mode 100644 (file)
index 0000000..9ad9f62
--- /dev/null
@@ -0,0 +1,18 @@
+include ../config.mak
+
+all:
+
+depend:
+
+distclean:
+
+clean:
+
+install:       all uninstall
+       install -d "$(DESTDIR)$(prefix)/share/mlt/profiles"
+       install -m 644 * "$(DESTDIR)$(prefix)/share/mlt/profiles"
+       rm -f "$(DESTDIR)$(prefix)/share/mlt/profiles/"*~
+       rm -f "$(DESTDIR)$(prefix)/share/mlt/profiles/Makefile"
+
+uninstall:
+       rm -rf "$(DESTDIR)$(prefix)/share/mlt/profiles"
diff --git a/profiles/atsc_1080i_50 b/profiles/atsc_1080i_50
new file mode 100644 (file)
index 0000000..4309b0f
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080i 50Hz
+frame_rate_num=25
+frame_rate_den=1
+width=1920
+height=1080
+progressive=0
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080i_60 b/profiles/atsc_1080i_60
new file mode 100644 (file)
index 0000000..3c95886
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080i 60Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=0
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080p_2398 b/profiles/atsc_1080p_2398
new file mode 100644 (file)
index 0000000..e8a1415
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080p 23.98Hz
+frame_rate_num=24000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080p_24 b/profiles/atsc_1080p_24
new file mode 100644 (file)
index 0000000..49f6422
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080p 24Hz
+frame_rate_num=24
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080p_25 b/profiles/atsc_1080p_25
new file mode 100644 (file)
index 0000000..769200a
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080p 25Hz
+frame_rate_num=25
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080p_2997 b/profiles/atsc_1080p_2997
new file mode 100644 (file)
index 0000000..9633cd6
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080p 29.97Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_1080p_30 b/profiles/atsc_1080p_30
new file mode 100644 (file)
index 0000000..1b00238
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 1080p 30Hz
+frame_rate_num=30
+frame_rate_den=1
+width=1920
+height=1080
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/atsc_720p_30 b/profiles/atsc_720p_30
new file mode 100644 (file)
index 0000000..beb6cc9
--- /dev/null
@@ -0,0 +1,10 @@
+description=ATSC 720p 30Hz
+frame_rate_num=30000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/cif_15 b/profiles/cif_15
new file mode 100644 (file)
index 0000000..7b590d2
--- /dev/null
@@ -0,0 +1,10 @@
+description=CIF 15 fps
+frame_rate_num=15
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/cif_ntsc b/profiles/cif_ntsc
new file mode 100644 (file)
index 0000000..48279a5
--- /dev/null
@@ -0,0 +1,10 @@
+description=CIF NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=288
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/cif_pal b/profiles/cif_pal
new file mode 100644 (file)
index 0000000..a7f66d4
--- /dev/null
@@ -0,0 +1,10 @@
+description=CIF PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/cvd_ntsc b/profiles/cvd_ntsc
new file mode 100644 (file)
index 0000000..508b3cc
--- /dev/null
@@ -0,0 +1,10 @@
+description=CVD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=480
+progressive=0
+sample_aspect_num=20
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/cvd_pal b/profiles/cvd_pal
new file mode 100644 (file)
index 0000000..2415c77
--- /dev/null
@@ -0,0 +1,10 @@
+description=CVD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=27
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/dv_ntsc b/profiles/dv_ntsc
new file mode 100644 (file)
index 0000000..ff1f24e
--- /dev/null
@@ -0,0 +1,10 @@
+description=DV NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=720
+height=480
+progressive=0
+sample_aspect_num=8
+sample_aspect_den=9
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/dv_ntsc_wide b/profiles/dv_ntsc_wide
new file mode 100644 (file)
index 0000000..d512d6c
--- /dev/null
@@ -0,0 +1,10 @@
+description=DV NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=720
+height=480
+progressive=0
+sample_aspect_num=32
+sample_aspect_den=27
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/dv_pal b/profiles/dv_pal
new file mode 100644 (file)
index 0000000..d569140
--- /dev/null
@@ -0,0 +1,10 @@
+description=DV PAL
+frame_rate_num=25
+frame_rate_den=1
+width=720
+height=576
+progressive=0
+sample_aspect_num=16
+sample_aspect_den=15
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/dv_pal_wide b/profiles/dv_pal_wide
new file mode 100644 (file)
index 0000000..a52b9cf
--- /dev/null
@@ -0,0 +1,10 @@
+description=DV PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=720
+height=576
+progressive=0
+sample_aspect_num=64
+sample_aspect_den=45
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_1080_25p b/profiles/hdv_1080_25p
new file mode 100644 (file)
index 0000000..bba72e8
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 1080 25p
+frame_rate_num=25
+frame_rate_den=1
+width=1440
+height=1080
+progressive=1
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_1080_30p b/profiles/hdv_1080_30p
new file mode 100644 (file)
index 0000000..7321364
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 1080 30p
+frame_rate_num=30000
+frame_rate_den=1001
+width=1440
+height=1080
+progressive=1
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_1080_50i b/profiles/hdv_1080_50i
new file mode 100644 (file)
index 0000000..6544470
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 1080 50i
+frame_rate_num=25
+frame_rate_den=1
+width=1440
+height=1080
+progressive=0
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_1080_60i b/profiles/hdv_1080_60i
new file mode 100644 (file)
index 0000000..9c3e6fc
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 1080 60i
+frame_rate_num=30000
+frame_rate_den=1001
+width=1440
+height=1080
+progressive=0
+sample_aspect_num=4
+sample_aspect_den=3
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_720_25p b/profiles/hdv_720_25p
new file mode 100644 (file)
index 0000000..7bd6eed
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 720 25p
+frame_rate_num=25
+frame_rate_den=1
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_720_30p b/profiles/hdv_720_30p
new file mode 100644 (file)
index 0000000..68caaf9
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 720 30p
+frame_rate_num=30000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_720_50p b/profiles/hdv_720_50p
new file mode 100644 (file)
index 0000000..2a93230
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 720 50p
+frame_rate_num=50
+frame_rate_den=1
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/hdv_720_60p b/profiles/hdv_720_60p
new file mode 100644 (file)
index 0000000..11189cb
--- /dev/null
@@ -0,0 +1,10 @@
+description=HDV 720 60p
+frame_rate_num=60000
+frame_rate_den=1001
+width=1280
+height=720
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/qcif_15 b/profiles/qcif_15
new file mode 100644 (file)
index 0000000..70d286d
--- /dev/null
@@ -0,0 +1,10 @@
+description=QCIF 15fps
+frame_rate_num=15
+frame_rate_den=1
+width=176
+height=144
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/qcif_ntsc b/profiles/qcif_ntsc
new file mode 100644 (file)
index 0000000..0e90157
--- /dev/null
@@ -0,0 +1,10 @@
+description=QCIF NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=176
+height=144
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/qcif_pal b/profiles/qcif_pal
new file mode 100644 (file)
index 0000000..21667ee
--- /dev/null
@@ -0,0 +1,10 @@
+description=QCIF PAL
+frame_rate_num=25
+frame_rate_den=1
+width=176
+height=144
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/quarter_15 b/profiles/quarter_15
new file mode 100644 (file)
index 0000000..7ba8eef
--- /dev/null
@@ -0,0 +1,10 @@
+description=QVGA 15fps
+frame_rate_num=15
+frame_rate_den=1
+width=320
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/quarter_ntsc b/profiles/quarter_ntsc
new file mode 100644 (file)
index 0000000..fb37cd7
--- /dev/null
@@ -0,0 +1,10 @@
+description=Quarter Square NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=320
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/quarter_ntsc_wide b/profiles/quarter_ntsc_wide
new file mode 100644 (file)
index 0000000..8c7be94
--- /dev/null
@@ -0,0 +1,10 @@
+description=Quarter Square NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=426
+height=240
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/quarter_pal b/profiles/quarter_pal
new file mode 100644 (file)
index 0000000..beec207
--- /dev/null
@@ -0,0 +1,10 @@
+description=Quarter Square PAL
+frame_rate_num=25
+frame_rate_den=1
+width=384
+height=288
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/quarter_pal_wide b/profiles/quarter_pal_wide
new file mode 100644 (file)
index 0000000..195410a
--- /dev/null
@@ -0,0 +1,10 @@
+description=Quarter Square PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=512
+height=288
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/square_ntsc b/profiles/square_ntsc
new file mode 100644 (file)
index 0000000..e139c3e
--- /dev/null
@@ -0,0 +1,10 @@
+description=Square NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=640
+height=480
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/square_ntsc_wide b/profiles/square_ntsc_wide
new file mode 100644 (file)
index 0000000..da2de3e
--- /dev/null
@@ -0,0 +1,10 @@
+description=Square NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=854
+height=480
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/square_pal b/profiles/square_pal
new file mode 100644 (file)
index 0000000..a69c8d6
--- /dev/null
@@ -0,0 +1,10 @@
+description=Square PAL
+frame_rate_num=25
+frame_rate_den=1
+width=768
+height=576
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/square_pal_wide b/profiles/square_pal_wide
new file mode 100644 (file)
index 0000000..316a80d
--- /dev/null
@@ -0,0 +1,10 @@
+description=Square PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=1024
+height=576
+progressive=1
+sample_aspect_num=1
+sample_aspect_den=1
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/svcd_ntsc b/profiles/svcd_ntsc
new file mode 100644 (file)
index 0000000..fd6f13a
--- /dev/null
@@ -0,0 +1,10 @@
+description=SVCD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=480
+height=480
+progressive=0
+sample_aspect_num=15
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/svcd_ntsc_wide b/profiles/svcd_ntsc_wide
new file mode 100644 (file)
index 0000000..174960a
--- /dev/null
@@ -0,0 +1,10 @@
+description=SVCD NTSC Widescreen
+frame_rate_num=30000
+frame_rate_den=1001
+width=480
+height=480
+progressive=0
+sample_aspect_num=20
+sample_aspect_den=11
+display_aspect_num=16
+display_aspect_den=9
diff --git a/profiles/svcd_pal b/profiles/svcd_pal
new file mode 100644 (file)
index 0000000..2049270
--- /dev/null
@@ -0,0 +1,10 @@
+description=SVCD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=480
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=36
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/svcd_pal_wide b/profiles/svcd_pal_wide
new file mode 100644 (file)
index 0000000..3aea87a
--- /dev/null
@@ -0,0 +1,10 @@
+description=SVCD PAL Widescreen
+frame_rate_num=25
+frame_rate_den=1
+width=480
+height=576
+progressive=0
+sample_aspect_num=59
+sample_aspect_den=27
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/vcd_ntsc b/profiles/vcd_ntsc
new file mode 100644 (file)
index 0000000..e58f4f2
--- /dev/null
@@ -0,0 +1,10 @@
+description=VCD NTSC
+frame_rate_num=30000
+frame_rate_den=1001
+width=352
+height=240
+progressive=1
+sample_aspect_num=10
+sample_aspect_den=11
+display_aspect_num=4
+display_aspect_den=3
diff --git a/profiles/vcd_pal b/profiles/vcd_pal
new file mode 100644 (file)
index 0000000..5f4e03f
--- /dev/null
@@ -0,0 +1,10 @@
+description=VCD PAL
+frame_rate_num=25
+frame_rate_den=1
+width=352
+height=288
+progressive=1
+sample_aspect_num=59
+sample_aspect_den=54
+display_aspect_num=4
+display_aspect_den=3
diff --git a/setenv b/setenv
new file mode 100644 (file)
index 0000000..53b5f6e
--- /dev/null
+++ b/setenv
@@ -0,0 +1,26 @@
+
+# Environment variable settings to allow execution without install
+
+export MLT_REPOSITORY=`pwd`/src/modules
+export MLT_DATA=`pwd`/src/modules
+export MLT_PROFILES_PATH=`pwd`/profiles
+
+export LD_LIBRARY_PATH=\
+`pwd`/src/framework:\
+`pwd`/src/valerie:\
+`pwd`/src/miracle:\
+`pwd`/src/modules/bluefish:\
+`pwd`/../BlueLinuxDriver/install/lib:\
+`pwd`/../mpeg_sdk_release/bin:\
+`pwd`/../dvcpro_sdk_release/lib:\
+`pwd`/../sr_sdk_release:\
+$LD_LIBRARY_PATH
+
+[ $(uname -s) = Darwin ] && export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
+
+export PATH=\
+`pwd`/src/albino:\
+`pwd`/src/inigo:\
+`pwd`/src/humperdink:\
+`pwd`/src/miracle:\
+$PATH
diff --git a/setenv_mc b/setenv_mc
new file mode 100644 (file)
index 0000000..705d4e2
--- /dev/null
+++ b/setenv_mc
@@ -0,0 +1,9 @@
+
+# Environment variable settings to allow execution without install
+
+export LD_LIBRARY_PATH=\
+`pwd`/../mpeg_sdk_release/bin:\
+`pwd`/../dvcpro_sdk_release/lib:\
+`pwd`/../sr_sdk_release/lib:\
+$LD_LIBRARY_PATH
+
diff --git a/src/albino/Makefile b/src/albino/Makefile
new file mode 100644 (file)
index 0000000..29bb3ee
--- /dev/null
@@ -0,0 +1,38 @@
+include ../../config.mak
+
+TARGET = albino
+
+OBJS = albino.o
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../miracle -lmiracle
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install:       all
+       install -d "$(DESTDIR)$(bindir)"
+       install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+       rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/albino/albino.c b/src/albino/albino.c
new file mode 100644 (file)
index 0000000..3979a51
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * albino.c -- Local Valerie/Miracle Test Utility
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+
+#ifdef __DARWIN__
+#include <SDL.h>
+#endif
+
+/* Application header files */
+#include <miracle/miracle_local.h>
+#include <valerie/valerie_remote.h>
+#include <valerie/valerie_util.h>
+
+char *prompt( char *command, int length )
+{
+       printf( "> " );
+       return fgets( command, length, stdin );
+}
+
+void report( valerie_response response )
+{
+       int index = 0;
+       if ( response != NULL )
+               for ( index = 0; index < valerie_response_count( response ); index ++ )
+                       printf( "%4d: %s\n", index, valerie_response_get_line( response, index ) );
+}
+
+int main( int argc, char **argv  )
+{
+       valerie_parser parser = NULL;
+       valerie_response response = NULL;
+       char temp[ 1024 ];
+       int index = 1;
+
+       if ( argc > 2 && !strcmp( argv[ 1 ], "-s" ) )
+       {
+               printf( "Miracle Client Instance\n" );
+               parser = valerie_parser_init_remote( argv[ 2 ], 5250 );
+               response = valerie_parser_connect( parser );
+               index = 3;
+       }
+       else
+       {
+               printf( "Miracle Standalone Instance\n" );
+               parser = miracle_parser_init_local( );
+               response = valerie_parser_connect( parser );
+       }
+
+       if ( response != NULL )
+       {
+               /* process files on command lines before going into console mode */
+               for ( ; index < argc; index ++ )
+               {
+                       valerie_response_close( response );
+                       response = valerie_parser_run( parser, argv[ index ] );
+                       report( response );
+               }
+       
+               while ( response != NULL && prompt( temp, 1024 ) )
+               {
+                       valerie_util_trim( valerie_util_chomp( temp ) );
+                       if ( !strcasecmp( temp, "BYE" ) )
+                       {
+                               break;
+                       }
+                       else if ( strcmp( temp, "" ) )
+                       {
+                               valerie_response_close( response );
+                               response = valerie_parser_execute( parser, temp );
+                               report( response );
+                       }
+               }
+       }
+       else
+       {
+               fprintf( stderr, "Unable to connect to a Miracle instance.\n" );
+       }
+
+       printf( "\n" );
+       valerie_parser_close( parser );
+
+       return 0;
+}
diff --git a/src/framework/Makefile b/src/framework/Makefile
new file mode 100644 (file)
index 0000000..58b3899
--- /dev/null
@@ -0,0 +1,109 @@
+include ../../config.mak
+
+NAME = libmlt$(LIBSUF)
+TARGET = $(NAME).$(version)
+
+ifneq ($(targetos), Darwin)
+NAME = libmlt$(LIBSUF)
+TARGET = $(NAME).$(version)
+SONAME = $(NAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(SONAME)
+else
+NAME = libmlt$(LIBSUF)
+TARGET = libmlt.$(version)$(LIBSUF)
+SONAME = libmlt.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+OBJS = mlt_frame.o \
+          mlt_geometry.o \
+          mlt_deque.o \
+          mlt_property.o \
+          mlt_properties.o \
+          mlt_events.o \
+          mlt_parser.o \
+          mlt_service.o \
+          mlt_producer.o \
+          mlt_multitrack.o \
+          mlt_playlist.o \
+          mlt_consumer.o \
+          mlt_filter.o \
+          mlt_transition.o \
+          mlt_field.o \
+          mlt_tractor.o \
+          mlt_factory.o \
+          mlt_repository.o \
+          mlt_pool.o \
+          mlt_tokeniser.o \
+          mlt_profile.o \
+          mlt_log.o \
+          mlt_cache.o
+
+INCS = mlt_consumer.h \
+          mlt_factory.h \
+          mlt_filter.h \
+          mlt.h \
+          mlt_multitrack.h \
+          mlt_pool.h \
+          mlt_properties.h \
+          mlt_events.h \
+          mlt_parser.h \
+          mlt_repository.h \
+          mlt_tractor.h \
+          mlt_types.h \
+          mlt_deque.h \
+          mlt_field.h \
+          mlt_frame.h \
+          mlt_geometry.h \
+          mlt_playlist.h \
+          mlt_producer.h \
+          mlt_property.h \
+          mlt_service.h  \
+          mlt_transition.h \
+          mlt_tokeniser.h \
+          mlt_profile.h \
+          mlt_log.h \
+          mlt_cache.h
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS += $(RDYNAMIC) -DPREFIX="\"$(prefix)\"" -DLIBDIR="\"$(libdir)\""
+
+LDFLAGS += -lm $(LIBDL) -lpthread
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+               ln -sf $(TARGET) $(NAME)
+               ln -sf $(TARGET) $(SONAME)
+
+depend:        $(SRCS)
+       $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) $(NAME)
+
+install:
+       install -d $(DESTDIR)$(libdir)
+       install -m 755 $(TARGET) $(DESTDIR)$(libdir)
+       ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME)
+       ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME)
+       install -d "$(DESTDIR)$(prefix)/include/mlt/framework"
+       install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/framework"
+       install -d "$(DESTDIR)$(prefix)/share/mlt"
+       install -m 644 metaschema.yaml "$(DESTDIR)$(prefix)/share/mlt/"
+
+uninstall:
+       rm -f "$(DESTDIR)$(libdir)/$(TARGET)"
+       rm -f "$(DESTDIR)$(libdir)/$(SONAME)"
+       rm -f "$(DESTDIR)$(libdir)/$(NAME)"
+       rm -rf "$(DESTDIR)$(prefix)/include/mlt/framework"
+       rm "$(DESTDIR)$(prefix)/share/mlt/metaschema.yaml"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/framework/configure b/src/framework/configure
new file mode 100755 (executable)
index 0000000..52655ae
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo "framework        -I$prefix/include -I$prefix/include/mlt -D_REENTRANT    -L$libdir -lmlt" >> ../../packages.dat
diff --git a/src/framework/metaschema.yaml b/src/framework/metaschema.yaml
new file mode 100644 (file)
index 0000000..9c7c78f
--- /dev/null
@@ -0,0 +1,129 @@
+--- # A metadata schema in Kwalify: http://www.kuwata-lab.com/kwalify/
+# Version: 0.1 
+type: map
+mapping:
+  "schema_version": # This should match the version comment above
+    type: float
+    required: yes
+  "type": # A service type
+    type: str
+    required: yes
+    enum: [consumer, filter, producer, transition]
+  "identifier": # The same value used to register and create the service
+    type: str
+    required: yes
+    unique: yes
+  "title": # The UI can use this for a field label
+    type: str
+  "copyright": # Who owns the rights to the module and/or service?
+    type: str
+  "version": # The version of the service implementation
+    type: text
+  "license": # The software license for the service implementation
+    type: str
+  "language": # A 2 character ISO 639-1 language code
+    type: str
+    required: yes
+  "url": # A hyperlink to a related website
+    type: str
+  "creator": # The name and/or e-mail address of the original author
+    type: str
+  "contributor": # The name and/or e-mail of all source code contributors
+    type: seq
+    sequence:
+      - type: str
+  "tags": # A set of categories, this might become an enum
+    type: seq
+    sequence:
+      - type: str
+  "description": # A slightly longer description than title
+    type: str
+  "icon": # A graphical representation of the effect
+    type: map
+    mapping:
+      "filename":
+        type: str
+      "content-type":
+        type: str
+      "content-encoding":
+        type: str
+      "content":
+        type: str
+  "notes": # Details about the usage and/or implementation - can be long
+    type: str
+  "bugs": # A list of known problems that users can try to avoid
+    type: seq
+    sequence:
+      - type: str # Can be a sentence or paragraph, preferably not a hyperlink
+  "parameters": # A list of all of the options for the service
+    type: seq
+    sequence:
+      - type: map
+        mapping:
+          "identifier": # The key that must be used to set the mlt_property
+            type: str
+            required: yes
+          "type": # An mlt_property_type
+            type: str
+            enum:
+              - float
+              - geometry
+              - integer
+              - properties # for passing options to encapsulated services
+              - string
+              - time # currently, mlt_position (frame), soon to be a time value
+          "service-name": # for type: properties, a reference to another service
+            type: str     # format: type.service, e.g. transition.composite
+          "title": # A UI can use this for a field label
+            type: str
+          "description": # A UI can use this for a tool tip or what's-this
+            type: str
+          "readonly": # If you set this property, it will be ignored
+            type: bool
+            default: no
+          "required": # Is this property required?
+            type: bool
+            default: no
+          "mutable": # The service will change behavior if this is set after
+                     # processing the first frame
+            type: bool
+            default: no
+          "widget": # A hint to the UI about how to let the user set this
+            type: str
+            enum:
+              - checkbox
+              - color
+              - combo
+              - curve
+              - directory
+              - fileopen
+              - filesave
+              - font
+              - knob
+              - listbox
+              - dropdown # aka HTML select or GtkOptionMenu
+              - radio
+              - rectangle # for use with type: geometry
+              - slider
+              - spinner
+              - text
+              - textbox # multi-line
+              - timecode
+          "minimum": # For numeric types, the minimal value
+            type: number
+          "maximum": # For numeric types, the maximal value 
+            type: number
+          "default":     # The default value to be used in a UI
+            type: scalar # If not specified, the UI might be able to display
+                         # this as blank
+          "unit": # A UI can display this as a label after the widget (e.g. %)
+            type: str
+          "scale": # the number of digits after decimal point when type: float
+            type: int
+          "format": # A hint about a custom string encoding, possibly scanf
+          "values": # A list of acceptable string values
+            type: seq # A UI can allow something outside the list with
+                      # widget: combo or if "other" is in this sequence
+            sequence:
+              - type: scalar
diff --git a/src/framework/mlt.h b/src/framework/mlt.h
new file mode 100644 (file)
index 0000000..6836540
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * \file mlt.h
+ * \brief header file for lazy client and implementation code :-)
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_H_
+#define _MLT_H_
+
+#define LIBMLT_VERSION_INT ((0<<16)+(3<<8)+9)
+#define LIBMLT_VERSION     0.3.9
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "mlt_factory.h"
+#include "mlt_frame.h"
+#include "mlt_deque.h"
+#include "mlt_multitrack.h"
+#include "mlt_producer.h"
+#include "mlt_transition.h"
+#include "mlt_consumer.h"
+#include "mlt_filter.h"
+#include "mlt_playlist.h"
+#include "mlt_properties.h"
+#include "mlt_field.h"
+#include "mlt_tractor.h"
+#include "mlt_tokeniser.h"
+#include "mlt_parser.h"
+#include "mlt_geometry.h"
+#include "mlt_profile.h"
+#include "mlt_repository.h"
+#include "mlt_log.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/framework/mlt_cache.c b/src/framework/mlt_cache.c
new file mode 100644 (file)
index 0000000..5a18871
--- /dev/null
@@ -0,0 +1,451 @@
+/**
+ * \file mlt_profile.c
+ * \brief least recently used cache
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "mlt_types.h"
+#include "mlt_log.h"
+#include "mlt_properties.h"
+#include "mlt_cache.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+/** the maximum number of data objects to cache per line */
+#define CACHE_SIZE (10)
+
+/** \brief Cache item class
+ *
+ * A cache item is a structure holding information about a data object including
+ * a reference count that is used to control its lifetime. When you get a
+ * a cache item from the cache, you hold a reference that prevents the data
+ * from being released when the cache is full and something new is added.
+ * When you close the cache item, the reference count is decremented.
+ * The data object is destroyed when all cache items are closed and the cache
+ * releases its reference.
+ */
+
+typedef struct mlt_cache_item_s
+{
+       mlt_cache cache;           /**< a reference to the cache to which this belongs */
+       void *object;              /**< a parent object to the cache data that uniquely identifies this cached item */
+       void *data;                /**< the opaque pointer to the cached data */
+       int size;                  /**< the size of the cached data */
+       int refcount;              /**< a reference counter to control when destructor is called */
+       mlt_destructor destructor; /**< a function to release or destroy the cached data */
+} mlt_cache_item_s;
+
+/** \brief Cache class
+ *
+ * This is a utility class for implementing a Least Recently Used (LRU) cache
+ * of data blobs indexed by the address of some other object (e.g., a service).
+ * Instead of sorting and manipulating linked lists, it tries to be simple and
+ * elegant by copying pointers between two arrays of fixed size to shuffle the
+ * order of elements.
+ *
+ * This class is useful if you have a service that wants to cache something
+ * somewhat large, but will not scale if there are many instances of the service.
+ * Of course, the service will need to know how to recreate the cached element
+ * if it gets flushed from the cache,
+ *
+ * The most obvious examples are the pixbuf and qimage producers that cache their
+ * respective objects representing a picture read from a file. If the picture
+ * is no longer in the cache, it can simply re-read it from file. However, a
+ * picture is often repeated over many frames and makes sense to cache instead
+ * of continually reading, parsing, and decoding. On the other hand, you might
+ * want to load hundreds of pictures as individual producers, which would use
+ * a lot of memory if every picture is held in memory!
+ */
+
+struct mlt_cache_s
+{
+       int count;             /**< the number of items currently in the cache */
+       void* *current;        /**< pointer to the current array of pointers */
+       void* A[ CACHE_SIZE ];
+       void* B[ CACHE_SIZE ];
+       pthread_mutex_t mutex; /**< a mutex to prevent multi-threaded race conditions */
+       mlt_properties active; /**< a list of cache items some of which may no longer
+                                   be in \p current but to which there are
+                                   outstanding references */
+       mlt_properties garbage;/**< a list cache items pending release. A cache item
+                                   is copied to this list when it is updated but there
+                                   are outstanding references to the old data object. */
+};
+
+/** Get the data pointer from the cache item.
+ *
+ * \public \memberof mlt_cache_s
+ * \param item a cache item
+ * \param[out] size the number of bytes pointed at, if supplied when putting the data into the cache
+ * \return the data pointer
+ */
+
+void *mlt_cache_item_data( mlt_cache_item item, int *size )
+{
+       if ( size && item )
+               *size = item->size;
+       return item? item->data : NULL;
+}
+
+/** Close a cache item given its parent object pointer.
+ *
+ * \private \memberof mlt_cache_s
+ * \param cache a cache
+ * \param object the object to which the data object belongs
+ * \param data the data object, which might be in the garbage list (optional)
+ */
+
+static void cache_object_close( mlt_cache cache, void *object, void* data )
+{
+       char key[19];
+
+       // Fetch the cache item from the active list by its owner's address
+       sprintf( key, "%p", object );
+       pthread_mutex_lock( &cache->mutex );
+       mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+       if ( item )
+       {
+               mlt_log( NULL, MLT_LOG_DEBUG, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__,
+                       item, item->object, item->data, item->refcount );
+               if ( item->destructor && --item->refcount <= 0 )
+               {
+                       // Destroy the data object
+                       item->destructor( item->data );
+                       item->data = NULL;
+                       item->destructor = NULL;
+                       // Do not dispose of the cache item because it could likely be used
+                       // again.
+               }
+       }
+
+       // Fetch the cache item from the garbage collection by its data address
+       if ( data )
+       {
+               sprintf( key, "%p", data );
+               item = mlt_properties_get_data( cache->garbage, key, NULL );
+               if ( item )
+               {
+                       mlt_log( NULL, MLT_LOG_DEBUG, "collecting garbage item %p object %p data %p refcount %d\n",
+                               item, item->object, item->data, item->refcount );
+                       if ( item->destructor && --item->refcount <= 0 )
+                       {
+                               item->destructor( item->data );
+                               item->data = NULL;
+                               item->destructor = NULL;
+                               // We do not need the garbage-collected cache item
+                               mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL );
+                       }
+               }
+       }
+       pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Close a cache item.
+ *
+ * Release a reference and call the destructor on the data object when all
+ * references are released.
+ *
+ * \public \memberof mlt_cache_item_s
+ * \param item a cache item
+ */
+
+void mlt_cache_item_close( mlt_cache_item item )
+{
+       if ( item )
+               cache_object_close( item->cache, item->object, item->data );
+}
+
+/** Create a new cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \return a new cache or NULL if there was an error
+ */
+
+mlt_cache mlt_cache_init()
+{
+       mlt_cache result = calloc( 1, sizeof( struct mlt_cache_s ) );
+       if ( result )
+       {
+               result->current = result->A;
+               pthread_mutex_init( &result->mutex, NULL );
+               result->active = mlt_properties_new();
+               result->garbage = mlt_properties_new();
+       }
+       return result;
+}
+
+/** Destroy a cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache the cache to detroy
+ */
+
+void mlt_cache_close( mlt_cache cache )
+{
+       if ( cache )
+       {
+               while ( cache->count-- )
+               {
+                       void *object = cache->current[ cache->count ];
+                       mlt_log( NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object );
+                       cache_object_close( cache, object, NULL );
+               }
+               mlt_properties_close( cache->active );
+               mlt_properties_close( cache->garbage );
+               pthread_mutex_destroy( &cache->mutex );
+               free( cache );
+       }
+}
+
+/** Remove cache entries for an object.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache
+ * \param object the object that owns the cached data
+ */
+
+void mlt_cache_purge( mlt_cache cache, void *object )
+{
+       pthread_mutex_lock( &cache->mutex );
+       if ( cache && object )
+       {
+               int i, j;
+               void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+               for ( i = 0, j = 0; i < cache->count; i++ )
+               {
+                       void *o = cache->current[ i ];
+
+                       if ( o == object )
+                       {
+                               pthread_mutex_unlock( &cache->mutex );
+                               cache_object_close( cache, o, NULL );
+                               pthread_mutex_lock( &cache->mutex );
+                       }
+                       else
+                       {
+                               alt[ j++ ] = o;
+                       }
+               }
+               cache->count = j;
+               cache->current = alt;
+
+               // Remove the object's data from the active list regardless of refcount
+               char key[19];
+               sprintf( key, "%p", object );
+               mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+               if ( item && item->destructor )
+               {
+                       item->destructor( item->data );
+                       item->data = NULL;
+                       item->destructor = NULL;
+                       mlt_properties_set_data( cache->active, key, NULL, 0, NULL, NULL );
+               }
+
+               // Remove the object's items from the garbage collection regardless of refcount
+               i = mlt_properties_count( cache->garbage );
+               while ( i-- )
+               {
+                       item = mlt_properties_get_data_at( cache->garbage, i, NULL );
+                       if ( object == item->object && item->destructor )
+                       {
+                               sprintf( key, "%p", item->data );
+                               item->destructor( item->data );
+                               item->data = NULL;
+                               item->destructor = NULL;
+                               mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL );
+                       }
+               }
+       }
+       pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Shuffle the cache entries between the two arrays and return the cache entry for an object.
+ *
+ * \private \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object that owns the cached data
+ * \return a cache entry if there was a hit or NULL for a miss
+ */
+
+static void** shuffle_get_hit( mlt_cache cache, void *object )
+{
+       int i = cache->count;
+       int j = cache->count - 1;
+       void **hit = NULL;
+       void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+       if ( cache->count > 0 && cache->count < CACHE_SIZE )
+       {
+               // first determine if we have a hit
+               while ( i-- && !hit )
+               {
+                       void **o = &cache->current[ i ];
+                       if ( *o == object )
+                               hit = o;
+               }
+               // if there was no hit, we will not be shuffling out an entry
+               // and are still filling the cache
+               if ( !hit )
+                       ++j;
+               // reset these
+               i = cache->count;
+               hit = NULL;
+       }
+
+       // shuffle the existing entries to the alternate array
+       while ( i-- )
+       {
+               void **o = &cache->current[ i ];
+
+               if ( !hit && *o == object )
+               {
+                       hit = o;
+               }
+               else if ( j > 0 )
+               {
+                       alt[ --j ] = *o;
+//                     mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] );
+               }
+       }
+       return hit;
+}
+
+/** Put a chunk of data in the cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object to which this data belongs
+ * \param data an opaque pointer to the data to cache
+ * \param size the size of the data in bytes
+ * \param destructor a pointer to a function that can destroy or release a reference to the data.
+ */
+
+void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor )
+{
+       pthread_mutex_lock( &cache->mutex );
+       void **hit = shuffle_get_hit( cache, object );
+       void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+       // add the object to the cache
+       if ( hit )
+       {
+               // release the old data
+               pthread_mutex_unlock( &cache->mutex );
+               cache_object_close( cache, *hit, NULL );
+               pthread_mutex_lock( &cache->mutex );
+               // the MRU end gets the updated data
+               hit = &alt[ cache->count - 1 ];
+       }
+       else if ( cache->count < CACHE_SIZE )
+       {
+               // more room in cache, add it to MRU end
+               hit = &alt[ cache->count++ ];
+       }
+       else
+       {
+               // release the entry at the LRU end
+               pthread_mutex_unlock( &cache->mutex );
+               cache_object_close( cache, cache->current[0], NULL );
+               pthread_mutex_lock( &cache->mutex );
+
+               // The MRU end gets the new item
+               hit = &alt[ cache->count - 1 ];
+       }
+       *hit = object;
+       mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p, %p\n", __FUNCTION__, cache->count - 1, object, data );
+
+       // Fetch the cache item
+       char key[19];
+       sprintf( key, "%p", object );
+       mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL );
+       if ( !item )
+       {
+               item = calloc( 1, sizeof( mlt_cache_item_s ) );
+               if ( item )
+                       mlt_properties_set_data( cache->active, key, item, 0, free, NULL );
+       }
+       if ( item )
+       {
+               // If updating the cache item but not all references are released
+               // copy the item to the garbage collection.
+               if ( item->refcount > 0 && item->data )
+               {
+                       mlt_cache_item orphan = calloc( 1, sizeof( mlt_cache_item_s ) );
+                       if ( orphan )
+                       {
+                               mlt_log( NULL, MLT_LOG_DEBUG, "adding to garbage collection object %p data %p\n", item->object, item->data );
+                               *orphan = *item;
+                               sprintf( key, "%p", orphan->data );
+                               // We store in the garbage collection by data address, not the owner's!
+                               mlt_properties_set_data( cache->garbage, key, orphan, 0, free, NULL );
+                       }
+               }
+
+               // Set/update the cache item
+               item->cache = cache;
+               item->object = object;
+               item->data = data;
+               item->size = size;
+               item->destructor = destructor;
+               item->refcount = 1;
+       }
+       
+       // swap the current array
+       cache->current = alt;
+       pthread_mutex_unlock( &cache->mutex );
+}
+
+/** Get a chunk of data from the cache.
+ *
+ * \public \memberof mlt_cache_s
+ * \param cache a cache object
+ * \param object the object for which you are trying to locate the data
+ * \return a mlt_cache_item if found or NULL if not found or has been flushed from the cache
+ */
+
+mlt_cache_item mlt_cache_get( mlt_cache cache, void *object )
+{
+       mlt_cache_item result = NULL;
+       pthread_mutex_lock( &cache->mutex );
+       void **hit = shuffle_get_hit( cache, object );
+       void **alt = cache->current == cache->A ? cache->B : cache->A;
+
+       if ( hit )
+       {
+               // copy the hit to the MRU end
+               alt[ cache->count - 1 ] = *hit;
+               hit = &alt[ cache->count - 1 ];
+
+               char key[19];
+               sprintf( key, "%p", *hit );
+               result = mlt_properties_get_data( cache->active, key, NULL );
+               if ( result && result->data )
+                       result->refcount++;
+               mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p, %p\n", __FUNCTION__, cache->count - 1, *hit, result->data );
+
+               // swap the current array
+               cache->current = alt;
+       }
+       pthread_mutex_unlock( &cache->mutex );
+       
+       return result;
+}
diff --git a/src/framework/mlt_cache.h b/src/framework/mlt_cache.h
new file mode 100644 (file)
index 0000000..aaec935
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * \file mlt_cache.h
+ * \brief least recently used cache
+ * \see mlt_cache_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 _MLT_CACHE_H
+#define _MLT_CACHE_H
+
+#include "mlt_types.h"
+
+extern void *mlt_cache_item_data( mlt_cache_item item, int *size );
+extern void mlt_cache_item_close( mlt_cache_item item );
+
+extern mlt_cache mlt_cache_init();
+extern void mlt_cache_close( mlt_cache cache );
+extern void mlt_cache_purge( mlt_cache cache, void *object );
+extern void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor );
+extern mlt_cache_item mlt_cache_get( mlt_cache cache, void *object );
+
+#endif
diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c
new file mode 100644 (file)
index 0000000..93afe44
--- /dev/null
@@ -0,0 +1,1010 @@
+/**
+ * \file mlt_consumer.c
+ * \brief abstraction for all consumer services
+ * \see mlt_consumer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_consumer.h"
+#include "mlt_factory.h"
+#include "mlt_producer.h"
+#include "mlt_frame.h"
+#include "mlt_profile.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+/** Define this if you want an automatic deinterlace (if necessary) when the
+ * consumer's producer is not running at normal speed.
+ */
+#undef DEINTERLACE_ON_NOT_NORMAL_SPEED
+
+static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name );
+static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties );
+
+/** Initialize a consumer service.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this the consumer to initialize
+ * \param child a pointer to the object for the subclass
+ * \param profile the \p mlt_profile_s to use (optional but recommended,
+ * uses the environment variable MLT if this is NULL)
+ * \return true if there was an error
+ */
+
+int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile )
+{
+       int error = 0;
+       memset( this, 0, sizeof( struct mlt_consumer_s ) );
+       this->child = child;
+       error = mlt_service_init( &this->parent, this );
+       if ( error == 0 )
+       {
+               // Get the properties from the service
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+
+               // Apply profile to properties
+               if ( profile == NULL )
+               {
+                       // Normally the application creates the profile and controls its lifetime
+                       // This is the fallback exception handling
+                       profile = mlt_profile_init( NULL );
+                       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+                       mlt_properties_set_data( properties, "_profile", profile, 0, (mlt_destructor)mlt_profile_close, NULL );
+               }
+               apply_profile_properties( this, profile, properties );
+
+               // Default rescaler for all consumers
+               mlt_properties_set( properties, "rescale", "bilinear" );
+
+               // Default read ahead buffer size
+               mlt_properties_set_int( properties, "buffer", 25 );
+
+               // Default audio frequency and channels
+               mlt_properties_set_int( properties, "frequency", 48000 );
+               mlt_properties_set_int( properties, "channels", 2 );
+
+               // Default of all consumers is real time
+               mlt_properties_set_int( properties, "real_time", 1 );
+
+               // Default to environment test card
+               mlt_properties_set( properties, "test_card", mlt_environment( "MLT_TEST_CARD" ) );
+
+               // Hmm - default all consumers to yuv422 :-/
+               this->format = mlt_image_yuv422;
+
+               mlt_events_register( properties, "consumer-frame-show", ( mlt_transmitter )mlt_consumer_frame_show );
+               mlt_events_register( properties, "consumer-frame-render", ( mlt_transmitter )mlt_consumer_frame_render );
+               mlt_events_register( properties, "consumer-stopped", NULL );
+
+               // Register a property-changed listener to handle the profile property -
+               // subsequent properties can override the profile
+               this->event_listener = mlt_events_listen( properties, this, "property-changed", ( mlt_listener )mlt_consumer_property_changed );
+
+               // Create the push mutex and condition
+               pthread_mutex_init( &this->put_mutex, NULL );
+               pthread_cond_init( &this->put_cond, NULL );
+
+       }
+       return error;
+}
+
+/** Convert the profile into properties on the consumer.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param profile a profile
+ * \param properties a properties list (typically, the consumer's)
+ */
+
+static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties )
+{
+       mlt_event_block( this->event_listener );
+       mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+       mlt_properties_set_int( properties, "frame_rate_num", profile->frame_rate_num );
+       mlt_properties_set_int( properties, "frame_rate_den", profile->frame_rate_den );
+       mlt_properties_set_int( properties, "width", profile->width );
+       mlt_properties_set_int( properties, "height", profile->height );
+       mlt_properties_set_int( properties, "progressive", profile->progressive );
+       mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
+       mlt_properties_set_int( properties, "sample_aspect_num", profile->sample_aspect_num );
+       mlt_properties_set_int( properties, "sample_aspect_den", profile->sample_aspect_den );
+       mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
+       mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
+       mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
+       mlt_event_unblock( this->event_listener );
+}
+
+/** The property-changed event listener
+ *
+ * \private \memberof mlt_consumer_s
+ * \param owner the service a service (ignored)
+ * \param this the consumer
+ * \param name the name of the property that changed
+ */
+
+static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name )
+{
+       if ( !strcmp( name, "profile" ) )
+       {
+               // Get the properies
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+               // Get the current profile
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+
+               // Load the new profile
+               mlt_profile new_profile = mlt_profile_init( mlt_properties_get( properties, name ) );
+
+               if ( new_profile )
+               {
+                       // Copy the profile
+                       if ( profile != NULL )
+                       {
+                               free( profile->description );
+                               memcpy( profile, new_profile, sizeof( struct mlt_profile_s ) );
+                               profile->description = strdup( new_profile->description );
+                               mlt_profile_close( new_profile );
+                       }
+                       else
+                       {
+                               profile = new_profile;
+                       }
+
+                       // Apply to properties
+                       apply_profile_properties( this, profile, properties );
+               }
+       }
+       else if ( !strcmp( name, "frame_rate_num" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+               {
+                       profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
+                       mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+               }
+       }
+       else if ( !strcmp( name, "frame_rate_den" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+               {
+                       profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
+                       mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+               }
+       }
+       else if ( !strcmp( name, "width" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+                       profile->width = mlt_properties_get_int( properties, "width" );
+       }
+       else if ( !strcmp( name, "height" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+                       profile->height = mlt_properties_get_int( properties, "height" );
+       }
+       else if ( !strcmp( name, "progressive" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+                       profile->progressive = mlt_properties_get_int( properties, "progressive" );
+       }
+       else if ( !strcmp( name, "sample_aspect_num" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
+               if ( profile )
+                       mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
+       }
+       else if ( !strcmp( name, "sample_aspect_den" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
+               if ( profile )
+                       mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
+       }
+       else if ( !strcmp( name, "display_aspect_num" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+               {
+                       profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
+                       mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
+               }
+       }
+       else if ( !strcmp( name, "display_aspect_den" ) )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+               {
+                       profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
+                       mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
+               }
+       }
+}
+
+/** The transmitter for the consumer-frame-show event
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param listener a function pointer that will be invoked
+ * \param owner  a properties list that will be passed to \p listener
+ * \param this  a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( mlt_frame )args[ 0 ] );
+}
+
+/** The transmitter for the consumer-frame-render event
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param listener a function pointer that will be invoked
+ * \param owner  a properties list that will be passed to \p listener
+ * \param this  a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( mlt_frame )args[ 0 ] );
+}
+
+/** Create a new consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param profile a profile (optional, but recommended)
+ * \return a new consumer
+ */
+
+mlt_consumer mlt_consumer_new( mlt_profile profile )
+{
+       // Create the memory for the structure
+       mlt_consumer this = malloc( sizeof( struct mlt_consumer_s ) );
+
+       // Initialise it
+       if ( this != NULL )
+               mlt_consumer_init( this, NULL, profile );
+
+       // Return it
+       return this;
+}
+
+/** Get the parent service object.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return the parent service class
+ * \see MLT_CONSUMER_SERVICE
+ */
+
+mlt_service mlt_consumer_service( mlt_consumer this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the consumer properties.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return the consumer's properties list
+ * \see MLT_CONSUMER_PROPERTIES
+ */
+
+mlt_properties mlt_consumer_properties( mlt_consumer this )
+{
+       return this != NULL ? MLT_SERVICE_PROPERTIES( &this->parent ) : NULL;
+}
+
+/** Connect the consumer to the producer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param producer a producer
+ * \return > 0 warning, == 0 success, < 0 serious error,
+ *         1 = this service does not accept input,
+ *         2 = the producer is invalid,
+ *         3 = the producer is already registered with this consumer
+ */
+
+int mlt_consumer_connect( mlt_consumer this, mlt_service producer )
+{
+       return mlt_service_connect_producer( &this->parent, producer, 0 );
+}
+
+/** Start the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if there was an error
+ */
+
+int mlt_consumer_start( mlt_consumer this )
+{
+       // Stop listening to the property-changed event
+       mlt_event_block( this->event_listener );
+
+       // Get the properies
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Determine if there's a test card producer
+       char *test_card = mlt_properties_get( properties, "test_card" );
+
+       // Just to make sure nothing is hanging around...
+       mlt_frame_close( this->put );
+       this->put = NULL;
+       this->put_active = 1;
+
+       // Deal with it now.
+       if ( test_card != NULL )
+       {
+               if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL )
+               {
+                       // Create a test card producer
+                       mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+                       mlt_producer producer = mlt_factory_producer( profile, NULL, test_card );
+
+                       // Do we have a producer
+                       if ( producer != NULL )
+                       {
+                               // Test card should loop I guess...
+                               mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+                               //mlt_producer_set_speed( producer, 0 );
+                               //mlt_producer_set_in_and_out( producer, 0, 0 );
+
+                               // Set the test card on the consumer
+                               mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+                       }
+               }
+       }
+       else
+       {
+               // Allow the hash table to speed things up
+               mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+       }
+
+       // Set the frame duration in microseconds for the frame-dropping heuristic
+       int frame_duration = 1000000 / mlt_properties_get_int( properties, "frame_rate_num" ) *
+                       mlt_properties_get_int( properties, "frame_rate_den" );
+       mlt_properties_set_int( properties, "frame_duration", frame_duration );
+
+       // Check and run an ante command
+       if ( mlt_properties_get( properties, "ante" ) )
+               if ( system( mlt_properties_get( properties, "ante" ) ) == -1 )
+                       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) );
+
+       // Set the real_time preference
+       this->real_time = mlt_properties_get_int( properties, "real_time" );
+
+       // Start the service
+       if ( this->start != NULL )
+               return this->start( this );
+
+       return 0;
+}
+
+/** An alternative method to feed frames into the consumer.
+ *
+ * Only valid if the consumer itself is not connected.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \param frame a frame
+ * \return true (ignore this for now)
+ */
+
+int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame )
+{
+       int error = 1;
+
+       // Get the service assoicated to the consumer
+       mlt_service service = MLT_CONSUMER_SERVICE( this );
+
+       if ( mlt_service_producer( service ) == NULL )
+       {
+               struct timeval now;
+               struct timespec tm;
+               pthread_mutex_lock( &this->put_mutex );
+               while ( this->put_active && this->put != NULL )
+               {
+                       gettimeofday( &now, NULL );
+                       tm.tv_sec = now.tv_sec + 1;
+                       tm.tv_nsec = now.tv_usec * 1000;
+                       pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
+               }
+               if ( this->put_active && this->put == NULL )
+                       this->put = frame;
+               else
+                       mlt_frame_close( frame );
+               pthread_cond_broadcast( &this->put_cond );
+               pthread_mutex_unlock( &this->put_mutex );
+       }
+       else
+       {
+               mlt_frame_close( frame );
+       }
+
+       return error;
+}
+
+/** Protected method for consumer to get frames from connected service
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return a frame
+ */
+
+mlt_frame mlt_consumer_get_frame( mlt_consumer this )
+{
+       // Frame to return
+       mlt_frame frame = NULL;
+
+       // Get the service assoicated to the consumer
+       mlt_service service = MLT_CONSUMER_SERVICE( this );
+
+       // Get the consumer properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get the frame
+       if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) )
+       {
+               struct timeval now;
+               struct timespec tm;
+               pthread_mutex_lock( &this->put_mutex );
+               while ( this->put_active && this->put == NULL )
+               {
+                       gettimeofday( &now, NULL );
+                       tm.tv_sec = now.tv_sec + 1;
+                       tm.tv_nsec = now.tv_usec * 1000;
+                       pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
+               }
+               frame = this->put;
+               this->put = NULL;
+               pthread_cond_broadcast( &this->put_cond );
+               pthread_mutex_unlock( &this->put_mutex );
+               if ( frame != NULL )
+                       mlt_service_apply_filters( service, frame, 0 );
+       }
+       else if ( mlt_service_producer( service ) != NULL )
+       {
+               mlt_service_get_frame( service, &frame, 0 );
+       }
+       else
+       {
+               frame = mlt_frame_init( service );
+       }
+
+       if ( frame != NULL )
+       {
+               // Get the frame properties
+               mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+               // Get the test card producer
+               mlt_producer test_card = mlt_properties_get_data( properties, "test_card_producer", NULL );
+
+               // Attach the test frame producer to it.
+               if ( test_card != NULL )
+                       mlt_properties_set_data( frame_properties, "test_card_producer", test_card, 0, NULL, NULL );
+
+               // Attach the rescale property
+               mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) );
+
+               // Aspect ratio and other jiggery pokery
+               mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "aspect_ratio" ) );
+               mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ) );
+               mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
+       }
+
+       // Return the frame
+       return frame;
+}
+
+/** Compute the time difference between now and a time value.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param time1 a time value to be compared against now
+ * \return the difference in microseconds
+ */
+
+static inline long time_difference( struct timeval *time1 )
+{
+       struct timeval time2;
+       time2.tv_sec = time1->tv_sec;
+       time2.tv_usec = time1->tv_usec;
+       gettimeofday( time1, NULL );
+       return time1->tv_sec * 1000000 + time1->tv_usec - time2.tv_sec * 1000000 - time2.tv_usec;
+}
+
+/** The thread procedure for asynchronously pulling frames through the service
+ * network connected to a consumer.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param arg a consumer
+ */
+
+static void *consumer_read_ahead_thread( void *arg )
+{
+       // The argument is the consumer
+       mlt_consumer this = arg;
+
+       // Get the properties of the consumer
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get the width and height
+       int width = mlt_properties_get_int( properties, "width" );
+       int height = mlt_properties_get_int( properties, "height" );
+
+       // See if video is turned off
+       int video_off = mlt_properties_get_int( properties, "video_off" );
+       int preview_off = mlt_properties_get_int( properties, "preview_off" );
+       int preview_format = mlt_properties_get_int( properties, "preview_format" );
+
+       // Get the audio settings
+       mlt_audio_format afmt = mlt_audio_pcm;
+       int counter = 0;
+       double fps = mlt_properties_get_double( properties, "fps" );
+       int channels = mlt_properties_get_int( properties, "channels" );
+       int frequency = mlt_properties_get_int( properties, "frequency" );
+       int samples = 0;
+       int16_t *pcm = NULL;
+
+       // See if audio is turned off
+       int audio_off = mlt_properties_get_int( properties, "audio_off" );
+
+       // Get the maximum size of the buffer
+       int buffer = mlt_properties_get_int( properties, "buffer" ) + 1;
+
+       // General frame variable
+       mlt_frame frame = NULL;
+       uint8_t *image = NULL;
+
+       // Time structures
+       struct timeval ante;
+
+       // Average time for get_frame and get_image
+       int count = 1;
+       int skipped = 0;
+       int64_t time_wait = 0;
+       int64_t time_frame = 0;
+       int64_t time_process = 0;
+       int skip_next = 0;
+       mlt_service lock_object = NULL;
+
+       if ( preview_off && preview_format != 0 )
+               this->format = preview_format;
+
+       // Get the first frame
+       frame = mlt_consumer_get_frame( this );
+
+       // Get the lock object
+       lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
+
+       // Lock it
+       if ( lock_object ) mlt_service_lock( lock_object );
+
+       // Get the image of the first frame
+       if ( !video_off )
+       {
+               mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
+               mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
+       }
+
+       if ( !audio_off )
+       {
+               samples = mlt_sample_calculator( fps, frequency, counter++ );
+               mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+       }
+
+       // Unlock the lock object
+       if ( lock_object ) mlt_service_unlock( lock_object );
+
+       // Mark as rendered
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+
+       // Get the starting time (can ignore the times above)
+       gettimeofday( &ante, NULL );
+
+       // Continue to read ahead
+       while ( this->ahead )
+       {
+               // Fetch width/height again
+               width = mlt_properties_get_int( properties, "width" );
+               height = mlt_properties_get_int( properties, "height" );
+
+               // Put the current frame into the queue
+               pthread_mutex_lock( &this->mutex );
+               while( this->ahead && mlt_deque_count( this->queue ) >= buffer )
+                       pthread_cond_wait( &this->cond, &this->mutex );
+               mlt_deque_push_back( this->queue, frame );
+               pthread_cond_broadcast( &this->cond );
+               pthread_mutex_unlock( &this->mutex );
+
+               time_wait += time_difference( &ante );
+
+               // Get the next frame
+               frame = mlt_consumer_get_frame( this );
+               time_frame += time_difference( &ante );
+
+               // If there's no frame, we're probably stopped...
+               if ( frame == NULL )
+                       continue;
+
+               // Attempt to fetch the lock object
+               lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
+
+               // Increment the count
+               count ++;
+
+               // Lock if there's a lock object
+               if ( lock_object ) mlt_service_lock( lock_object );
+
+               // All non normal playback frames should be shown
+               if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 )
+               {
+#ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
+#endif
+                       skipped = 0;
+                       time_frame = 0;
+                       time_process = 0;
+                       time_wait = 0;
+                       count = 1;
+                       skip_next = 0;
+               }
+
+               // Get the image
+               if ( !skip_next || this->real_time == -1 )
+               {
+                       // Get the image, mark as rendered and time it
+                       if ( !video_off )
+                       {
+                               mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
+                               mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
+                       }
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+               }
+               else
+               {
+                       // Increment the number of sequentially skipped frames
+                       skipped ++;
+                       skip_next = 0;
+
+                       // If we've reached an unacceptable level, reset everything
+                       if ( skipped > 5 )
+                       {
+                               skipped = 0;
+                               time_frame = 0;
+                               time_process = 0;
+                               time_wait = 0;
+                               count = 1;
+                       }
+               }
+
+               // Always process audio
+               if ( !audio_off )
+               {
+                       samples = mlt_sample_calculator( fps, frequency, counter++ );
+                       mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+               }
+
+               // Increment the time take for this frame
+               time_process += time_difference( &ante );
+
+               // Determine if the next frame should be skipped
+               if ( mlt_deque_count( this->queue ) <= 5 )
+               {
+                       int frame_duration = mlt_properties_get_int( properties, "frame_duration" );
+                       if ( ( ( time_wait + time_frame + time_process ) / count ) > frame_duration )
+                               skip_next = 1;
+               }
+
+               // Unlock if there's a lock object
+               if ( lock_object ) mlt_service_unlock( lock_object );
+       }
+
+       // Remove the last frame
+       mlt_frame_close( frame );
+
+       return NULL;
+}
+
+/** Start the read/render thread.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+static void consumer_read_ahead_start( mlt_consumer this )
+{
+       // We're running now
+       this->ahead = 1;
+
+       // Create the frame queue
+       this->queue = mlt_deque_init( );
+
+       // Create the mutex
+       pthread_mutex_init( &this->mutex, NULL );
+
+       // Create the condition
+       pthread_cond_init( &this->cond, NULL );
+
+       // Create the read ahead
+       if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "priority" ) )
+       {
+               struct sched_param priority;
+               priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( this ), "priority" );
+               pthread_attr_t thread_attributes;
+               pthread_attr_init( &thread_attributes );
+               pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER );
+               pthread_attr_setschedparam( &thread_attributes, &priority );
+               pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED );
+               pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM );
+               if ( pthread_create( &this->ahead_thread, &thread_attributes, consumer_read_ahead_thread, this ) < 0 )
+                       pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
+               pthread_attr_destroy( &thread_attributes );
+       }
+       else
+       {
+               pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
+       }
+}
+
+/** Stop the read/render thread.
+ *
+ * \private \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+static void consumer_read_ahead_stop( mlt_consumer this )
+{
+       // Make sure we're running
+       if ( this->ahead )
+       {
+               // Inform thread to stop
+               this->ahead = 0;
+
+               // Broadcast to the condition in case it's waiting
+               pthread_mutex_lock( &this->mutex );
+               pthread_cond_broadcast( &this->cond );
+               pthread_mutex_unlock( &this->mutex );
+
+               // Broadcast to the put condition in case it's waiting
+               pthread_mutex_lock( &this->put_mutex );
+               pthread_cond_broadcast( &this->put_cond );
+               pthread_mutex_unlock( &this->put_mutex );
+
+               // Join the thread
+               pthread_join( this->ahead_thread, NULL );
+
+               // Destroy the mutex
+               pthread_mutex_destroy( &this->mutex );
+
+               // Destroy the condition
+               pthread_cond_destroy( &this->cond );
+
+               // Wipe the queue
+               while ( mlt_deque_count( this->queue ) )
+                       mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+
+               // Close the queue
+               mlt_deque_close( this->queue );
+       }
+}
+
+/** Flush the read/render thread's buffer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_purge( mlt_consumer this )
+{
+       if ( this->ahead )
+       {
+               pthread_mutex_lock( &this->mutex );
+               while ( mlt_deque_count( this->queue ) )
+                       mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+               pthread_cond_broadcast( &this->cond );
+               pthread_mutex_unlock( &this->mutex );
+       }
+}
+
+/** Get the next frame from the producer connected to a consumer.
+ *
+ * Typically, one uses this instead of \p mlt_consumer_get_frame to make
+ * the asynchronous/real-time behavior configurable at runtime.
+ * You should close the frame returned from this when you are done with it.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return a frame
+ */
+
+mlt_frame mlt_consumer_rt_frame( mlt_consumer this )
+{
+       // Frame to return
+       mlt_frame frame = NULL;
+
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check if the user has requested real time or not
+       if ( this->real_time )
+       {
+               int size = 1;
+
+               // Is the read ahead running?
+               if ( this->ahead == 0 )
+               {
+                       int buffer = mlt_properties_get_int( properties, "buffer" );
+                       int prefill = mlt_properties_get_int( properties, "prefill" );
+                       consumer_read_ahead_start( this );
+                       if ( buffer > 1 )
+                               size = prefill > 0 && prefill < buffer ? prefill : buffer;
+               }
+
+               // Get frame from queue
+               pthread_mutex_lock( &this->mutex );
+               while( this->ahead && mlt_deque_count( this->queue ) < size )
+                       pthread_cond_wait( &this->cond, &this->mutex );
+               frame = mlt_deque_pop_front( this->queue );
+               pthread_cond_broadcast( &this->cond );
+               pthread_mutex_unlock( &this->mutex );
+       }
+       else
+       {
+               // Get the frame in non real time
+               frame = mlt_consumer_get_frame( this );
+
+               // This isn't true, but from the consumers perspective it is
+               if ( frame != NULL )
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
+       }
+
+       return frame;
+}
+
+/** Callback for the implementation to indicate a stopped condition.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_stopped( mlt_consumer this )
+{
+       mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "running", 0 );
+       mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-stopped", NULL );
+       mlt_event_unblock( this->event_listener );
+}
+
+/** Stop the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if there was an error
+ */
+
+int mlt_consumer_stop( mlt_consumer this )
+{
+       // Get the properies
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Just in case...
+       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping put waiting\n" );
+       pthread_mutex_lock( &this->put_mutex );
+       this->put_active = 0;
+       pthread_cond_broadcast( &this->put_cond );
+       pthread_mutex_unlock( &this->put_mutex );
+
+       // Stop the consumer
+       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping consumer\n" );
+       if ( this->stop != NULL )
+               this->stop( this );
+
+       // Check if the user has requested real time or not and stop if necessary
+       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping read_ahead\n" );
+       if ( mlt_properties_get_int( properties, "real_time" ) )
+               consumer_read_ahead_stop( this );
+
+       // Kill the test card
+       mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+
+       // Check and run a post command
+       if ( mlt_properties_get( properties, "post" ) )
+               if (system( mlt_properties_get( properties, "post" ) ) == -1 )
+                       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) );
+
+       mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopped\n" );
+
+       return 0;
+}
+
+/** Determine if the consumer is stopped.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ * \return true if the consumer is stopped
+ */
+
+int mlt_consumer_is_stopped( mlt_consumer this )
+{
+       // Check if the consumer is stopped
+       if ( this->is_stopped != NULL )
+               return this->is_stopped( this );
+
+       return 0;
+}
+
+/** Close and destroy the consumer.
+ *
+ * \public \memberof mlt_consumer_s
+ * \param this a consumer
+ */
+
+void mlt_consumer_close( mlt_consumer this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_CONSUMER_PROPERTIES( this ) ) <= 0 )
+       {
+               // Get the childs close function
+               void ( *consumer_close )( ) = this->close;
+
+               if ( consumer_close )
+               {
+                       // Just in case...
+                       //mlt_consumer_stop( this );
+
+                       this->close = NULL;
+                       consumer_close( this );
+               }
+               else
+               {
+                       // Make sure it only gets called once
+                       this->parent.close = NULL;
+
+                       // Destroy the push mutex and condition
+                       pthread_mutex_destroy( &this->put_mutex );
+                       pthread_cond_destroy( &this->put_cond );
+
+                       mlt_service_close( &this->parent );
+               }
+       }
+}
diff --git a/src/framework/mlt_consumer.h b/src/framework/mlt_consumer.h
new file mode 100644 (file)
index 0000000..736d0a2
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * \file mlt_consumer.h
+ * \brief abstraction for all consumer services
+ * \see mlt_consumer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_CONSUMER_H_
+#define _MLT_CONSUMER_H_
+
+#include "mlt_service.h"
+#include "mlt_events.h"
+#include <pthread.h>
+
+/** \brief Consumer abstract service class
+ *
+ * A consumer is a service that pulls audio and video from the connected
+ * producers, filters, and transitions. Typically a consumer is used to
+ * output audio and/or video to a device, file, or socket.
+ *
+ * \extends mlt_service_s
+ * \properties \em rescale the scaling algorithm to pass on to all scaling
+ * filters, defaults to "bilinear"
+ * \properties \em buffer the number of frames to use in the asynchronous
+ * render thread, defaults to 25
+ * \properties \em frequency the audio sample rate to use in Hertz, defaults to 48000
+ * \properties \em channels the number of audio channels to use, defaults to 2
+ * \properties \em real_time the asynchronous behavior: 1 (default) for asynchronous
+ * with frame dropping, -1 for asynchronous without frame dropping, 0 to disable (synchronous)
+ * \properties \em test_card the name of a resource to use as the test card, defaults to
+ * environment variable MLT_TEST_CARD. If undefined, the hard-coded default test card is
+ * white silence. A test card is what appears when nothing is produced.
+ * \event \em consumer-frame-show Subclass implementations should fire this.
+ * \event \em consumer-frame-render The abstract class fires this.
+ * \event \em consumer-stopped
+ * \properties \em fps video frames per second as floating point (read only)
+ * \properties \em frame_rate_num the numerator of the video frame rate, overrides \p mlt_profile_s
+ * \properties \em frame_rate_den the denominator of the video frame rate, overrides \p mlt_profile_s
+ * \properties \em width the horizontal video resolution, overrides \p mlt_profile_s
+ * \properties \em height the vertical video resolution, overrides \p mlt_profile_s
+ * \properties \em progressive a flag that indicates if the video is interlaced
+ * or progressive, overrides \p mlt_profile_s
+ * \properties \em aspect_ratio the video sample (pixel) aspect ratio as floating point (read only)
+ * \properties \em sample_aspect_num the numerator of the sample aspect ratio, overrides \p mlt_profile_s
+ * \properties \em sample_aspect_den the denominator of the sample aspect ratio, overrides \p mlt_profile_s
+ * \properties \em display_ratio the video frame aspect ratio as floating point (read only)
+ * \properties \em display_aspect_num the numerator of the video frame aspect ratio, overrides \p mlt_profile_s
+ * \properties \em display_aspect_den the denominator of the video frame aspect ratio, overrides \p mlt_profile_s
+ *
+ */
+
+struct mlt_consumer_s
+{
+       /** A consumer is a service. */
+       struct mlt_service_s parent;
+
+       /** Start the consumer to pull frames (virtual function).
+        *
+        * \param mlt_consumer a consumer
+        * \return true if there was an error
+        */
+       int ( *start )( mlt_consumer );
+
+       /** Stop the consumer (virtual function).
+        *
+        * \param mlt_consumer a consumer
+        * \return true if there was an error
+        */
+       int ( *stop )( mlt_consumer );
+
+       /** Get whether the consumer is running or stopped (virtual function).
+        *
+        * \param mlt_consumer a consumer
+        * \return true if the consumer is stopped
+        */
+       int ( *is_stopped )( mlt_consumer );
+
+       /** The destructor virtual function
+        *
+        * \param mlt_consumer a consumer
+        */
+       void ( *close )( mlt_consumer );
+
+       void *local; /**< \private instance object */
+       void *child; /**< \private the object of a subclass */
+
+       int real_time;
+       int ahead;
+       mlt_image_format format;
+       mlt_deque queue;
+       pthread_t ahead_thread;
+       pthread_mutex_t mutex;
+       pthread_cond_t cond;
+       pthread_mutex_t put_mutex;
+       pthread_cond_t put_cond;
+       mlt_frame put;
+       int put_active;
+       mlt_event event_listener;
+};
+
+#define MLT_CONSUMER_SERVICE( consumer )       ( &( consumer )->parent )
+#define MLT_CONSUMER_PROPERTIES( consumer )    MLT_SERVICE_PROPERTIES( MLT_CONSUMER_SERVICE( consumer ) )
+
+extern int mlt_consumer_init( mlt_consumer self, void *child, mlt_profile profile );
+extern mlt_consumer mlt_consumer_new( mlt_profile profile );
+extern mlt_service mlt_consumer_service( mlt_consumer self );
+extern mlt_properties mlt_consumer_properties( mlt_consumer self );
+extern int mlt_consumer_connect( mlt_consumer self, mlt_service producer );
+extern int mlt_consumer_start( mlt_consumer self );
+extern void mlt_consumer_purge( mlt_consumer self );
+extern int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame );
+extern mlt_frame mlt_consumer_get_frame( mlt_consumer self );
+extern mlt_frame mlt_consumer_rt_frame( mlt_consumer self );
+extern int mlt_consumer_stop( mlt_consumer self );
+extern int mlt_consumer_is_stopped( mlt_consumer self );
+extern void mlt_consumer_stopped( mlt_consumer self );
+extern void mlt_consumer_close( mlt_consumer );
+
+#endif
diff --git a/src/framework/mlt_deque.c b/src/framework/mlt_deque.c
new file mode 100644 (file)
index 0000000..ad65a2b
--- /dev/null
@@ -0,0 +1,398 @@
+/**
+ * \file mlt_deque.c
+ * \brief double ended queue
+ * \see mlt_deque_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+// Local header files
+#include "mlt_deque.h"
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Deque entry class
+ *
+ */
+
+typedef union
+{
+       void *addr;
+       int value;
+       double floating;
+}
+deque_entry;
+
+/** \brief Double-Ended Queue (deque) class
+ *
+ * The double-ended queue is a very versatile data structure. MLT uses it as
+ * list, stack, and circular queue.
+ */
+
+struct mlt_deque_s
+{
+       deque_entry *list;
+       int size;
+       int count;
+};
+
+/** Create a deque.
+ *
+ * \public \memberof mlt_deque_s
+ * \return a new deque
+ */
+
+mlt_deque mlt_deque_init( )
+{
+       mlt_deque this = malloc( sizeof( struct mlt_deque_s ) );
+       if ( this != NULL )
+       {
+               this->list = NULL;
+               this->size = 0;
+               this->count = 0;
+       }
+       return this;
+}
+
+/** Return the number of items in the deque.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return the number of items
+ */
+
+int mlt_deque_count( mlt_deque this )
+{
+       return this->count;
+}
+
+/** Allocate space on the deque.
+ *
+ * \private \memberof mlt_deque_s
+ * \param this a deque
+ * \return true if there was an error
+ */
+
+static int mlt_deque_allocate( mlt_deque this )
+{
+       if ( this->count == this->size )
+       {
+               this->list = realloc( this->list, sizeof( deque_entry ) * ( this->size + 20 ) );
+               this->size += 20;
+       }
+       return this->list == NULL;
+}
+
+/** Push an item to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an opaque pointer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back( mlt_deque this, void *item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+               this->list[ this->count ++ ].addr = item;
+
+       return error;
+}
+
+/** Pop an item.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a pointer
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_pop_back( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ -- this->count ].addr : NULL;
+}
+
+/** Queue an item at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an opaque pointer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front( mlt_deque this, void *item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+       {
+               memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+               this->list[ 0 ].addr = item;
+       }
+
+       return error;
+}
+
+/** Remove an item from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a pointer
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_pop_front( mlt_deque this )
+{
+       void *item = NULL;
+
+       if ( this->count > 0 )
+       {
+               item = this->list[ 0 ].addr;
+               memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+       }
+
+       return item;
+}
+
+/** Inquire on item at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_peek_back( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ this->count - 1 ].addr : NULL;
+}
+
+/** Inquire on item at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an opaque pointer
+ */
+
+void *mlt_deque_peek_front( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ 0 ].addr : NULL;
+}
+
+/** Push an integer to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an integer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back_int( mlt_deque this, int item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+               this->list[ this->count ++ ].value = item;
+
+       return error;
+}
+
+/** Pop an integer.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_pop_back_int( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ -- this->count ].value : 0;
+}
+
+/** Queue an integer at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item an integer
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front_int( mlt_deque this, int item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+       {
+               memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+               this->list[ 0 ].value = item;
+       }
+
+       return error;
+}
+
+/** Remove an integer from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_pop_front_int( mlt_deque this )
+{
+       int item = 0;
+
+       if ( this->count > 0 )
+       {
+               item = this->list[ 0 ].value;
+               memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+       }
+
+       return item;
+}
+
+/** Inquire on an integer at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_peek_back_int( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ this->count - 1 ].value : 0;
+}
+
+/** Inquire on an integer at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return an integer
+ */
+
+int mlt_deque_peek_front_int( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ 0 ].value : 0;
+}
+
+/** Push a double float to the end.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item a double float
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_back_double( mlt_deque this, double item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+               this->list[ this->count ++ ].floating = item;
+
+       return error;
+}
+
+/** Pop a double float.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_pop_back_double( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ -- this->count ].floating : 0;
+}
+
+/** Queue a double float at the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \param item a double float
+ * \return true if there was an error
+ */
+
+int mlt_deque_push_front_double( mlt_deque this, double item )
+{
+       int error = mlt_deque_allocate( this );
+
+       if ( error == 0 )
+       {
+               memmove( &this->list[ 1 ], this->list, ( this->count ++ ) * sizeof( deque_entry ) );
+               this->list[ 0 ].floating = item;
+       }
+
+       return error;
+}
+
+/** Remove a double float from the start.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_pop_front_double( mlt_deque this )
+{
+       double item = 0;
+
+       if ( this->count > 0 )
+       {
+               item = this->list[ 0 ].floating;
+               memmove( this->list, &this->list[ 1 ], ( -- this->count ) * sizeof( deque_entry ) );
+       }
+
+       return item;
+}
+
+/** Inquire on a double float at back of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_peek_back_double( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ this->count - 1 ].floating : 0;
+}
+
+/** Inquire on a double float at front of deque but don't remove.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ * \return a double float
+ */
+
+double mlt_deque_peek_front_double( mlt_deque this )
+{
+       return this->count > 0 ? this->list[ 0 ].floating : 0;
+}
+
+/** Destroy the queue.
+ *
+ * \public \memberof mlt_deque_s
+ * \param this a deque
+ */
+
+void mlt_deque_close( mlt_deque this )
+{
+       free( this->list );
+       free( this );
+}
diff --git a/src/framework/mlt_deque.h b/src/framework/mlt_deque.h
new file mode 100644 (file)
index 0000000..d895798
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * \file mlt_deque.h
+ * \brief double ended queue
+ * \see mlt_deque_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_DEQUE_H_
+#define _MLT_DEQUE_H_
+
+#include "mlt_types.h"
+
+extern mlt_deque mlt_deque_init( );
+extern int mlt_deque_count( mlt_deque self );
+extern int mlt_deque_push_back( mlt_deque self, void *item );
+extern void *mlt_deque_pop_back( mlt_deque self );
+extern int mlt_deque_push_front( mlt_deque self, void *item );
+extern void *mlt_deque_pop_front( mlt_deque self );
+extern void *mlt_deque_peek_back( mlt_deque self );
+extern void *mlt_deque_peek_front( mlt_deque self );
+
+extern int mlt_deque_push_back_int( mlt_deque self, int item );
+extern int mlt_deque_pop_back_int( mlt_deque self );
+extern int mlt_deque_push_front_int( mlt_deque self, int item );
+extern int mlt_deque_pop_front_int( mlt_deque self );
+extern int mlt_deque_peek_back_int( mlt_deque self );
+extern int mlt_deque_peek_front_int( mlt_deque self );
+
+extern int mlt_deque_push_back_double( mlt_deque self, double item );
+extern double mlt_deque_pop_back_double( mlt_deque self );
+extern int mlt_deque_push_front_double( mlt_deque self, double item );
+extern double mlt_deque_pop_front_double( mlt_deque self );
+extern double mlt_deque_peek_back_double( mlt_deque self );
+extern double mlt_deque_peek_front_double( mlt_deque self );
+
+extern void mlt_deque_close( mlt_deque self );
+
+#endif
diff --git a/src/framework/mlt_events.c b/src/framework/mlt_events.c
new file mode 100644 (file)
index 0000000..5c84190
--- /dev/null
@@ -0,0 +1,503 @@
+/**
+ * \file mlt_events.c
+ * \brief event handling
+ * \see mlt_events_struct
+ *
+ * Copyright (C) 2004-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "mlt_properties.h"
+#include "mlt_events.h"
+
+/* Memory leak checks. */
+
+#undef _MLT_EVENT_CHECKS_
+
+#ifdef _MLT_EVENT_CHECKS_
+static int events_created = 0;
+static int events_destroyed = 0;
+#endif
+
+/** \brief Events class
+ *
+ * Events provide messages and notifications between services and the application.
+ * A service can register an event and fire/send it upon certain conditions or times.
+ * Likewise, a service or an application can listen/receive specific events on specific
+ * services.
+ */
+
+struct mlt_events_struct
+{
+       mlt_properties owner;
+       mlt_properties list;
+};
+
+typedef struct mlt_events_struct *mlt_events;
+
+/** \brief Event class
+ *
+ */
+
+struct mlt_event_struct
+{
+       mlt_events owner;
+       int ref_count;
+       int block_count;
+       mlt_listener listener;
+       void *service;
+};
+
+/** Increment the reference count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_inc_ref( mlt_event this )
+{
+       if ( this != NULL )
+               this->ref_count ++;
+}
+
+/** Increment the block count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_block( mlt_event this )
+{
+       if ( this != NULL && this->owner != NULL )
+               this->block_count ++;
+}
+
+/** Decrement the block count on this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_unblock( mlt_event this )
+{
+       if ( this != NULL && this->owner != NULL )
+               this->block_count --;
+}
+
+/** Close this event.
+ *
+ * \public \memberof mlt_event_struct
+ * \param this an event
+ */
+
+void mlt_event_close( mlt_event this )
+{
+       if ( this != NULL )
+       {
+               if ( -- this->ref_count == 1 )
+                       this->owner = NULL;
+               if ( this->ref_count <= 0 )
+               {
+#ifdef _MLT_EVENT_CHECKS_
+                       mlt_log( NULL, MLT_LOG_DEBUG, "Events created %d, destroyed %d\n", events_created, ++events_destroyed );
+#endif
+                       free( this );
+               }
+       }
+}
+
+/* Forward declaration to private functions.
+*/
+
+static mlt_events mlt_events_fetch( mlt_properties );
+static void mlt_events_store( mlt_properties, mlt_events );
+static void mlt_events_close( mlt_events );
+
+/** Initialise the events structure.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ */
+
+void mlt_events_init( mlt_properties this )
+{
+       mlt_events events = mlt_events_fetch( this );
+       if ( events == NULL )
+       {
+               events = malloc( sizeof( struct mlt_events_struct ) );
+               events->list = mlt_properties_new( );
+               mlt_events_store( this, events );
+       }
+}
+
+/** Register an event and transmitter.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of an event
+ * \param transmitter the callback function to send an event message
+ * \return true if there was an error
+ */
+
+int mlt_events_register( mlt_properties this, const char *id, mlt_transmitter transmitter )
+{
+       int error = 1;
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               mlt_properties list = events->list;
+               char temp[ 128 ];
+               error = mlt_properties_set_data( list, id, transmitter, 0, NULL, NULL );
+               sprintf( temp, "list:%s", id );
+               if ( mlt_properties_get_data( list, temp, NULL ) == NULL )
+                       mlt_properties_set_data( list, temp, mlt_properties_new( ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+       }
+       return error;
+}
+
+/** Fire an event.
+ *
+ * This takes a variable number of arguments to supply to the listener.
+
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of an event
+ */
+
+void mlt_events_fire( mlt_properties this, const char *id, ... )
+{
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               int i = 0;
+               va_list alist;
+               void *args[ 10 ];
+               mlt_properties list = events->list;
+               mlt_properties listeners = NULL;
+               char temp[ 128 ];
+               mlt_transmitter transmitter = mlt_properties_get_data( list, id, NULL );
+               sprintf( temp, "list:%s", id );
+               listeners = mlt_properties_get_data( list, temp, NULL );
+
+               va_start( alist, id );
+               do
+                       args[ i ] = va_arg( alist, void * );
+               while( args[ i ++ ] != NULL );
+               va_end( alist );
+
+               if ( listeners != NULL )
+               {
+                       for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+                       {
+                               mlt_event event = mlt_properties_get_data_at( listeners, i, NULL );
+                               if ( event != NULL && event->owner != NULL && event->block_count == 0 )
+                               {
+                                       if ( transmitter != NULL )
+                                               transmitter( event->listener, event->owner, event->service, args );
+                                       else
+                                               event->listener( event->owner, event->service );
+                               }
+                       }
+               }
+       }
+}
+
+/** Register a listener.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ * \param id the name of the event to listen for
+ * \param listener the callback to receive an event message
+ * \return
+ */
+
+mlt_event mlt_events_listen( mlt_properties this, void *service, const char *id, mlt_listener listener )
+{
+       mlt_event event = NULL;
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               mlt_properties list = events->list;
+               mlt_properties listeners = NULL;
+               char temp[ 128 ];
+               sprintf( temp, "list:%s", id );
+               listeners = mlt_properties_get_data( list, temp, NULL );
+               if ( listeners != NULL )
+               {
+                       int first_null = -1;
+                       int i = 0;
+                       for ( i = 0; event == NULL && i < mlt_properties_count( listeners ); i ++ )
+                       {
+                               mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+                               if ( entry != NULL && entry->owner != NULL )
+                               {
+                                       if ( entry->service == service && entry->listener == listener )
+                                               event = entry;
+                               }
+                               else if ( ( entry == NULL || entry->owner == NULL ) && first_null == -1 )
+                               {
+                                       first_null = i;
+                               }
+                       }
+
+                       if ( event == NULL )
+                       {
+                               event = malloc( sizeof( struct mlt_event_struct ) );
+                               if ( event != NULL )
+                               {
+#ifdef _MLT_EVENT_CHECKS_
+                                       events_created ++;
+#endif
+                                       sprintf( temp, "%d", first_null == -1 ? mlt_properties_count( listeners ) : first_null );
+                                       event->owner = events;
+                                       event->ref_count = 0;
+                                       event->block_count = 0;
+                                       event->listener = listener;
+                                       event->service = service;
+                                       mlt_properties_set_data( listeners, temp, event, 0, ( mlt_destructor )mlt_event_close, NULL );
+                                       mlt_event_inc_ref( event );
+                               }
+                       }
+
+               }
+       }
+       return event;
+}
+
+/** Block all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_block( mlt_properties this, void *service )
+{
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               int i = 0, j = 0;
+               mlt_properties list = events->list;
+               for ( j = 0; j < mlt_properties_count( list ); j ++ )
+               {
+                       char *temp = mlt_properties_get_name( list, j );
+                       if ( !strncmp( temp, "list:", 5 ) )
+                       {
+                               mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+                               for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+                               {
+                                       mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+                                       if ( entry != NULL && entry->service == service )
+                                               mlt_event_block( entry );
+                               }
+                       }
+               }
+       }
+}
+
+/** Unblock all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_unblock( mlt_properties this, void *service )
+{
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               int i = 0, j = 0;
+               mlt_properties list = events->list;
+               for ( j = 0; j < mlt_properties_count( list ); j ++ )
+               {
+                       char *temp = mlt_properties_get_name( list, j );
+                       if ( !strncmp( temp, "list:", 5 ) )
+                       {
+                               mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+                               for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+                               {
+                                       mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+                                       if ( entry != NULL && entry->service == service )
+                                               mlt_event_unblock( entry );
+                               }
+                       }
+               }
+       }
+}
+
+/** Disconnect all events for a given service.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param service an opaque pointer
+ */
+
+void mlt_events_disconnect( mlt_properties this, void *service )
+{
+       mlt_events events = mlt_events_fetch( this );
+       if ( events != NULL )
+       {
+               int i = 0, j = 0;
+               mlt_properties list = events->list;
+               for ( j = 0; j < mlt_properties_count( list ); j ++ )
+               {
+                       char *temp = mlt_properties_get_name( list, j );
+                       if ( !strncmp( temp, "list:", 5 ) )
+                       {
+                               mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
+                               for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
+                               {
+                                       mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
+                                       char *name = mlt_properties_get_name( listeners, i );
+                                       if ( entry != NULL && entry->service == service )
+                                               mlt_properties_set_data( listeners, name, NULL, 0, NULL, NULL );
+                               }
+                       }
+               }
+       }
+}
+
+/** \brief private to mlt_events_struct, used by mlt_events_wait_for() */
+
+typedef struct
+{
+       int done;
+       pthread_cond_t cond;
+       pthread_mutex_t mutex;
+}
+condition_pair;
+
+/** The event listener callback for the wait functions.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \param pair a condition pair
+ */
+
+static void mlt_events_listen_for( mlt_properties this, condition_pair *pair )
+{
+       pthread_mutex_lock( &pair->mutex );
+       if ( pair->done == 0 )
+       {
+               pthread_cond_signal( &pair->cond );
+               pthread_mutex_unlock( &pair->mutex );
+       }
+}
+
+/** Prepare to wait for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param id the name of the event to wait for
+ * \return an event
+ */
+
+mlt_event mlt_events_setup_wait_for( mlt_properties this, const char *id )
+{
+       condition_pair *pair = malloc( sizeof( condition_pair ) );
+       pair->done = 0;
+       pthread_cond_init( &pair->cond, NULL );
+       pthread_mutex_init( &pair->mutex, NULL );
+       pthread_mutex_lock( &pair->mutex );
+       return mlt_events_listen( this, pair, id, ( mlt_listener )mlt_events_listen_for );
+}
+
+/** Wait for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param event an event
+ */
+
+void mlt_events_wait_for( mlt_properties this, mlt_event event )
+{
+       if ( event != NULL )
+       {
+               condition_pair *pair = event->service;
+               pthread_cond_wait( &pair->cond, &pair->mutex );
+       }
+}
+
+/** Cleanup after waiting for an event.
+ *
+ * \public \memberof mlt_events_struct
+ * \param this a properties list
+ * \param event an event
+ */
+
+void mlt_events_close_wait_for( mlt_properties this, mlt_event event )
+{
+       if ( event != NULL )
+       {
+               condition_pair *pair = event->service;
+               event->owner = NULL;
+               pair->done = 0;
+               pthread_mutex_unlock( &pair->mutex );
+               pthread_mutex_destroy( &pair->mutex );
+               pthread_cond_destroy( &pair->cond );
+       }
+}
+
+/** Fetch the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \return an events object
+ */
+
+static mlt_events mlt_events_fetch( mlt_properties this )
+{
+       mlt_events events = NULL;
+       if ( this != NULL )
+               events = mlt_properties_get_data( this, "_events", NULL );
+       return events;
+}
+
+/** Store the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param this a properties list
+ * \param events an events object
+ */
+
+static void mlt_events_store( mlt_properties this, mlt_events events )
+{
+       if ( this != NULL && events != NULL )
+               mlt_properties_set_data( this, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL );
+}
+
+/** Close the events object.
+ *
+ * \private \memberof mlt_events_struct
+ * \param events an events object
+ */
+
+static void mlt_events_close( mlt_events events )
+{
+       if ( events != NULL )
+       {
+               mlt_properties_close( events->list );
+               free( events );
+       }
+}
diff --git a/src/framework/mlt_events.h b/src/framework/mlt_events.h
new file mode 100644 (file)
index 0000000..55bb18f
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * \file mlt_events.h
+ * \brief event handling
+ * \see mlt_events_struct
+ *
+ * Copyright (C) 2004-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_EVENTS_H_
+#define _MLT_EVENTS_H_
+
+#include "mlt_types.h"
+
+#if GCC_VERSION >= 40000
+typedef void ( *mlt_transmitter )( void *, ... );
+typedef void ( *mlt_listener )( void *, ... );
+#else
+/** callback function to send an event message
+ *
+ */
+typedef void ( *mlt_transmitter )( );
+/** event handler when receiving an event message
+ * \param the properties object on which the event was registered
+ * \param an opaque pointer to a service or really an object
+ * \param variable args supplied by the transmitter
+ */
+typedef void ( *mlt_listener )( );
+#endif
+
+extern void mlt_events_init( mlt_properties self );
+extern int mlt_events_register( mlt_properties self, const char *id, mlt_transmitter transmitter );
+extern void mlt_events_fire( mlt_properties self, const char *id, ... );
+extern mlt_event mlt_events_listen( mlt_properties self, void *service, const char *id, mlt_listener listener );
+extern void mlt_events_block( mlt_properties self, void *service );
+extern void mlt_events_unblock( mlt_properties self, void *service );
+extern void mlt_events_disconnect( mlt_properties self, void *service );
+
+extern mlt_event mlt_events_setup_wait_for( mlt_properties self, const char *id );
+extern void mlt_events_wait_for( mlt_properties self, mlt_event event );
+extern void mlt_events_close_wait_for( mlt_properties self, mlt_event event );
+
+extern void mlt_event_inc_ref( mlt_event self );
+extern void mlt_event_block( mlt_event self );
+extern void mlt_event_unblock( mlt_event self );
+extern void mlt_event_close( mlt_event self );
+
+#endif
+
diff --git a/src/framework/mlt_factory.c b/src/framework/mlt_factory.c
new file mode 100644 (file)
index 0000000..552b8ce
--- /dev/null
@@ -0,0 +1,378 @@
+/**
+ * \file mlt_factory.c
+ * \brief the factory method interfaces
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt.h"
+#include "mlt_repository.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** the default subdirectory of the libdir for holding modules (plugins) */
+#define PREFIX_LIB LIBDIR "/mlt"
+/** the default subdirectory of the install prefix for holding module (plugin) data */
+#define PREFIX_DATA PREFIX "/share/mlt"
+
+
+/** holds the full path to the modules directory - initialized and retained for the entire session */
+static char *mlt_directory = NULL;
+/** a global properties list for holding environment config data and things needing session-oriented cleanup */
+static mlt_properties global_properties = NULL;
+/** the global repository singleton */
+static mlt_repository repository = NULL;
+/** the events object for the factory events */
+static mlt_properties event_object = NULL;
+/** for tracking the unique_id set on each constructed service */
+static int unique_id = 0;
+
+/* Event transmitters. */
+
+/** the -create-request event transmitter
+ *
+ * \param listener
+ * \param owner
+ * \param this
+ * \param args
+ */
+
+static void mlt_factory_create_request( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service * )args[ 2 ] );
+}
+
+/** the -create-done event transmitter
+ *
+ * \param listener
+ * \param owner
+ * \param this
+ * \param args
+ */
+
+static void mlt_factory_create_done( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service )args[ 2 ] );
+}
+
+/** Construct the repository and factories.
+ *
+ * The environment variable MLT_PRODUCER is the name of a default producer often used by other services, defaults to "fezzil".
+ *
+ * The environment variable MLT_CONSUMER is the name of a default consumer, defaults to "sdl".
+ *
+ * The environment variable MLT_TEST_CARD is the name of a producer or file to be played when nothing is available (all tracks blank).
+ *
+ * The environment variable MLT_DATA overrides the default full path to the MLT and module supplemental data files, defaults to \p PREFIX_DATA.
+ *
+ * The environment variable MLT_PROFILE defaults to "dv_pal."
+ *
+ * The environment variable MLT_REPOSITORY overrides the default location of the plugin modules, defaults to \p PREFIX_LIB.
+ *
+ * \param directory an optional full path to a directory containing the modules that overrides the default and
+ * the MLT_REPOSITORY environment variable
+ * \return the repository
+ */
+
+mlt_repository mlt_factory_init( const char *directory )
+{
+       // Only initialise once
+       if ( mlt_directory == NULL )
+       {
+               // Allow user over rides
+               if ( directory == NULL || !strcmp( directory, "" ) )
+                       directory = getenv( "MLT_REPOSITORY" );
+
+               // If no directory is specified, default to install directory
+               if ( directory == NULL )
+                       directory = PREFIX_LIB;
+
+               // Store the prefix for later retrieval
+               mlt_directory = strdup( directory );
+
+               // Initialise the pool
+               mlt_pool_init( );
+
+               // Create and set up the events object
+               event_object = mlt_properties_new( );
+               mlt_events_init( event_object );
+               mlt_events_register( event_object, "producer-create-request", ( mlt_transmitter )mlt_factory_create_request );
+               mlt_events_register( event_object, "producer-create-done", ( mlt_transmitter )mlt_factory_create_done );
+               mlt_events_register( event_object, "filter-create-request", ( mlt_transmitter )mlt_factory_create_request );
+               mlt_events_register( event_object, "filter-create-done", ( mlt_transmitter )mlt_factory_create_done );
+               mlt_events_register( event_object, "transition-create-request", ( mlt_transmitter )mlt_factory_create_request );
+               mlt_events_register( event_object, "transition-create-done", ( mlt_transmitter )mlt_factory_create_done );
+               mlt_events_register( event_object, "consumer-create-request", ( mlt_transmitter )mlt_factory_create_request );
+               mlt_events_register( event_object, "consumer-create-done", ( mlt_transmitter )mlt_factory_create_done );
+
+               // Create the global properties
+               global_properties = mlt_properties_new( );
+
+               // Create the repository of services
+               repository = mlt_repository_init( directory );
+
+               // Force a clean up when app closes
+               atexit( mlt_factory_close );
+       }
+
+       // Allow property refresh on a subsequent initialisation
+       if ( global_properties != NULL )
+       {
+               mlt_properties_set_or_default( global_properties, "MLT_NORMALISATION", getenv( "MLT_NORMALISATION" ), "PAL" );
+               mlt_properties_set_or_default( global_properties, "MLT_PRODUCER", getenv( "MLT_PRODUCER" ), "fezzik" );
+               mlt_properties_set_or_default( global_properties, "MLT_CONSUMER", getenv( "MLT_CONSUMER" ), "sdl" );
+               mlt_properties_set( global_properties, "MLT_TEST_CARD", getenv( "MLT_TEST_CARD" ) );
+               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 );
+       }
+
+
+       return repository;
+}
+
+/** Fetch the events object.
+ *
+ * \return the global factory event object
+ */
+
+mlt_properties mlt_factory_event_object( )
+{
+       return event_object;
+}
+
+/** Fetch the module directory used in this instance.
+ *
+ * \return the full path to the module directory that this session is using
+ */
+
+const char *mlt_factory_directory( )
+{
+       return mlt_directory;
+}
+
+/** Get a value from the environment.
+ *
+ * \param name the name of a MLT (runtime configuration) environment variable
+ * \return the value of the variable
+ */
+
+char *mlt_environment( const char *name )
+{
+       if ( global_properties )
+               return mlt_properties_get( global_properties, name );
+       else
+               return NULL;
+}
+
+/** Set a value in the environment.
+ *
+ * \param name the name of a MLT environment variable
+ * \param value the value of the variable
+ * \return true on error
+ */
+
+int mlt_environment_set( const char *name, const char *value )
+{
+       if ( global_properties )
+               return mlt_properties_set( global_properties, name, value );
+       else
+               return -1;
+}
+
+/** Set some properties common to all services.
+ *
+ * This sets _unique_id, \p mlt_type, \p mlt_service (unless _mlt_service_hidden), and _profile.
+ *
+ * \param properties a service's properties list
+ * \param profile the \p mlt_profile supplied to the factory function
+ * \param type the MLT service class
+ * \param service the name of the service
+ */
+
+static void set_common_properties( mlt_properties properties, mlt_profile profile, const char *type, const char *service )
+{
+       mlt_properties_set_int( properties, "_unique_id", ++ unique_id );
+       mlt_properties_set( properties, "mlt_type", type );
+       if ( mlt_properties_get_int( properties, "_mlt_service_hidden" ) == 0 )
+               mlt_properties_set( properties, "mlt_service", service );
+       if ( profile != NULL )
+               mlt_properties_set_data( properties, "_profile", profile, 0, NULL, NULL );
+}
+
+/** Fetch a producer from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the producer (optional, defaults to MLT_PRODUCER)
+ * \param input an optional argument to the producer constructor, typically a string
+ * \return a new producer
+ */
+
+mlt_producer mlt_factory_producer( mlt_profile profile, const char *service, const void *input )
+{
+       mlt_producer obj = NULL;
+
+       // Pick up the default normalising producer if necessary
+       if ( service == NULL )
+               service = mlt_environment( "MLT_PRODUCER" );
+
+       // Offer the application the chance to 'create'
+       mlt_events_fire( event_object, "producer-create-request", service, input, &obj, NULL );
+
+       // Try to instantiate via the specified service
+       if ( obj == NULL )
+       {
+               obj = mlt_repository_create( repository, profile, producer_type, service, input );
+               mlt_events_fire( event_object, "producer-create-done", service, input, obj, NULL );
+               if ( obj != NULL )
+               {
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( obj );
+                       set_common_properties( properties, profile, "producer", service );
+               }
+       }
+       return obj;
+}
+
+/** Fetch a filter from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the filter
+ * \param input an optional argument to the filter constructor, typically a string
+ * \return a new filter
+ */
+
+mlt_filter mlt_factory_filter( mlt_profile profile, const char *service, const void *input )
+{
+       mlt_filter obj = NULL;
+
+       // Offer the application the chance to 'create'
+       mlt_events_fire( event_object, "filter-create-request", service, input, &obj, NULL );
+
+       if ( obj == NULL )
+       {
+               obj = mlt_repository_create( repository, profile, filter_type, service, input );
+               mlt_events_fire( event_object, "filter-create-done", service, input, obj, NULL );
+       }
+
+       if ( obj != NULL )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( obj );
+               set_common_properties( properties, profile, "filter", service );
+       }
+       return obj;
+}
+
+/** Fetch a transition from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the transition
+ * \param input an optional argument to the transition constructor, typically a string
+ * \return a new transition
+ */
+
+mlt_transition mlt_factory_transition( mlt_profile profile, const char *service, const void *input )
+{
+       mlt_transition obj = NULL;
+
+       // Offer the application the chance to 'create'
+       mlt_events_fire( event_object, "transition-create-request", service, input, &obj, NULL );
+
+       if ( obj == NULL )
+       {
+               obj = mlt_repository_create( repository, profile, transition_type, service, input );
+               mlt_events_fire( event_object, "transition-create-done", service, input, obj, NULL );
+       }
+
+       if ( obj != NULL )
+       {
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( obj );
+               set_common_properties( properties, profile, "transition", service );
+       }
+       return obj;
+}
+
+/** Fetch a consumer from the repository.
+ *
+ * \param profile the \p mlt_profile to use
+ * \param service the name of the consumer (optional, defaults to MLT_CONSUMER)
+ * \param input an optional argument to the consumer constructor, typically a string
+ * \return a new consumer
+ */
+
+mlt_consumer mlt_factory_consumer( mlt_profile profile, const char *service, const void *input )
+{
+       mlt_consumer obj = NULL;
+
+       if ( service == NULL )
+               service = mlt_environment( "MLT_CONSUMER" );
+
+       // Offer the application the chance to 'create'
+       mlt_events_fire( event_object, "consumer-create-request", service, input, &obj, NULL );
+
+       if ( obj == NULL )
+       {
+               obj = mlt_repository_create( repository, profile, consumer_type, service, input );
+               mlt_events_fire( event_object, "consumer-create-done", service, input, obj, NULL );
+       }
+
+       if ( obj != NULL )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( obj );
+               set_common_properties( properties, profile, "consumer", service );
+       }
+       return obj;
+}
+
+/** Register an object for clean up.
+ *
+ * \param ptr an opaque pointer to anything allocated on the heap
+ * \param destructor the function pointer of the deallocation subroutine (e.g., free or \p mlt_pool_release)
+ */
+
+void mlt_factory_register_for_clean_up( void *ptr, mlt_destructor destructor )
+{
+       char unique[ 256 ];
+       sprintf( unique, "%08d", mlt_properties_count( global_properties ) );
+       mlt_properties_set_data( global_properties, unique, ptr, 0, destructor, NULL );
+}
+
+/** Close the factory.
+ *
+ * Cleanup all resources for the session.
+ */
+
+void mlt_factory_close( )
+{
+       if ( mlt_directory != NULL )
+       {
+               mlt_properties_close( event_object );
+               mlt_properties_close( global_properties );
+               mlt_repository_close( repository );
+               free( mlt_directory );
+               mlt_directory = NULL;
+               mlt_pool_close( );
+       }
+}
+
+mlt_properties mlt_global_properties( )
+{
+       return global_properties;
+}
diff --git a/src/framework/mlt_factory.h b/src/framework/mlt_factory.h
new file mode 100644 (file)
index 0000000..29dc62a
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * \file mlt_factory.h
+ * \brief the factory method interfaces
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_FACTORY_H
+#define _MLT_FACTORY_H
+
+#include "mlt_types.h"
+#include "mlt_profile.h"
+#include "mlt_repository.h"
+
+/**
+ * \event \em producer-create-request fired when mlt_factory_producer is called
+ * \event \em producer-create-done fired when a producer registers itself
+ * \event \em filter-create-request fired when mlt_factory_filter is called
+ * \event \em filter-create-done fired when a filter registers itself
+ * \event \em transition-create-request fired when mlt_factory_transition is called
+ * \event \em transition-create-done fired when a transition registers itself
+ * \event \em consumer-create-request fired when mlt_factory_consumer is called
+ * \event \em consumer-create-done fired when a consumer registers itself
+ */
+
+extern mlt_repository mlt_factory_init( const char *directory );
+extern const char *mlt_factory_directory( );
+extern char *mlt_environment( const char *name );
+extern int mlt_environment_set( const char *name, const char *value );
+extern mlt_properties mlt_factory_event_object( );
+extern mlt_producer mlt_factory_producer( mlt_profile profile, const char *name, const void *input );
+extern mlt_filter mlt_factory_filter( mlt_profile profile, const char *name, const void *input );
+extern mlt_transition mlt_factory_transition( mlt_profile profile, const char *name, const void *input );
+extern mlt_consumer mlt_factory_consumer( mlt_profile profile, const char *name, const void *input );
+extern void mlt_factory_register_for_clean_up( void *ptr, mlt_destructor destructor );
+extern void mlt_factory_close( );
+extern mlt_properties mlt_global_properties( );
+
+#endif
diff --git a/src/framework/mlt_field.c b/src/framework/mlt_field.c
new file mode 100644 (file)
index 0000000..c988ae6
--- /dev/null
@@ -0,0 +1,270 @@
+/**
+ * \file mlt_field.c
+ * \brief a field for planting multiple transitions and filters
+ * \see mlt_field_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_field.h"
+#include "mlt_service.h"
+#include "mlt_filter.h"
+#include "mlt_transition.h"
+#include "mlt_multitrack.h"
+#include "mlt_tractor.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Field class
+ *
+ * The field is a convenience class that works with the tractor and multitrack classes to manage track filters and transitions.
+ */
+
+struct mlt_field_s
+{
+       /// This is the producer we're connected to
+       mlt_service producer;
+
+       /// Multitrack
+       mlt_multitrack multitrack;
+
+       /// Tractor
+       mlt_tractor tractor;
+};
+
+/** Construct a field, mulitrack, and tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \return a new field
+ */
+
+mlt_field mlt_field_init( )
+{
+       // Initialise the field
+       mlt_field this = calloc( sizeof( struct mlt_field_s ), 1 );
+
+       // Initialise it
+       if ( this != NULL )
+       {
+               // Construct a multitrack
+               this->multitrack = mlt_multitrack_init( );
+
+               // Construct a tractor
+               this->tractor = mlt_tractor_init( );
+
+               // The first plant will be connected to the mulitrack
+               this->producer = MLT_MULTITRACK_SERVICE( this->multitrack );
+
+               // Connect the tractor to the multitrack
+               mlt_tractor_connect( this->tractor, this->producer );
+       }
+
+       // Return this
+       return this;
+}
+
+/** Construct a field and initialize with supplied multitrack and tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \param multitrack a multitrack
+ * \param tractor a tractor
+ * \return a new field
+ */
+
+mlt_field mlt_field_new( mlt_multitrack multitrack, mlt_tractor tractor )
+{
+       // Initialise the field
+       mlt_field this = calloc( sizeof( struct mlt_field_s ), 1 );
+
+       // Initialise it
+       if ( this != NULL )
+       {
+               // Construct a multitrack
+               this->multitrack = multitrack;
+
+               // Construct a tractor
+               this->tractor = tractor;
+
+               // The first plant will be connected to the mulitrack
+               this->producer = MLT_MULTITRACK_SERVICE( this->multitrack );
+
+               // Connect the tractor to the multitrack
+               mlt_tractor_connect( this->tractor, this->producer );
+       }
+
+       // Return this
+       return this;
+}
+
+/** Get the service associated to this field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the tractor as a service
+ */
+
+mlt_service mlt_field_service( mlt_field this )
+{
+       return MLT_TRACTOR_SERVICE( this->tractor );
+}
+
+/** Get the multitrack.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the multitrack
+ */
+
+mlt_multitrack mlt_field_multitrack( mlt_field this )
+{
+       return this != NULL ? this->multitrack : NULL;
+}
+
+/** Get the tractor.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return the tractor
+ */
+
+mlt_tractor mlt_field_tractor( mlt_field this )
+{
+       return this != NULL ? this->tractor : NULL;
+}
+
+/** Get the properties associated to this field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \return a properties list
+ */
+
+mlt_properties mlt_field_properties( mlt_field this )
+{
+       return MLT_SERVICE_PROPERTIES( mlt_field_service( this ) );
+}
+
+/** Plant a filter.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \param that a filter
+ * \param track the track index
+ * \return true if there was an error
+ */
+
+int mlt_field_plant_filter( mlt_field this, mlt_filter that, int track )
+{
+       // Connect the filter to the last producer
+       int result = mlt_filter_connect( that, this->producer, track );
+
+       // If sucessful, then we'll use this for connecting in the future
+       if ( result == 0 )
+       {
+               // This is now the new producer
+               this->producer = MLT_FILTER_SERVICE( that );
+
+               // Reconnect tractor to new producer
+               mlt_tractor_connect( this->tractor, this->producer );
+
+               // Fire an event
+               mlt_events_fire( mlt_field_properties( this ), "service-changed", NULL );
+       }
+
+       return result;
+}
+
+/** Plant a transition.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ * \param that a transition
+ * \param a_track input A's track index
+ * \param b_track input B's track index
+ * \return true if there was an error
+ */
+
+int mlt_field_plant_transition( mlt_field this, mlt_transition that, int a_track, int b_track )
+{
+       // Connect the transition to the last producer
+       int result = mlt_transition_connect( that, this->producer, a_track, b_track );
+
+       // If sucessful, then we'll use this for connecting in the future
+       if ( result == 0 )
+       {
+               // This is now the new producer
+               this->producer = MLT_TRANSITION_SERVICE( that );
+
+               // Reconnect tractor to new producer
+               mlt_tractor_connect( this->tractor, this->producer );
+
+               // Fire an event
+               mlt_events_fire( mlt_field_properties( this ), "service-changed", NULL );
+       }
+
+       return 0;
+}
+
+/** Close the field.
+ *
+ * \public \memberof mlt_field_s
+ * \param this a field
+ */
+
+void mlt_field_close( mlt_field this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( mlt_field_properties( this ) ) <= 0 )
+       {
+               //mlt_tractor_close( this->tractor );
+               //mlt_multitrack_close( this->multitrack );
+               free( this );
+       }
+}
+
+/** Remove a filter or transition from the field.
+ *
+ * \public \memberof mlt_field_s
+ * \param self a field
+ * \param service the filter or transition to remove
+ */
+
+void mlt_field_disconnect_service( mlt_field self, mlt_service service )
+{
+       mlt_service p = mlt_service_producer( service );
+       mlt_service c = mlt_service_consumer( service);
+       int i;
+       switch ( mlt_service_identify(c) )
+       {
+               case filter_type:
+                       i = mlt_filter_get_track( MLT_FILTER(c) );
+                       mlt_service_connect_producer( c, p, i );
+                       break;
+               case transition_type:
+                       i = mlt_transition_get_a_track ( MLT_TRANSITION(c) );
+                       mlt_service_connect_producer( c, p, i );
+                       MLT_TRANSITION(c)->producer = p;
+                       break;
+               case tractor_type:
+                       self->producer = p;
+                       mlt_tractor_connect( MLT_TRACTOR(c), p );
+               default:
+                       break;
+       }
+       mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL );
+}
diff --git a/src/framework/mlt_field.h b/src/framework/mlt_field.h
new file mode 100644 (file)
index 0000000..a54db54
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * \file mlt_field.h
+ * \brief a field for planting multiple transitions and services
+ * \see mlt_field_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_FIELD_H_
+#define _MLT_FIELD_H_
+
+#include "mlt_types.h"
+
+extern mlt_field mlt_field_init( );
+extern mlt_field mlt_field_new( mlt_multitrack multitrack, mlt_tractor tractor );
+extern mlt_service mlt_field_service( mlt_field self );
+extern mlt_tractor mlt_field_tractor( mlt_field self );
+extern mlt_multitrack mlt_field_multitrack( mlt_field self );
+extern mlt_properties mlt_field_properties( mlt_field self );
+extern int mlt_field_plant_filter( mlt_field self, mlt_filter that, int track );
+extern int mlt_field_plant_transition( mlt_field self, mlt_transition that, int a_track, int b_track );
+extern void mlt_field_close( mlt_field self );
+extern void mlt_field_disconnect_service( mlt_field self, mlt_service service );
+
+#endif
+
diff --git a/src/framework/mlt_filter.c b/src/framework/mlt_filter.c
new file mode 100644 (file)
index 0000000..9a04352
--- /dev/null
@@ -0,0 +1,275 @@
+/**
+ * \file mlt_filter.c
+ * \brief abstraction for all filter services
+ * \see mlt_filter_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_filter.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** Initialize a new filter.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param child the object of a subclass
+ * \return true if there was an error
+ */
+
+int mlt_filter_init( mlt_filter this, void *child )
+{
+       mlt_service service = &this->parent;
+       memset( this, 0, sizeof( struct mlt_filter_s ) );
+       this->child = child;
+       if ( mlt_service_init( service, this ) == 0 )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+               // Override the get_frame method
+               service->get_frame = filter_get_frame;
+
+               // Define the destructor
+               service->close = ( mlt_destructor )mlt_filter_close;
+               service->close_object = this;
+
+               // 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;
+       }
+       return 1;
+}
+
+/** Create a new filter and initialize it.
+ *
+ * \public \memberof mlt_filter_s
+ * \return a new filter
+ */
+
+mlt_filter mlt_filter_new( )
+{
+       mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) );
+       if ( this != NULL )
+               mlt_filter_init( this, NULL );
+       return this;
+}
+
+/** Get the service class interface.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the service parent class
+ * \see MLT_FILTER_SERVICE
+ */
+
+mlt_service mlt_filter_service( mlt_filter this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the filter properties.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the properties list for the filter
+ * \see MLT_FILTER_PROPERTIES
+ */
+
+mlt_properties mlt_filter_properties( mlt_filter this )
+{
+       return MLT_SERVICE_PROPERTIES( MLT_FILTER_SERVICE( this ) );
+}
+
+/** Connect this filter to a producers track. Note that a filter only operates
+ * on a single track, and by default it operates on the entirety of that track.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param producer the producer to which to connect this filter
+ * \param index which of potentially multiple producers to this service (0 based)
+ */
+
+int mlt_filter_connect( mlt_filter this, mlt_service producer, int index )
+{
+       int ret = mlt_service_connect_producer( &this->parent, producer, index );
+
+       // If the connection was successful, grab the producer, track and reset in/out
+       if ( ret == 0 )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+               mlt_properties_set_position( properties, "in", 0 );
+               mlt_properties_set_position( properties, "out", 0 );
+               mlt_properties_set_int( properties, "track", index );
+       }
+
+       return ret;
+}
+
+/** Set the starting and ending time.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param in the time relative to the producer at which start applying the filter
+ * \param out the time relative to the producer at which to stop applying the filter
+ */
+
+
+void mlt_filter_set_in_and_out( mlt_filter this, mlt_position in, mlt_position out )
+{
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+       mlt_properties_set_position( properties, "in", in );
+       mlt_properties_set_position( properties, "out", out );
+}
+
+/** Return the track that this filter is operating on.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return true on error
+ */
+
+
+int mlt_filter_get_track( mlt_filter this )
+{
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+       return mlt_properties_get_int( properties, "track" );
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the start time for the filter relative to the producer
+ */
+
+
+mlt_position mlt_filter_get_in( mlt_filter this )
+{
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+       return mlt_properties_get_position( properties, "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \return the ending time for the filter relative to the producer
+ */
+
+
+mlt_position mlt_filter_get_out( mlt_filter this )
+{
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
+       return mlt_properties_get_position( properties, "out" );
+}
+
+/** Process the frame.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ * \param frame a frame
+ * \return a frame
+ */
+
+
+mlt_frame mlt_filter_process( mlt_filter this, mlt_frame frame )
+{
+       int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "disable" );
+       if ( disable || this->process == NULL )
+               return frame;
+       else
+               return this->process( this, frame );
+}
+
+/** Get a frame from this filter.
+ *
+ * \private \memberof mlt_filter_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return true on error
+ */
+
+
+static int filter_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+       mlt_filter this = service->child;
+
+       // Get coords in/out/track
+       int track = mlt_filter_get_track( this );
+       int in = mlt_filter_get_in( this );
+       int out = mlt_filter_get_out( this );
+
+       // Get the producer this is connected to
+       mlt_service producer = mlt_service_producer( &this->parent );
+
+       // If the frame request is for this filters track, we need to process it
+       if ( index == track || track == -1 )
+       {
+               int ret = mlt_service_get_frame( producer, frame, index );
+               if ( ret == 0 )
+               {
+                       mlt_position position = mlt_frame_get_position( *frame );
+                       if ( position >= in && ( out == 0 || position <= out ) )
+                               *frame = mlt_filter_process( this, *frame );
+                       return 0;
+               }
+               else
+               {
+                       *frame = mlt_frame_init( service );
+                       return 0;
+               }
+       }
+       else
+       {
+               return mlt_service_get_frame( producer, frame, index );
+       }
+}
+
+/** Close and destroy the filter.
+ *
+ * \public \memberof mlt_filter_s
+ * \param this a filter
+ */
+
+
+void mlt_filter_close( mlt_filter this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_FILTER_PROPERTIES( this ) ) <= 0 )
+       {
+               if ( this->close != NULL )
+               {
+                       this->close( this );
+               }
+               else
+               {
+                       this->parent.close = NULL;
+                       mlt_service_close( &this->parent );
+               }
+               free( this );
+       }
+}
diff --git a/src/framework/mlt_filter.h b/src/framework/mlt_filter.h
new file mode 100644 (file)
index 0000000..66e2e47
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * \file mlt_filter.h
+ * \brief abstraction for all filter services
+ * \see mlt_filter_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_FILTER_H_
+#define _MLT_FILTER_H_
+
+#include "mlt_service.h"
+
+/** \brief Filter abstract service class
+ *
+ * A filter is a service that may modify the output of a single producer.
+ *
+ * \extends mlt_service_s
+ * \properties \em track the index of the track of a multitrack on which the filter is applied
+ */
+
+struct mlt_filter_s
+{
+       /** We're implementing service here */
+       struct mlt_service_s parent;
+
+       /** public virtual */
+       void ( *close )( mlt_filter );
+
+       /** protected filter method */
+       mlt_frame ( *process )( mlt_filter, mlt_frame );
+
+       /** Protected */
+       void *child;
+};
+
+#define MLT_FILTER_SERVICE( filter )           ( &( filter )->parent )
+#define MLT_FILTER_PROPERTIES( filter )                MLT_SERVICE_PROPERTIES( MLT_FILTER_SERVICE( filter ) )
+
+extern int mlt_filter_init( mlt_filter self, void *child );
+extern mlt_filter mlt_filter_new( );
+extern mlt_service mlt_filter_service( mlt_filter self );
+extern mlt_properties mlt_filter_properties( mlt_filter self );
+extern mlt_frame mlt_filter_process( mlt_filter self, mlt_frame that );
+extern int mlt_filter_connect( mlt_filter self, mlt_service producer, int index );
+extern void mlt_filter_set_in_and_out( mlt_filter self, mlt_position in, mlt_position out );
+extern int mlt_filter_get_track( mlt_filter self );
+extern mlt_position mlt_filter_get_in( mlt_filter self );
+extern mlt_position mlt_filter_get_out( mlt_filter self );
+extern void mlt_filter_close( mlt_filter );
+
+#endif
diff --git a/src/framework/mlt_frame.c b/src/framework/mlt_frame.c
new file mode 100644 (file)
index 0000000..0899b06
--- /dev/null
@@ -0,0 +1,1314 @@
+/**
+ * \file mlt_frame.c
+ * \brief interface for all frame classes
+ * \see mlt_frame_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_frame.h"
+#include "mlt_producer.h"
+#include "mlt_factory.h"
+#include "mlt_profile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/** Constructor for a frame.
+*/
+
+mlt_frame mlt_frame_init( mlt_service service )
+{
+       // Allocate a frame
+       mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 );
+
+       if ( this != NULL )
+       {
+               mlt_profile profile = mlt_service_profile( service );
+
+               // Initialise the properties
+               mlt_properties properties = &this->parent;
+               mlt_properties_init( properties, this );
+
+               // Set default properties on the frame
+               mlt_properties_set_position( properties, "_position", 0.0 );
+               mlt_properties_set_data( properties, "image", NULL, 0, NULL, NULL );
+               mlt_properties_set_int( properties, "width", profile? profile->width : 720 );
+               mlt_properties_set_int( properties, "height", profile? profile->height : 576 );
+               mlt_properties_set_int( properties, "normalised_width", profile? profile->width : 720 );
+               mlt_properties_set_int( properties, "normalised_height", profile? profile->height : 576 );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( NULL ) );
+               mlt_properties_set_data( properties, "audio", NULL, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL );
+
+               // Construct stacks for frames and methods
+               this->stack_image = mlt_deque_init( );
+               this->stack_audio = mlt_deque_init( );
+               this->stack_service = mlt_deque_init( );
+       }
+
+       return this;
+}
+
+/** Fetch the frames properties.
+*/
+
+mlt_properties mlt_frame_properties( mlt_frame this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Check if we have a way to derive something other than a test card.
+*/
+
+int mlt_frame_is_test_card( mlt_frame this )
+{
+       return mlt_deque_count( this->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_image" );
+}
+
+/** Check if we have a way to derive something other than test audio.
+*/
+
+int mlt_frame_is_test_audio( mlt_frame this )
+{
+       return mlt_deque_count( this->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_audio" );
+}
+
+/** Get the aspect ratio of the frame.
+*/
+
+double mlt_frame_get_aspect_ratio( mlt_frame this )
+{
+       return mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio" );
+}
+
+/** Set the aspect ratio of the frame.
+*/
+
+int mlt_frame_set_aspect_ratio( mlt_frame this, double value )
+{
+       return mlt_properties_set_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio", value );
+}
+
+/** Get the position of this frame.
+*/
+
+mlt_position mlt_frame_get_position( mlt_frame this )
+{
+       int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( this ), "_position" );
+       return pos < 0 ? 0 : pos;
+}
+
+/** Set the position of this frame.
+*/
+
+int mlt_frame_set_position( mlt_frame this, mlt_position value )
+{
+       return mlt_properties_set_position( MLT_FRAME_PROPERTIES( this ), "_position", value );
+}
+
+/** Stack a get_image callback.
+*/
+
+int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image )
+{
+       return mlt_deque_push_back( this->stack_image, get_image );
+}
+
+/** Pop a get_image callback.
+*/
+
+mlt_get_image mlt_frame_pop_get_image( mlt_frame this )
+{
+       return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a frame.
+*/
+
+int mlt_frame_push_frame( mlt_frame this, mlt_frame that )
+{
+       return mlt_deque_push_back( this->stack_image, that );
+}
+
+/** Pop a frame.
+*/
+
+mlt_frame mlt_frame_pop_frame( mlt_frame this )
+{
+       return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a service.
+*/
+
+int mlt_frame_push_service( mlt_frame this, void *that )
+{
+       return mlt_deque_push_back( this->stack_image, that );
+}
+
+/** Pop a service.
+*/
+
+void *mlt_frame_pop_service( mlt_frame this )
+{
+       return mlt_deque_pop_back( this->stack_image );
+}
+
+/** Push a service.
+*/
+
+int mlt_frame_push_service_int( mlt_frame this, int that )
+{
+       return mlt_deque_push_back_int( this->stack_image, that );
+}
+
+/** Pop a service.
+*/
+
+int mlt_frame_pop_service_int( mlt_frame this )
+{
+       return mlt_deque_pop_back_int( this->stack_image );
+}
+
+/** Push an audio item on the stack.
+*/
+
+int mlt_frame_push_audio( mlt_frame this, void *that )
+{
+       return mlt_deque_push_back( this->stack_audio, that );
+}
+
+/** Pop an audio item from the stack
+*/
+
+void *mlt_frame_pop_audio( mlt_frame this )
+{
+       return mlt_deque_pop_back( this->stack_audio );
+}
+
+/** Return the service stack
+*/
+
+mlt_deque mlt_frame_service_stack( mlt_frame this )
+{
+       return this->stack_service;
+}
+
+/** Replace image stack with the information provided.
+
+       This might prove to be unreliable and restrictive - the idea is that a transition
+       which normally uses two images may decide to only use the b frame (ie: in the case
+       of a composite where the b frame completely obscures the a frame).
+
+       The image must be writable and the destructor for the image itself must be taken
+       care of on another frame and that frame cannot have a replace applied to it...
+       Further it assumes that no alpha mask is in use.
+
+       For these reasons, it can only be used in a specific situation - when you have
+       multiple tracks each with their own transition and these transitions are applied
+       in a strictly reversed order (ie: highest numbered [lowest track] is processed
+       first).
+
+       More reliable approach - the cases should be detected during the process phase
+       and the upper tracks should simply not be invited to stack...
+*/
+
+void mlt_frame_replace_image( mlt_frame this, uint8_t *image, mlt_image_format format, int width, int height )
+{
+       // Remove all items from the stack
+       while( mlt_deque_pop_back( this->stack_image ) ) ;
+
+       // Update the information
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, 0, NULL, NULL );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "width", width );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "height", height );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "format", format );
+       this->get_alpha_mask = NULL;
+}
+
+/** Get the image associated to the frame.
+*/
+
+int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       mlt_get_image get_image = mlt_frame_pop_get_image( this );
+       mlt_producer producer = mlt_properties_get_data( properties, "test_card_producer", NULL );
+       int error = 0;
+
+       if ( get_image != NULL )
+       {
+               mlt_properties_set_int( properties, "image_count", mlt_properties_get_int( properties, "image_count" ) - 1 );
+               mlt_position position = mlt_frame_get_position( this );
+               error = get_image( this, buffer, format, width, height, writable );
+               mlt_properties_set_int( properties, "width", *width );
+               mlt_properties_set_int( properties, "height", *height );
+               mlt_properties_set_int( properties, "format", *format );
+               mlt_frame_set_position( this, position );
+       }
+       else if ( mlt_properties_get_data( properties, "image", NULL ) != NULL )
+       {
+               *format = mlt_properties_get_int( properties, "format" );
+               *buffer = mlt_properties_get_data( properties, "image", NULL );
+               *width = mlt_properties_get_int( properties, "width" );
+               *height = mlt_properties_get_int( properties, "height" );
+       }
+       else if ( producer != NULL )
+       {
+               mlt_frame test_frame = NULL;
+               mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &test_frame, 0 );
+               if ( test_frame != NULL )
+               {
+                       mlt_properties test_properties = MLT_FRAME_PROPERTIES( test_frame );
+                       mlt_properties_set_double( test_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+                       mlt_properties_set( test_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) );
+                       mlt_frame_get_image( test_frame, buffer, format, width, height, writable );
+                       mlt_properties_set_data( properties, "test_card_frame", test_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+                       mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, NULL, NULL );
+                       mlt_properties_set_int( properties, "width", *width );
+                       mlt_properties_set_int( properties, "height", *height );
+                       mlt_properties_set_int( properties, "format", *format );
+                       mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( test_frame ) );
+               }
+               else
+               {
+                       mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
+                       mlt_frame_get_image( this, buffer, format, width, height, writable );
+               }
+       }
+       else
+       {
+               register uint8_t *p;
+               register uint8_t *q;
+               int size = 0;
+
+               *width = *width == 0 ? 720 : *width;
+               *height = *height == 0 ? 576 : *height;
+               size = *width * *height;
+
+               mlt_properties_set_int( properties, "format", *format );
+               mlt_properties_set_int( properties, "width", *width );
+               mlt_properties_set_int( properties, "height", *height );
+               mlt_properties_set_int( properties, "aspect_ratio", 0 );
+
+               switch( *format )
+               {
+                       case mlt_image_none:
+                               size = 0;
+                               *buffer = NULL;
+                               break;
+                       case mlt_image_rgb24:
+                               size *= 3;
+                               size += *width * 3;
+                               *buffer = mlt_pool_alloc( size );
+                               if ( *buffer )
+                                       memset( *buffer, 255, size );
+                               break;
+                       case mlt_image_rgb24a:
+                       case mlt_image_opengl:
+                               size *= 4;
+                               size += *width * 4;
+                               *buffer = mlt_pool_alloc( size );
+                               if ( *buffer )
+                                       memset( *buffer, 255, size );
+                               break;
+                       case mlt_image_yuv422:
+                               size *= 2;
+                               size += *width * 2;
+                               *buffer = mlt_pool_alloc( size );
+                               p = *buffer;
+                               q = p + size;
+                               while ( p != NULL && p != q )
+                               {
+                                       *p ++ = 235;
+                                       *p ++ = 128;
+                               }
+                               break;
+                       case mlt_image_yuv420p:
+                               size = size * 3 / 2;
+                               *buffer = mlt_pool_alloc( size );
+                               if ( *buffer )
+                                       memset( *buffer, 255, size );
+                               break;
+               }
+
+               mlt_properties_set_data( properties, "image", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "test_image", 1 );
+       }
+
+       mlt_properties_set_int( properties, "scaled_width", *width );
+       mlt_properties_set_int( properties, "scaled_height", *height );
+
+       return error;
+}
+
+uint8_t *mlt_frame_get_alpha_mask( mlt_frame this )
+{
+       uint8_t *alpha = NULL;
+       if ( this != NULL )
+       {
+               if ( this->get_alpha_mask != NULL )
+                       alpha = this->get_alpha_mask( this );
+               if ( alpha == NULL )
+                       alpha = mlt_properties_get_data( &this->parent, "alpha", NULL );
+               if ( alpha == NULL )
+               {
+                       int size = mlt_properties_get_int( &this->parent, "scaled_width" ) * mlt_properties_get_int( &this->parent, "scaled_height" );
+                       alpha = mlt_pool_alloc( size );
+                       memset( alpha, 255, size );
+                       mlt_properties_set_data( &this->parent, "alpha", alpha, size, mlt_pool_release, NULL );
+               }
+       }
+       return alpha;
+}
+
+int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       mlt_get_audio get_audio = mlt_frame_pop_audio( this );
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       int hide = mlt_properties_get_int( properties, "test_audio" );
+
+       if ( hide == 0 && get_audio != NULL )
+       {
+               mlt_position position = mlt_frame_get_position( this );
+               get_audio( this, buffer, format, frequency, channels, samples );
+               mlt_frame_set_position( this, position );
+       }
+       else if ( mlt_properties_get_data( properties, "audio", NULL ) )
+       {
+               *buffer = mlt_properties_get_data( properties, "audio", NULL );
+               *frequency = mlt_properties_get_int( properties, "audio_frequency" );
+               *channels = mlt_properties_get_int( properties, "audio_channels" );
+               *samples = mlt_properties_get_int( properties, "audio_samples" );
+       }
+       else
+       {
+               int size = 0;
+               *samples = *samples <= 0 ? 1920 : *samples;
+               *channels = *channels <= 0 ? 2 : *channels;
+               *frequency = *frequency <= 0 ? 48000 : *frequency;
+               size = *samples * *channels * sizeof( int16_t );
+               *buffer = mlt_pool_alloc( size );
+               if ( *buffer != NULL )
+                       memset( *buffer, 0, size );
+               mlt_properties_set_data( properties, "audio", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "test_audio", 1 );
+       }
+
+       mlt_properties_set_int( properties, "audio_frequency", *frequency );
+       mlt_properties_set_int( properties, "audio_channels", *channels );
+       mlt_properties_set_int( properties, "audio_samples", *samples );
+
+       if ( mlt_properties_get( properties, "meta.volume" ) )
+       {
+               double value = mlt_properties_get_double( properties, "meta.volume" );
+
+               if ( value == 0.0 )
+               {
+                       memset( *buffer, 0, *samples * *channels * 2 );
+               }
+               else if ( value != 1.0 )
+               {
+                       int total = *samples * *channels;
+                       int16_t *p = *buffer;
+                       while ( total -- )
+                       {
+                               *p = *p * value;
+                               p ++;
+                       }
+               }
+
+               mlt_properties_set( properties, "meta.volume", NULL );
+       }
+
+       return 0;
+}
+
+unsigned char *mlt_frame_get_waveform( mlt_frame this, int w, int h )
+{
+       int16_t *pcm = NULL;
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       mlt_audio_format format = mlt_audio_pcm;
+       int frequency = 32000; // lower frequency available?
+       int channels = 2;
+       double fps = mlt_profile_fps( NULL );
+       int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( this ) );
+
+       // Get the pcm data
+       mlt_frame_get_audio( this, &pcm, &format, &frequency, &channels, &samples );
+
+       // Make an 8-bit buffer large enough to hold rendering
+       int size = w * h;
+       unsigned char *bitmap = ( unsigned char* )mlt_pool_alloc( size );
+       if ( bitmap != NULL )
+               memset( bitmap, 0, size );
+       mlt_properties_set_data( properties, "waveform", bitmap, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+       // Render vertical lines
+       int16_t *ubound = pcm + samples * channels;
+       int skip = samples / w - 1;
+       int i, j, k;
+
+       // Iterate sample stream and along x coordinate
+       for ( i = 0; i < w && pcm < ubound; i++ )
+       {
+               // pcm data has channels interleaved
+               for ( j = 0; j < channels; j++ )
+               {
+                       // Determine sample's magnitude from 2s complement;
+                       int pcm_magnitude = *pcm < 0 ? ~(*pcm) + 1 : *pcm;
+                       // The height of a line is the ratio of the magnitude multiplied by
+                       // half the vertical resolution
+                       int height = ( int )( ( double )( pcm_magnitude ) / 32768 * h / 2 );
+                       // Determine the starting y coordinate - left channel above center,
+                       // right channel below - currently assumes 2 channels
+                       int displacement = ( h / 2 ) - ( 1 - j ) * height;
+                       // Position buffer pointer using y coordinate, stride, and x coordinate
+                       unsigned char *p = &bitmap[ i + displacement * w ];
+
+                       // Draw vertical line
+                       for ( k = 0; k < height; k++ )
+                               p[ w * k ] = 0xFF;
+
+                       pcm++;
+               }
+               pcm += skip * channels;
+       }
+
+       return bitmap;
+}
+
+mlt_producer mlt_frame_get_original_producer( mlt_frame this )
+{
+       if ( this != NULL )
+               return mlt_properties_get_data( MLT_FRAME_PROPERTIES( this ), "_producer", NULL );
+       return NULL;
+}
+
+void mlt_frame_close( mlt_frame this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( this ) ) <= 0 )
+       {
+               mlt_deque_close( this->stack_image );
+               mlt_deque_close( this->stack_audio );
+               while( mlt_deque_peek_back( this->stack_service ) )
+                       mlt_service_close( mlt_deque_pop_back( this->stack_service ) );
+               mlt_deque_close( this->stack_service );
+               mlt_properties_close( &this->parent );
+               free( this );
+       }
+}
+
+/***** convenience functions *****/
+
+int mlt_convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int total )
+{
+       int ret = 0;
+       int yy, uu, vv;
+       int r,g,b;
+       total /= 2;
+       while (total--)
+       {
+               yy = yuv[0];
+               uu = yuv[1];
+               vv = yuv[3];
+               YUV2RGB(yy, uu, vv, r, g, b);
+               rgba[0] = r;
+               rgba[1] = g;
+               rgba[2] = b;
+               rgba[3] = 255;
+               yy = yuv[2];
+               YUV2RGB(yy, uu, vv, r, g, b);
+               rgba[4] = r;
+               rgba[5] = g;
+               rgba[6] = b;
+               rgba[7] = 255;
+               yuv += 4;
+               rgba += 8;
+       }
+       return ret;
+}
+
+int mlt_convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+       int ret = 0;
+       register int y0, y1, u0, u1, v0, v1;
+       register int r, g, b;
+       register uint8_t *d = yuv;
+       register int i, j;
+
+       if ( alpha )
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       else
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+
+       return ret;
+}
+
+int mlt_convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv )
+{
+       int ret = 0;
+       register int y0, y1, u0, u1, v0, v1;
+       register int r, g, b;
+       register uint8_t *d = yuv;
+       register int i, j;
+
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgb + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       return ret;
+}
+
+int mlt_convert_bgr24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+       int ret = 0;
+       register int y0, y1, u0, u1, v0, v1;
+       register int r, g, b;
+       register uint8_t *d = yuv;
+       register int i, j;
+
+       if ( alpha )
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       *alpha++ = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       else
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       return ret;
+}
+
+int mlt_convert_bgr24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv )
+{
+       int ret = 0;
+       register int y0, y1, u0, u1, v0, v1;
+       register int r, g, b;
+       register uint8_t *d = yuv;
+       register int i, j;
+
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgb + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       b = *s++;
+                       g = *s++;
+                       r = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       return ret;
+}
+
+int mlt_convert_argb_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha )
+{
+       int ret = 0;
+       register int y0, y1, u0, u1, v0, v1;
+       register int r, g, b;
+       register uint8_t *d = yuv;
+       register int i, j;
+
+       if ( alpha )
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       *alpha++ = *s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *alpha++ = *s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       *alpha++ = *s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       else
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *s = rgba + ( stride * i );
+               for ( j = 0; j < ( width / 2 ); j++ )
+               {
+                       s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y1, u1 , v1);
+                       *d++ = y0;
+                       *d++ = (u0+u1) >> 1;
+                       *d++ = y1;
+                       *d++ = (v0+v1) >> 1;
+               }
+               if ( width % 2 )
+               {
+                       s++;
+                       r = *s++;
+                       g = *s++;
+                       b = *s++;
+                       RGB2YUV (r, g, b, y0, u0 , v0);
+                       *d++ = y0;
+                       *d++ = u0;
+               }
+       }
+       return ret;
+}
+
+int mlt_convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, int stride, uint8_t *yuv )
+{
+       int ret = 0;
+       register int i, j;
+
+       int half = width >> 1;
+
+       uint8_t *Y = yuv420p;
+       uint8_t *U = Y + width * height;
+       uint8_t *V = U + width * height / 4;
+
+       register uint8_t *d = yuv;
+
+       for ( i = 0; i < height; i++ )
+       {
+               register uint8_t *u = U + ( i / 2 ) * ( half );
+               register uint8_t *v = V + ( i / 2 ) * ( half );
+
+               for ( j = 0; j < half; j++ )
+               {
+                       *d ++ = *Y ++;
+                       *d ++ = *u ++;
+                       *d ++ = *Y ++;
+                       *d ++ = *v ++;
+               }
+       }
+       return ret;
+}
+
+uint8_t *mlt_resize_alpha( uint8_t *input, int owidth, int oheight, int iwidth, int iheight, uint8_t alpha_value )
+{
+       uint8_t *output = NULL;
+
+       if ( input != NULL && ( iwidth != owidth || iheight != oheight ) && ( owidth > 6 && oheight > 6 ) )
+       {
+               uint8_t *out_line;
+               int offset_x = ( owidth - iwidth ) / 2;
+               int offset_y = ( oheight - iheight ) / 2;
+               int iused = iwidth;
+
+               output = mlt_pool_alloc( owidth * oheight );
+               memset( output, alpha_value, owidth * oheight );
+
+               offset_x -= offset_x % 2;
+
+               out_line = output + offset_y * owidth;
+               out_line += offset_x;
+
+               // Loop for the entirety of our output height.
+               while ( iheight -- )
+               {
+                       // We're in the input range for this row.
+                       memcpy( out_line, input, iused );
+
+                       // Move to next input line
+                       input += iwidth;
+
+                       // Move to next output line
+                       out_line += owidth;
+               }
+       }
+
+       return output;
+}
+
+void mlt_resize_yuv422( uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight )
+{
+       // Calculate strides
+       int istride = iwidth * 2;
+       int ostride = owidth * 2;
+       int offset_x = ( owidth - iwidth );
+       int offset_y = ( oheight - iheight ) / 2;
+       uint8_t *in_line = input;
+       uint8_t *out_line;
+       int size = owidth * oheight;
+       uint8_t *p = output;
+
+       // Optimisation point
+       if ( output == NULL || input == NULL || ( owidth <= 6 || oheight <= 6 || iwidth <= 6 || oheight <= 6 ) )
+       {
+               return;
+       }
+       else if ( iwidth == owidth && iheight == oheight )
+       {
+               memcpy( output, input, iheight * istride );
+               return;
+       }
+
+       while( size -- )
+       {
+               *p ++ = 16;
+               *p ++ = 128;
+       }
+
+       offset_x -= offset_x % 4;
+
+       out_line = output + offset_y * ostride;
+       out_line += offset_x;
+
+       // Loop for the entirety of our output height.
+       while ( iheight -- )
+       {
+               // We're in the input range for this row.
+               memcpy( out_line, in_line, iwidth * 2 );
+
+               // Move to next input line
+               in_line += istride;
+
+               // Move to next output line
+               out_line += ostride;
+       }
+}
+
+/** A resizing function for yuv422 frames - this does not rescale, but simply
+       resizes. It assumes yuv422 images available on the frame so use with care.
+*/
+
+uint8_t *mlt_frame_resize_yuv422( mlt_frame this, int owidth, int oheight )
+{
+       // Get properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the input image, width and height
+       uint8_t *input = mlt_properties_get_data( properties, "image", NULL );
+       uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+       int iwidth = mlt_properties_get_int( properties, "width" );
+       int iheight = mlt_properties_get_int( properties, "height" );
+
+       // If width and height are correct, don't do anything
+       if ( iwidth != owidth || iheight != oheight )
+       {
+               uint8_t alpha_value = mlt_properties_get_int( properties, "resize_alpha" );
+
+               // Create the output image
+               uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+               // Call the generic resize
+               mlt_resize_yuv422( output, owidth, oheight, input, iwidth, iheight );
+
+               // Now update the frame
+               mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", owidth );
+               mlt_properties_set_int( properties, "height", oheight );
+
+               // We should resize the alpha too
+               alpha = mlt_resize_alpha( alpha, owidth, oheight, iwidth, iheight, alpha_value );
+               if ( alpha != NULL )
+               {
+                       mlt_properties_set_data( properties, "alpha", alpha, owidth * oheight, ( mlt_destructor )mlt_pool_release, NULL );
+                       this->get_alpha_mask = NULL;
+               }
+
+               // Return the output
+               return output;
+       }
+       // No change, return input
+       return input;
+}
+
+/** A rescaling function for yuv422 frames - low quality, and provided for testing
+       only. It assumes yuv422 images available on the frame so use with care.
+*/
+
+uint8_t *mlt_frame_rescale_yuv422( mlt_frame this, int owidth, int oheight )
+{
+       // Get properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the input image, width and height
+       uint8_t *input = mlt_properties_get_data( properties, "image", NULL );
+       int iwidth = mlt_properties_get_int( properties, "width" );
+       int iheight = mlt_properties_get_int( properties, "height" );
+
+       // If width and height are correct, don't do anything
+       if ( iwidth != owidth || iheight != oheight )
+       {
+               // Create the output image
+               uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+               // Calculate strides
+               int istride = iwidth * 2;
+               int ostride = owidth * 2;
+
+               iwidth = iwidth - ( iwidth % 4 );
+
+               // Derived coordinates
+               int dy, dx;
+
+       // Calculate ranges
+       int out_x_range = owidth / 2;
+       int out_y_range = oheight / 2;
+       int in_x_range = iwidth / 2;
+       int in_y_range = iheight / 2;
+
+       // Output pointers
+       register uint8_t *out_line = output;
+       register uint8_t *out_ptr;
+
+       // Calculate a middle pointer
+       uint8_t *in_middle = input + istride * in_y_range + in_x_range * 2;
+       uint8_t *in_line;
+
+               // Generate the affine transform scaling values
+               register int scale_width = ( iwidth << 16 ) / owidth;
+               register int scale_height = ( iheight << 16 ) / oheight;
+               register int base = 0;
+
+               int outer = out_x_range * scale_width;
+               int bottom = out_y_range * scale_height;
+
+       // Loop for the entirety of our output height.
+       for ( dy = - bottom; dy < bottom; dy += scale_height )
+       {
+               // Start at the beginning of the line
+               out_ptr = out_line;
+
+               // Pointer to the middle of the input line
+               in_line = in_middle + ( dy >> 16 ) * istride;
+
+               // Loop for the entirety of our output row.
+               for ( dx = - outer; dx < outer; dx += scale_width )
+               {
+                               base = dx >> 15;
+                               base &= 0xfffffffe;
+                               *out_ptr ++ = *( in_line + base );
+                               base &= 0xfffffffc;
+                               *out_ptr ++ = *( in_line + base + 1 );
+                               dx += scale_width;
+                               base = dx >> 15;
+                               base &= 0xfffffffe;
+                               *out_ptr ++ = *( in_line + base );
+                               base &= 0xfffffffc;
+                               *out_ptr ++ = *( in_line + base + 3 );
+               }
+
+               // Move to next output line
+               out_line += ostride;
+       }
+
+               // Now update the frame
+               mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", owidth );
+               mlt_properties_set_int( properties, "height", oheight );
+
+               // Return the output
+               return output;
+       }
+
+       // No change, return input
+       return input;
+}
+
+int mlt_frame_mix_audio( mlt_frame this, mlt_frame that, float weight_start, float weight_end, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, 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 d = 0, s = 0;
+
+       mlt_frame_get_audio( that, &src, format, &frequency_src, &channels_src, &samples_src );
+       mlt_frame_get_audio( this, &dest, format, &frequency_dest, &channels_dest, &samples_dest );
+
+       int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
+       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 ( channels_src > 6 )
+               channels_src = 0;
+       if ( channels_dest > 6 )
+               channels_dest = 0;
+       if ( samples_src > 4000 )
+               samples_src = 0;
+       if ( samples_dest > 4000 )
+               samples_dest = 0;
+
+       // 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;
+
+       // Compute a smooth ramp over start to end
+       float weight = weight_start;
+       float weight_step = ( weight_end - weight_start ) / *samples;
+
+       if ( src == dest )
+       {
+               *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++ )
+               {
+                       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 );
+               }
+               weight += weight_step;
+       }
+
+       return ret;
+}
+
+// Replacement for broken mlt_frame_audio_mix - this filter uses an inline low pass filter
+// to allow mixing without volume hacking
+int mlt_frame_combine_audio( mlt_frame this, mlt_frame that, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, 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( this ), "meta.mixdown" ) )
+               b_weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "meta.volume" );
+
+       mlt_frame_get_audio( that, &src, format, &frequency_src, &channels_src, &samples_src );
+       mlt_frame_get_audio( this, &dest, format, &frequency_dest, &channels_dest, &samples_dest );
+
+       int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
+       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;
+
+       for ( i = 0; i < *samples; i++ )
+       {
+               for ( j = 0; j < *channels; 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 );
+               }
+       }
+
+       return ret;
+}
+
+/* Will this break when mlt_position is converted to double? -Zach */
+int mlt_sample_calculator( float fps, int frequency, int64_t position )
+{
+       int samples = 0;
+
+       if ( ( int )( fps * 100 ) == 2997 )
+       {
+               samples = frequency / 30;
+
+               switch ( frequency )
+               {
+                       case 48000:
+                               if ( position % 5 != 0 )
+                                       samples += 2;
+                               break;
+                       case 44100:
+                               if ( position % 300 == 0 )
+                                       samples = 1471;
+                               else if ( position % 30 == 0 )
+                                       samples = 1470;
+                               else if ( position % 2 == 0 )
+                                       samples = 1472;
+                               else
+                                       samples = 1471;
+                               break;
+                       case 32000:
+                               if ( position % 30 == 0 )
+                                       samples = 1068;
+                               else if ( position % 29 == 0 )
+                                       samples = 1067;
+                               else if ( position % 4 == 2 )
+                                       samples = 1067;
+                               else
+                                       samples = 1068;
+                               break;
+                       default:
+                               samples = 0;
+               }
+       }
+       else if ( fps != 0 )
+       {
+               samples = frequency / fps;
+       }
+
+       return samples;
+}
+
+int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t frame )
+{
+       int64_t samples = 0;
+
+       // TODO: Correct rules for NTSC and drop the * 100 hack
+       if ( ( int )( fps * 100 ) == 2997 )
+       {
+               samples = ( ( double )( frame * frequency ) / 30 );
+               switch( frequency )
+               {
+                       case 48000:
+                               samples += 2 * ( frame / 5 );
+                               break;
+                       case 44100:
+                               samples += frame + ( frame / 2 ) - ( frame / 30 ) + ( frame / 300 );
+                               break;
+                       case 32000:
+                               samples += ( 2 * frame ) - ( frame / 4 ) - ( frame / 29 );
+                               break;
+               }
+       }
+       else if ( fps != 0 )
+       {
+               samples = ( ( frame * frequency ) / ( int )fps );
+       }
+
+       return samples;
+}
diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h
new file mode 100644 (file)
index 0000000..bd586a7
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * \file mlt_frame.h
+ * \brief interface for all frame classes
+ * \see mlt_frame_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_FRAME_H_
+#define _MLT_FRAME_H_
+
+#include "mlt_properties.h"
+#include "mlt_deque.h"
+#include "mlt_service.h"
+
+/** callback function to get video data
+ *
+ */
+
+typedef int ( *mlt_get_image )( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+
+/** callback function to get audio data
+ *
+ */
+
+typedef int ( *mlt_get_audio )( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+
+/** \brief Frame class
+ *
+ * \properties \em test_image set if the frame holds a "test card" image
+ * \properties \em test_audio set if the frame holds "test card" audio
+ * \properties \em _producer holds a reference to the frame's end producer
+ * \properties \em _speed the current speed of the producer that generated the frame
+ * \properties \em _position the position of the frame
+ * \properties \em meta.* holds metadata
+ * \properties \em hide set to 1 to hide the video, 2 to mute the audio
+ * \properties \em last_track a flag to indicate an end-of-tracks frame
+ */
+
+struct mlt_frame_s
+{
+       /* We're extending properties here */
+       struct mlt_properties_s parent;
+
+       /* Virtual methods */
+       uint8_t * ( *get_alpha_mask )( mlt_frame self );
+
+       /* Private properties */
+       mlt_deque stack_image;
+       mlt_deque stack_audio;
+       mlt_deque stack_service;
+};
+
+#define MLT_FRAME_PROPERTIES( frame )          ( &( frame )->parent )
+#define MLT_FRAME_SERVICE_STACK( frame )       ( ( frame )->stack_service )
+#define MLT_FRAME_IMAGE_STACK( frame )         ( ( frame )->stack_image )
+#define MLT_FRAME_AUDIO_STACK( frame )         ( ( frame )->stack_audio )
+
+extern mlt_frame mlt_frame_init( mlt_service service );
+extern mlt_properties mlt_frame_properties( mlt_frame self );
+extern int mlt_frame_is_test_card( mlt_frame self );
+extern int mlt_frame_is_test_audio( mlt_frame self );
+extern double mlt_frame_get_aspect_ratio( mlt_frame self );
+extern int mlt_frame_set_aspect_ratio( mlt_frame self, double value );
+extern mlt_position mlt_frame_get_position( mlt_frame self );
+extern int mlt_frame_set_position( mlt_frame self, mlt_position value );
+extern void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height );
+extern int mlt_frame_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable );
+extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame self );
+extern int mlt_frame_get_audio( mlt_frame self, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples );
+extern unsigned char *mlt_frame_get_waveform( mlt_frame self, int w, int h );
+extern int mlt_frame_push_get_image( mlt_frame self, mlt_get_image get_image );
+extern mlt_get_image mlt_frame_pop_get_image( mlt_frame self );
+extern int mlt_frame_push_frame( mlt_frame self, mlt_frame that );
+extern mlt_frame mlt_frame_pop_frame( mlt_frame self );
+extern int mlt_frame_push_service( mlt_frame self, void *that );
+extern void *mlt_frame_pop_service( mlt_frame self );
+extern int mlt_frame_push_service_int( mlt_frame self, int that );
+extern int mlt_frame_pop_service_int( mlt_frame self );
+extern int mlt_frame_push_audio( mlt_frame self, void *that );
+extern void *mlt_frame_pop_audio( mlt_frame self );
+extern mlt_deque mlt_frame_service_stack( mlt_frame self );
+extern mlt_producer mlt_frame_get_original_producer( mlt_frame self );
+extern void mlt_frame_close( mlt_frame self );
+
+/* convenience functions */
+extern int mlt_convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, unsigned int total );
+extern int mlt_convert_rgb24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_rgb24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv );
+extern int mlt_convert_bgr24a_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_argb_to_yuv422( uint8_t *rgba, int width, int height, int stride, uint8_t *yuv, uint8_t *alpha );
+extern int mlt_convert_bgr24_to_yuv422( uint8_t *rgb, int width, int height, int stride, uint8_t *yuv );
+extern int mlt_convert_yuv420p_to_yuv422( uint8_t *yuv420p, int width, int height, int stride, uint8_t *yuv );
+extern uint8_t *mlt_frame_resize_yuv422( mlt_frame self, int owidth, int oheight );
+extern uint8_t *mlt_frame_rescale_yuv422( mlt_frame self, int owidth, int oheight );
+extern void mlt_resize_yuv422( uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight );
+extern int mlt_frame_mix_audio( mlt_frame self, mlt_frame that, float weight_start, float weight_end, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples  );
+extern int mlt_frame_combine_audio( mlt_frame self, mlt_frame that, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples  );
+extern int mlt_sample_calculator( float fps, int frequency, int64_t position );
+extern int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t position );
+
+/** This macro scales rgb into the yuv gamut - y is scaled by 219/255 and uv by 224/255. */
+#define RGB2YUV(r, g, b, y, u, v)\
+  y = ((263*r + 516*g + 100*b) >> 10) + 16;\
+  u = ((-152*r - 298*g + 450*b) >> 10) + 128;\
+  v = ((450*r - 377*g - 73*b) >> 10) + 128;
+
+/** This macro assumes the user has already scaled their rgb down into the broadcast limits. **/
+#define RGB2YUV_UNSCALED(r, g, b, y, u, v)\
+  y = (299*r + 587*g + 114*b) >> 10;\
+  u = ((-169*r - 331*g + 500*b) >> 10) + 128;\
+  v = ((500*r - 419*g - 81*b) >> 10) + 128;\
+  y = y < 16 ? 16 : y;\
+  u = u < 16 ? 16 : u;\
+  v = v < 16 ? 16 : v;\
+  y = y > 235 ? 235 : y;\
+  u = u > 240 ? 240 : u;\
+  v = v > 240 ? 240 : v
+
+/** This macro converts a YUV value to the RGB color space. */
+#define YUV2RGB( y, u, v, r, g, b ) \
+  r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \
+  g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 400 * ( u - 128 ) ) >> 10 ); \
+  b = ((1192 * ( y - 16 ) + 2066 * ( u - 128 ) ) >> 10 ); \
+  r = r < 0 ? 0 : r > 255 ? 255 : r; \
+  g = g < 0 ? 0 : g > 255 ? 255 : g; \
+  b = b < 0 ? 0 : b > 255 ? 255 : b;
+
+#endif
diff --git a/src/framework/mlt_geometry.c b/src/framework/mlt_geometry.c
new file mode 100644 (file)
index 0000000..db9c877
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * mlt_geometry.c -- provides the geometry API
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_geometry.h"
+#include "mlt_tokeniser.h"
+#include "mlt_factory.h"
+#include "mlt_profile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+typedef struct geometry_item_s
+{
+       struct mlt_geometry_item_s data;
+       struct geometry_item_s *next, *prev;
+}
+*geometry_item;
+
+typedef struct
+{
+       char *data;
+       int length;
+       int nw;
+       int nh;
+       geometry_item item;
+}
+geometry_s, *geometry;
+
+// Create a new geometry structure
+mlt_geometry mlt_geometry_init( )
+{
+       mlt_geometry this = calloc( 1, sizeof( struct mlt_geometry_s ) );
+       if ( this != NULL )
+       {
+               this->local = calloc( 1, sizeof( geometry_s ) );
+               if ( this->local != NULL )
+               {
+                       geometry self = this->local;
+                       self->nw = 720;
+                       self->nh = 576;
+               }
+               else
+               {
+                       free( this );
+                       this = NULL;
+               }
+       }
+       return this;
+}
+
+/** A linear step
+*/
+
+static inline double linearstep( double start, double end, double position, int length )
+{
+       double o = ( end - start ) / length;
+       return start + position * o;
+}
+
+static void mlt_geometry_virtual_refresh( mlt_geometry this )
+{
+       geometry self = this->local;
+
+       // Parse of all items to ensure unspecified keys are calculated correctly
+       if ( self->item != NULL )
+       {
+               int i = 0;
+               for ( i = 0; i < 5; i ++ )
+               {
+                       geometry_item current = self->item;
+                       while( current != NULL )
+                       {
+                               int fixed = current->data.f[ i ];
+                               if ( !fixed )
+                               {
+                                       geometry_item prev = current->prev;
+                                       geometry_item next = current->next;
+
+                                       double prev_value = 0;
+                                       double next_value = 0;
+                                       double value = 0;
+
+                                       while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
+                                       while( next != NULL && !next->data.f[ i ] ) next = next->next;
+
+                                       switch( i )
+                                       {
+                                               case 0:
+                                                       if ( prev ) prev_value = prev->data.x;
+                                                       if ( next ) next_value = next->data.x;
+                                                       break;
+                                               case 1:
+                                                       if ( prev ) prev_value = prev->data.y;
+                                                       if ( next ) next_value = next->data.y;
+                                                       break;
+                                               case 2:
+                                                       if ( prev ) prev_value = prev->data.w;
+                                                       if ( next ) next_value = next->data.w;
+                                                       break;
+                                               case 3:
+                                                       if ( prev ) prev_value = prev->data.h;
+                                                       if ( next ) next_value = next->data.h;
+                                                       break;
+                                               case 4:
+                                                       if ( prev ) prev_value = prev->data.mix;
+                                                       if ( next ) next_value = next->data.mix;
+                                                       break;
+                                       }
+
+                                       // This should never happen
+                                       if ( prev == NULL )
+                                               current->data.f[ i ] = 1;
+                                       else if ( next == NULL )
+                                               value = prev_value;
+                                       else 
+                                               value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
+
+                                       switch( i )
+                                       {
+                                               case 0: current->data.x = value; break;
+                                               case 1: current->data.y = value; break;
+                                               case 2: current->data.w = value; break;
+                                               case 3: current->data.h = value; break;
+                                               case 4: current->data.mix = value; break;
+                                       }
+                               }
+
+                               // Move to the next item
+                               current = current->next;
+                       }
+               }
+       }
+}
+
+static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
+{
+       geometry self = this->local;
+
+       if ( item == self->item )
+       {
+               self->item = item->next;
+               if ( self->item != NULL )
+                       self->item->prev = NULL;
+               // To ensure correct seeding, ensure all values are fixed
+               if ( self->item != NULL )
+               {
+                       self->item->data.f[0] = 1;
+                       self->item->data.f[1] = 1;
+                       self->item->data.f[2] = 1;
+                       self->item->data.f[3] = 1;
+                       self->item->data.f[4] = 1;
+               }
+       }
+       else if ( item->next != NULL && item->prev != NULL )
+       {
+               item->prev->next = item->next;
+               item->next->prev = item->prev;
+       }
+       else if ( item->next != NULL )
+       {
+               item->next->prev = item->prev;
+       }
+       else if ( item->prev != NULL )
+       {
+               item->prev->next = item->next;
+       }
+
+       free( item );
+
+       return 0;
+}
+
+static void mlt_geometry_clean( mlt_geometry this )
+{
+       geometry self = this->local;
+       free( self->data );
+       self->data = NULL;
+       while( self->item )
+               mlt_geometry_drop( this, self->item );
+}
+
+// Parse the geometry specification for a given length and normalised width/height (-1 for default)
+// data is constructed as: [frame=]X,Y:WxH[:mix][;[frame=]X,Y:WxH[:mix]]*
+// and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
+int mlt_geometry_parse( mlt_geometry this, char *data, int length, int nw, int nh )
+{
+       int i = 0;
+
+       // Create a tokeniser
+       mlt_tokeniser tokens = mlt_tokeniser_init( );
+
+       // Get the local/private structure
+       geometry self = this->local;
+
+       // Clean the existing geometry
+       mlt_geometry_clean( this );
+
+       // Update the info on the data
+       if ( length != -1 )
+               self->length = length;
+       if ( nw != -1 )
+               self->nw = nw;
+       if ( nh != -1 )
+               self->nh = nh;
+       if ( data != NULL )
+               self->data = strdup( data );
+
+       // Tokenise
+       if ( data != NULL )
+               mlt_tokeniser_parse_new( tokens, data, ";" );
+
+       // Iterate through each token
+       for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
+       {
+               struct mlt_geometry_item_s item;
+               char *value = mlt_tokeniser_get_string( tokens, i );
+
+               // Set item to 0
+               memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
+
+               // Now parse the item
+               mlt_geometry_parse_item( this, &item, value );
+
+               // Now insert into place
+               mlt_geometry_insert( this, &item );
+       }
+
+       // Remove the tokeniser
+       mlt_tokeniser_close( tokens );
+
+       // ???
+       return 0;
+}
+
+// Conditionally refresh in case of a change
+int mlt_geometry_refresh( mlt_geometry this, char *data, int length, int nw, int nh )
+{
+       geometry self = this->local;
+       int changed = ( length != -1 && length != self->length );
+       changed = changed || ( nw != -1 && nw != self->nw );
+       changed = changed || ( nh != -1 && nh != self->nh );
+       changed = changed || ( data != NULL && ( self->data == NULL || strcmp( data, self->data ) ) );
+       if ( changed )
+               return mlt_geometry_parse( this, data, length, nw, nh );
+       return -1;
+}
+
+int mlt_geometry_get_length( mlt_geometry this )
+{
+       // Get the local/private structure
+       geometry self = this->local;
+
+       // return the length
+       return self->length;
+}
+
+void mlt_geometry_set_length( mlt_geometry this, int length )
+{
+       // Get the local/private structure
+       geometry self = this->local;
+
+       // set the length
+       self->length = length;
+}
+
+int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
+{
+       int ret = 0;
+
+       // Get the local/private structure
+       geometry self = this->local;
+
+       if ( value != NULL && strcmp( value, "" ) )
+       {
+               char *p = strchr( value, '=' );
+               int count = 0;
+               double temp;
+
+               // Determine if a position has been specified
+               if ( p != NULL )
+               {
+                       temp = atof( value );
+                       if ( temp > -1 && temp < 1 )
+                               item->frame = temp * self->length;
+                       else
+                               item->frame = temp;
+                       value = p + 1;
+               }
+
+               // Special case - frame < 0
+               if ( item->frame < 0 )
+                       item->frame += self->length;
+
+               // Obtain the current value at this position - this allows new
+               // frames to be created which don't specify all values
+               mlt_geometry_fetch( this, item, item->frame );
+
+               // Special case - when an empty string is specified, all values are fixed
+               // TODO: Check if this is logical - it's convenient, but it's also odd...
+               if ( !*value )
+               {
+                       item->f[0] = 1;
+                       item->f[1] = 1;
+                       item->f[2] = 1;
+                       item->f[3] = 1;
+                       item->f[4] = 1;
+               }
+
+               // Iterate through the remainder of value
+               while( *value )
+               {
+                       // Get the value
+                       temp = strtod( value, &p );
+
+                       // Check if a value was specified
+                       if ( p != value )
+                       {
+                               // Handle the % case
+                               if ( *p == '%' )
+                               {
+                                       if ( count == 0 || count == 2 )
+                                               temp *= self->nw / 100.0;
+                                       else if ( count == 1 || count == 3 )
+                                               temp *= self->nh / 100.0;
+                                       p ++;
+                               }
+
+                               // Special case - distort token
+                               if ( *p == '!' || *p == '*' )
+                               {
+                                       p ++;
+                                       item->distort = 1;
+                               }
+
+                               // Actually, we don't care about the delimiter at all..
+                               if ( *p ) p ++;
+
+                               // Assign to the item
+                               switch( count )
+                               {
+                                       case 0: item->x = temp; item->f[0] = 1; break;
+                                       case 1: item->y = temp; item->f[1] = 1; break;
+                                       case 2: item->w = temp; item->f[2] = 1; break;
+                                       case 3: item->h = temp; item->f[3] = 1; break;
+                                       case 4: item->mix = temp; item->f[4] = 1; break;
+                               }
+                       }
+                       else
+                       {
+                               p ++;
+                       }
+
+                       // Update the value pointer
+                       value = p;
+                       count ++;
+               }
+       }
+       else
+       {
+               ret = 1;
+       }
+
+       return ret;
+}
+
+// Fetch a geometry item for an absolute position
+int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
+{
+       // Get the local geometry
+       geometry self = this->local;
+
+       // Need to find the nearest key to the position specifed
+       geometry_item key = self->item;
+
+       // Iterate through the keys until we reach last or have 
+       while( key != NULL && key->next != NULL && position >= key->next->data.frame )
+               key = key->next;
+
+       if ( key != NULL )
+       {
+               // Position is situated before the first key - all zeroes
+               if ( position < key->data.frame )
+               {
+                       memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+                       item->mix = 100;
+               }
+               // Position is a key itself - no iterpolation need
+               else if ( position == key->data.frame )
+               {
+                       memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+               }
+               // Position is after the last key - no interpolation, but not a key frame
+               else if ( key->next == NULL )
+               {
+                       memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+                       item->key = 0;
+                       item->f[ 0 ] = 0;
+                       item->f[ 1 ] = 0;
+                       item->f[ 2 ] = 0;
+                       item->f[ 3 ] = 0;
+                       item->f[ 4 ] = 0;
+               }
+               // Interpolation is needed - position > key and there is a following key
+               else
+               {
+                       item->key = 0;
+                       item->frame = position;
+                       position -= key->data.frame;
+                       item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
+                       item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
+                       item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
+                       item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
+                       item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
+                       item->distort = key->data.distort;
+                       position += key->data.frame;
+               }
+
+               item->frame = position;
+       }
+       else
+       {
+               memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+               item->frame = position;
+               item->mix = 100;
+       }
+
+       return key == NULL;
+}
+
+// Specify a geometry item at an absolute position
+int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
+{
+       // Get the local/private geometry structure
+       geometry self = this->local;
+
+       // Create a new local item (this may be removed if a key already exists at this position)
+       geometry_item new = calloc( 1, sizeof( struct geometry_item_s ) );
+       memcpy( &new->data, item, sizeof( struct mlt_geometry_item_s ) );
+       new->data.key = 1;
+
+       // Determine if we need to insert or append to the list, or if it's a new list
+       if ( self->item != NULL )
+       {
+               // Get the first item
+               geometry_item place = self->item;
+
+               // Locate an existing nearby item
+               while ( place->next != NULL && item->frame > place->data.frame )
+                       place = place->next;
+
+               if ( item->frame < place->data.frame )
+               {
+                       if ( place == self->item )
+                               self->item = new;
+                       if ( place->prev )
+                               place->prev->next = new;
+                       new->next = place;
+                       new->prev = place->prev;
+                       place->prev = new;
+               }
+               else if ( item->frame > place->data.frame )
+               {
+                       if ( place->next )
+                               place->next->prev = new;
+                       new->next = place->next;
+                       new->prev = place;
+                       place->next = new;
+               }
+               else
+               {
+                       memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
+                       free( new );
+               }
+       }
+       else
+       {
+               // Set the first item
+               self->item = new;
+
+               // To ensure correct seeding, ensure all values are fixed
+               self->item->data.f[0] = 1;
+               self->item->data.f[1] = 1;
+               self->item->data.f[2] = 1;
+               self->item->data.f[3] = 1;
+               self->item->data.f[4] = 1;
+       }
+
+       // Refresh all geometries
+       mlt_geometry_virtual_refresh( this );
+
+       // TODO: Error checking
+       return 0;
+}
+
+// Remove the key at the specified position
+int mlt_geometry_remove( mlt_geometry this, int position )
+{
+       int ret = 1;
+
+       // Get the local/private geometry structure
+       geometry self = this->local;
+
+       // Get the first item
+       geometry_item place = self->item;
+
+       while( place != NULL && position != place->data.frame )
+               place = place->next;
+
+       if ( place != NULL && position == place->data.frame )
+               ret = mlt_geometry_drop( this, place );
+
+       // Refresh all geometries
+       mlt_geometry_virtual_refresh( this );
+
+       return ret;
+}
+
+// Get the key at the position or the next following
+int mlt_geometry_next_key( mlt_geometry this, mlt_geometry_item item, int position )
+{
+       // Get the local/private geometry structure
+       geometry self = this->local;
+
+       // Get the first item
+       geometry_item place = self->item;
+
+       while( place != NULL && position > place->data.frame )
+               place = place->next;
+
+       if ( place != NULL )
+               memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
+
+       return place == NULL;
+}
+
+// Get the key at the position or the previous key
+int mlt_geometry_prev_key( mlt_geometry this, mlt_geometry_item item, int position )
+{
+       // Get the local/private geometry structure
+       geometry self = this->local;
+
+       // Get the first item
+       geometry_item place = self->item;
+
+       while( place != NULL && place->next != NULL && position >= place->next->data.frame )
+               place = place->next;
+
+       if ( place != NULL )
+               memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
+
+       return place == NULL;
+}
+
+char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
+{
+       geometry self = this->local;
+       struct mlt_geometry_item_s item;
+       char *ret = malloc( 1000 );
+       int used = 0;
+       int size = 1000;
+
+       if ( in == -1 )
+               in = 0;
+       if ( out == -1 )
+               out = mlt_geometry_get_length( this );
+
+       if ( ret != NULL )
+       {
+               char temp[ 100 ];
+
+               strcpy( ret, "" );
+
+               item.frame = in;
+
+               while( 1 )
+               {
+                       strcpy( temp, "" );
+
+                       // If it's the first frame, then it's not necessarily a key
+                       if ( item.frame == in )
+                       {
+                               if ( mlt_geometry_fetch( this, &item, item.frame ) )
+                                       break;
+
+                               // If the first key is larger than the current position
+                               // then do nothing here
+                               if ( self->item->data.frame > item.frame )
+                               {
+                                       item.frame ++;
+                                       continue;
+                               }
+
+                               // To ensure correct seeding, ensure all values are fixed
+                               item.f[0] = 1;
+                               item.f[1] = 1;
+                               item.f[2] = 1;
+                               item.f[3] = 1;
+                               item.f[4] = 1;
+                       }
+                       // Typically, we move from key to key
+                       else if ( item.frame < out )
+                       {
+                               if ( mlt_geometry_next_key( this, &item, item.frame ) )
+                                       break;
+
+                               // Special case - crop at the out point
+                               if ( item.frame > out )
+                                       mlt_geometry_fetch( this, &item, out );
+                       }
+                       // We've handled the last key
+                       else
+                       {
+                               break;
+                       }
+
+                       if ( item.frame - in != 0 )
+                               sprintf( temp, "%d=", item.frame - in );
+
+                       if ( item.f[0] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.x );
+                       strcat( temp, "," );
+                       if ( item.f[1] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.y );
+                       strcat( temp, ":" );
+                       if ( item.f[2] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.w );
+                       strcat( temp, "x" );
+                       if ( item.f[3] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.h );
+                       if ( item.f[4] ) 
+                               sprintf( temp + strlen( temp ), ":%.0f", item.mix );
+
+                       if ( used + strlen( temp ) > size )
+                       {
+                               size += 1000;
+                               ret = realloc( ret, size );
+                       }
+
+                       if ( ret != NULL && used != 0 )
+                       {
+                               used ++;
+                               strcat( ret, ";" );
+                       }
+                       if ( ret != NULL )
+                       {
+                               used += strlen( temp );
+                               strcat( ret, temp );
+                       }
+
+                       item.frame ++;
+               }
+       }
+
+       return ret;
+}
+
+// Serialise the current geometry
+char *mlt_geometry_serialise( mlt_geometry this )
+{
+       geometry self = this->local;
+       char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
+       if ( ret )
+       {
+               free( self->data );
+               self->data = ret;
+       }
+       return ret;
+}
+
+// Close the geometry
+void mlt_geometry_close( mlt_geometry this )
+{
+       if ( this != NULL )
+       {
+               mlt_geometry_clean( this );
+               free( this->local );
+               free( this );
+       }
+}
+
+
diff --git a/src/framework/mlt_geometry.h b/src/framework/mlt_geometry.h
new file mode 100644 (file)
index 0000000..8120184
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * mlt_geometry.h -- provides the geometry API
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_GEOMETRY_H
+#define _MLT_GEOMETRY_H
+
+#include "mlt_types.h"
+
+struct mlt_geometry_item_s
+{
+       /* Will be 1 when this is a key frame */
+       int key;
+       /* The actual frame this corresponds to */
+       int frame;
+       /* Distort */
+       int distort;
+       /* x,y are upper left */
+       float x, y, w, h, mix;
+       /* Indicates which values are fixed */
+       int f[ 5 ];
+};
+
+struct mlt_geometry_s
+{
+       void *local;
+};
+
+/* Create a new geometry structure */
+extern mlt_geometry mlt_geometry_init( );
+/* Parse the geometry specification for a given length and normalised width/height (-1 for default) */
+extern int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh );
+/* Conditionally refresh the geometry if it's modified */
+extern int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh );
+/* Get and set the length */
+extern int mlt_geometry_get_length( mlt_geometry self );
+extern void mlt_geometry_set_length( mlt_geometry self, int length );
+/* Parse an item - doesn't affect the geometry itself but uses current information for evaluation */
+/* (item->frame should be specified if not included in the data itself) */
+extern int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *data );
+/* Fetch a geometry item for an absolute position */
+extern int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position );
+/* Specify a geometry item at an absolute position */
+extern int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item );
+/* Remove the key at the specified position */
+extern int mlt_geometry_remove( mlt_geometry self, int position );
+/* Get the key at the position or the next following */
+extern int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position );
+extern int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position );
+/* Serialise the current geometry */
+extern char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out );
+extern char *mlt_geometry_serialise( mlt_geometry self );
+/* Close the geometry */
+extern void mlt_geometry_close( mlt_geometry self );
+
+#endif
+
diff --git a/src/framework/mlt_log.c b/src/framework/mlt_log.c
new file mode 100644 (file)
index 0000000..9a4c70b
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * \file mlt_log.c
+ * \brief logging functions
+ *
+ * Copyright (c) 2003 Michel Bardiaux
+ *
+ * This file was a part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "mlt_log.h"
+#include "mlt_service.h"
+
+#include <string.h>
+
+static int log_level = MLT_LOG_INFO;
+
+void default_callback( void* ptr, int level, const char* fmt, va_list vl )
+{
+       static int print_prefix = 1;
+       mlt_properties properties = ptr ? MLT_SERVICE_PROPERTIES( ( mlt_service )ptr ) : NULL;
+       
+       if ( level > log_level )
+               return;
+       if ( print_prefix && properties )
+       {
+               char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+               char *resource = mlt_properties_get( properties, "resource" );
+       
+               if ( resource && *resource && resource[0] == '<' && resource[ strlen(resource) - 1 ] == '>' )
+                       mlt_type = resource;
+               fprintf( stderr, "[%s @ %p]", mlt_type, ptr );
+       }
+       print_prefix = strstr( fmt, "\n" ) != NULL;
+       vfprintf( stderr, fmt, vl );
+}
+
+static void ( *callback )( void*, int, const char*, va_list ) = default_callback;
+
+void mlt_log( void* service, int level, const char *fmt, ...)
+{
+       va_list vl;
+       
+       va_start( vl, fmt );
+       mlt_vlog( service, level, fmt, vl );
+       va_end( vl );
+}
+
+void mlt_vlog( void* service, int level, const char *fmt, va_list vl )
+{
+       if ( callback ) callback( service, level, fmt, vl );
+}
+
+int mlt_log_get_level( void )
+{
+       return log_level;
+}
+
+void mlt_log_set_level( int level )
+{
+       log_level = level;
+}
+
+void mlt_log_set_callback( void (*new_callback)( void*, int, const char*, va_list ) )
+{
+       callback = new_callback;
+}
diff --git a/src/framework/mlt_log.h b/src/framework/mlt_log.h
new file mode 100644 (file)
index 0000000..ec9a134
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * \file mlt_log.h
+ * \brief logging functions
+ *
+ * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file was a part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MLT_LOG_H
+#define MLT_LOG_H
+
+#include <stdarg.h>
+
+#define MLT_LOG_QUIET    -8
+
+/**
+ * something went really wrong and we will crash now
+ */
+#define MLT_LOG_PANIC     0
+
+/**
+ * something went wrong and recovery is not possible
+ * like no header in a format which depends on it or a combination
+ * of parameters which are not allowed
+ */
+#define MLT_LOG_FATAL     8
+
+/**
+ * something went wrong and cannot losslessly be recovered
+ * but not all future data is affected
+ */
+#define MLT_LOG_ERROR    16
+
+/**
+ * something somehow does not look correct / something which may or may not
+ * lead to some problems
+ */
+#define MLT_LOG_WARNING  24
+
+#define MLT_LOG_INFO     32
+#define MLT_LOG_VERBOSE  40
+
+/**
+ * stuff which is only useful for MLT developers
+ */
+#define MLT_LOG_DEBUG    48
+
+/**
+ * Send the specified message to the log if the level is less than or equal to
+ * the current logging level. By default, all logging messages are sent to
+ * stderr. This behavior can be altered by setting a different mlt_vlog callback
+ * function.
+ *
+ * \param service An optional pointer to a \p mlt_service_s.
+ * \param level The importance level of the message, lower values signifying
+ * higher importance.
+ * \param fmt The format string (printf-compatible) that specifies how
+ * subsequent arguments are converted to output.
+ * \see mlt_vlog
+ */
+#ifdef __GNUC__
+void mlt_log( void *service, int level, const char *fmt, ... ) __attribute__ ((__format__ (__printf__, 3, 4)));
+#else
+void mlt_log( void *service, int level, const char *fmt, ... );
+#endif
+
+#define mlt_log_panic(service, format, args...) mlt_log((service), MLT_LOG_PANIC, (format), ## args)
+#define mlt_log_fatal(service, format, args...) mlt_log((service), MLT_LOG_FATAL, (format), ## args)
+#define mlt_log_error(service, format, args...) mlt_log((service), MLT_LOG_ERROR, (format), ## args)
+#define mlt_log_warning(service, format, args...) mlt_log((service), MLT_LOG_WARNING, (format), ## args)
+#define mlt_log_info(service, format, args...) mlt_log((service), MLT_LOG_INFO, (format), ## args)
+#define mlt_log_verbose(service, format, args...) mlt_log((service), MLT_LOG_VERBOSE, (format), ## args)
+#define mlt_log_debug(service, format, args...) mlt_log((service), MLT_LOG_DEBUG, (format), ## args)
+
+void mlt_vlog( void *service, int level, const char *fmt, va_list );
+int mlt_log_get_level( void );
+void mlt_log_set_level( int );
+void mlt_log_set_callback( void (*)( void*, int, const char*, va_list ) );
+
+#endif /* MLT_LOG_H */
diff --git a/src/framework/mlt_multitrack.c b/src/framework/mlt_multitrack.c
new file mode 100644 (file)
index 0000000..a98d412
--- /dev/null
@@ -0,0 +1,514 @@
+/**
+ * \file mlt_multitrack.c
+ * \brief multitrack service class
+ * \see mlt_multitrack_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_multitrack.h"
+#include "mlt_playlist.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Forward reference. */
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+
+/** Construct and initialize a new multitrack.
+ *
+ * Sets the resource property to "<multitrack>".
+ *
+ * \public \memberof mlt_multitrack_s
+ * \return a new multitrack
+ */
+
+mlt_multitrack mlt_multitrack_init( )
+{
+       // Allocate the multitrack object
+       mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
+
+       if ( this != NULL )
+       {
+               mlt_producer producer = &this->parent;
+               if ( mlt_producer_init( producer, this ) == 0 )
+               {
+                       mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
+                       producer->get_frame = producer_get_frame;
+                       mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
+                       mlt_properties_set( properties, "log_id", "multitrack" );
+                       mlt_properties_set( properties, "resource", "<multitrack>" );
+                       mlt_properties_set_int( properties, "in", 0 );
+                       mlt_properties_set_int( properties, "out", -1 );
+                       mlt_properties_set_int( properties, "length", 0 );
+                       producer->close = ( mlt_destructor )mlt_multitrack_close;
+               }
+               else
+               {
+                       free( this );
+                       this = NULL;
+               }
+       }
+
+       return this;
+}
+
+/** Get the producer associated to this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the producer object
+ * \see MLT_MULTITRACK_PRODUCER
+ */
+
+mlt_producer mlt_multitrack_producer( mlt_multitrack this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the service associated this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the service object
+ * \see MLT_MULTITRACK_SERVICE
+ */
+
+mlt_service mlt_multitrack_service( mlt_multitrack this )
+{
+       return MLT_MULTITRACK_SERVICE( this );
+}
+
+/** Get the properties associated this multitrack.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the multitrack's property list
+ * \see MLT_MULTITRACK_PROPERTIES
+ */
+
+mlt_properties mlt_multitrack_properties( mlt_multitrack this )
+{
+       return MLT_MULTITRACK_PROPERTIES( this );
+}
+
+/** Initialize position related information.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ */
+
+void mlt_multitrack_refresh( mlt_multitrack this )
+{
+       int i = 0;
+
+       // Obtain the properties of this multitrack
+       mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this );
+
+       // We need to ensure that the multitrack reports the longest track as its length
+       mlt_position length = 0;
+
+       // Obtain stats on all connected services
+       for ( i = 0; i < this->count; i ++ )
+       {
+               // Get the producer from this index
+               mlt_track track = this->list[ i ];
+               mlt_producer producer = track->producer;
+
+               // If it's allocated then, update our stats
+               if ( producer != NULL )
+               {
+                       // If we have more than 1 track, we must be in continue mode
+                       if ( this->count > 1 )
+                               mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "continue" );
+
+                       // Determine the longest length
+                       //if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
+                               length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
+               }
+       }
+
+       // Update multitrack properties now - we'll not destroy the in point here
+       mlt_events_block( properties, properties );
+       mlt_properties_set_position( properties, "length", length );
+       mlt_events_unblock( properties, properties );
+       mlt_properties_set_position( properties, "out", length - 1 );
+}
+
+/** Listener for producers on the playlist.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param producer a producer
+ * \param this a multitrack
+ */
+
+static void mlt_multitrack_listener( mlt_producer producer, mlt_multitrack this )
+{
+       mlt_multitrack_refresh( this );
+}
+
+/** Connect a producer to a given track.
+ *
+ * Note that any producer can be connected here, but see special case treatment
+ * of playlist in clip point determination below.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param producer the producer to connect to the multitrack producer
+ * \param track the 0-based index of the track on which to connect the multitrack
+ * \return true on error
+ */
+
+int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
+{
+       // Connect to the producer to ourselves at the specified track
+       int result = mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( this ), MLT_PRODUCER_SERVICE( producer ), track );
+
+       if ( result == 0 )
+       {
+               // Resize the producer list if need be
+               if ( track >= this->size )
+               {
+                       int i;
+                       this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_track ) );
+                       for ( i = this->size; i < track + 10; i ++ )
+                               this->list[ i ] = NULL;
+                       this->size = track + 10;
+               }
+
+               if ( this->list[ track ] != NULL )
+               {
+                       mlt_event_close( this->list[ track ]->event );
+                       mlt_producer_close( this->list[ track ]->producer );
+               }
+               else
+               {
+                       this->list[ track ] = malloc( sizeof( struct mlt_track_s ) );
+               }
+
+               // Assign the track in our list here
+               this->list[ track ]->producer = producer;
+               this->list[ track ]->event = mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer ), this,
+                                                                        "producer-changed", ( mlt_listener )mlt_multitrack_listener );
+               mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+               mlt_event_inc_ref( this->list[ track ]->event );
+
+               // Increment the track count if need be
+               if ( track >= this->count )
+                       this->count = track + 1;
+
+               // Refresh our stats
+               mlt_multitrack_refresh( this );
+       }
+
+       return result;
+}
+
+/** Get the number of tracks.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \return the number of tracks
+ */
+
+int mlt_multitrack_count( mlt_multitrack this )
+{
+       return this->count;
+}
+
+/** Get an individual track as a producer.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param track the 0-based index of the producer to get
+ * \return the producer or NULL if not valid
+ */
+
+mlt_producer mlt_multitrack_track( mlt_multitrack this, int track )
+{
+       mlt_producer producer = NULL;
+
+       if ( this->list != NULL && track < this->count )
+               producer = this->list[ track ]->producer;
+
+       return producer;
+}
+
+/** Position comparison function for sorting.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param p1 a position
+ * \param p2 another position
+ * \return <0 if \p p1 is less than \p p2, 0 if equal, >0 if greater
+ */
+
+static int position_compare( const void *p1, const void *p2 )
+{
+       return *( const mlt_position * )p1 - *( const mlt_position * )p2;
+}
+
+/** Add a position to a set.
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param array an array of positions (the set)
+ * \param size the current number of positions in the array (not the capacity of the array)
+ * \param position the position to add
+ * \return the new size of the array
+ */
+
+static int add_unique( mlt_position *array, int size, mlt_position position )
+{
+       int i = 0;
+       for ( i = 0; i < size; i ++ )
+               if ( array[ i ] == position )
+                       break;
+       if ( i == size )
+               array[ size ++ ] = position;
+       return size;
+}
+
+/** Determine the clip point.
+ *
+ * <pre>
+ * Special case here: a 'producer' has no concept of multiple clips - only the
+ * playlist and multitrack producers have clip functionality. Further to that a
+ * multitrack determines clip information from any connected tracks that happen
+ * to be playlists.
+ *
+ * Additionally, it must locate clips in the correct order, for example, consider
+ * the following track arrangement:
+ *
+ * playlist1 |0.0     |b0.0      |0.1          |0.1         |0.2           |
+ * playlist2 |b1.0  |1.0           |b1.1     |1.1             |
+ *
+ * Note - b clips represent blanks. They are also reported as clip positions.
+ *
+ * When extracting clip positions from these playlists, we should get a sequence of:
+ *
+ * 0.0, 1.0, b0.0, 0.1, b1.1, 1.1, 0.1, 0.2, [out of playlist2], [out of playlist1]
+ * </pre>
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ * \param whence from where to extract
+ * \param index the 0-based index of which clip to extract
+ * \return the position of clip \p index relative to \p whence
+ */
+
+mlt_position mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
+{
+       mlt_position position = 0;
+       int i = 0;
+       int j = 0;
+       mlt_position *map = malloc( 1000 * sizeof( mlt_position ) );
+       int count = 0;
+
+       for ( i = 0; i < this->count; i ++ )
+       {
+               // Get the producer for this track
+               mlt_producer producer = this->list[ i ]->producer;
+
+               // If it's assigned and not a hidden track
+               if ( producer != NULL )
+               {
+                       // Get the properties of this producer
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+                       // Determine if it's a playlist
+                       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+                       // Special case consideration of playlists
+                       if ( playlist != NULL )
+                       {
+                               for ( j = 0; j < mlt_playlist_count( playlist ); j ++ )
+                                       count = add_unique( map, count, mlt_playlist_clip( playlist, mlt_whence_relative_start, j ) );
+                               count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
+                       }
+                       else
+                       {
+                               count = add_unique( map, count, 0 );
+                               count = add_unique( map, count, mlt_producer_get_out( producer ) + 1 );
+                       }
+               }
+       }
+
+       // Now sort the map
+       qsort( map, count, sizeof( mlt_position ), position_compare );
+
+       // Now locate the requested index
+       switch( whence )
+       {
+               case mlt_whence_relative_start:
+                       if ( index < count )
+                               position = map[ index ];
+                       else
+                               position = map[ count - 1 ];
+                       break;
+
+               case mlt_whence_relative_current:
+                       position = mlt_producer_position( MLT_MULTITRACK_PRODUCER( this ) );
+                       for ( i = 0; i < count - 2; i ++ )
+                               if ( position >= map[ i ] && position < map[ i + 1 ] )
+                                       break;
+                       index += i;
+                       if ( index >= 0 && index < count )
+                               position = map[ index ];
+                       else if ( index < 0 )
+                               position = map[ 0 ];
+                       else
+                               position = map[ count - 1 ];
+                       break;
+
+               case mlt_whence_relative_end:
+                       if ( index < count )
+                               position = map[ count - index - 1 ];
+                       else
+                               position = map[ 0 ];
+                       break;
+       }
+
+       // Free the map
+       free( map );
+
+       return position;
+}
+
+/** Get frame method.
+ *
+ * <pre>
+ * Special case here: The multitrack must be used in a conjunction with a downstream
+ * tractor-type service, ie:
+ *
+ * Producer1 \
+ * Producer2 - multitrack - { filters/transitions } - tractor - consumer
+ * Producer3 /
+ *
+ * The get_frame of a tractor pulls frames from it's connected service on all tracks and
+ * will terminate as soon as it receives a test card with a last_track property. The
+ * important case here is that the mulitrack does not move to the next frame until all
+ * tracks have been pulled.
+ *
+ * Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
+ * that all producers are positioned on the same frame. It uses the 'last track' logic
+ * to determine when to move to the next frame.
+ *
+ * Flaw: if a transition is configured to read from a b-track which happens to trigger
+ * the last frame logic (ie: it's configured incorrectly), then things are going to go
+ * out of sync.
+ *
+ * See playlist logic too.
+ * </pre>
+ *
+ * \private \memberof mlt_multitrack_s
+ * \param parent the producer interface to a mulitrack
+ * \param[out] frame a frame by reference
+ * \param index the 0-based track index
+ * \return true if there was an error
+ */
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
+{
+       // Get the mutiltrack object
+       mlt_multitrack this = parent->child;
+
+       // Check if we have a track for this index
+       if ( index < this->count && this->list[ index ] != NULL )
+       {
+               // Get the producer for this track
+               mlt_producer producer = this->list[ index ]->producer;
+
+               // Get the track hide property
+               int hide = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) ), "hide" );
+
+               // Obtain the current position
+               mlt_position position = mlt_producer_frame( parent );
+
+               // Get the parent properties
+               mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( parent );
+
+               // Get the speed
+               double speed = mlt_properties_get_double( producer_properties, "_speed" );
+
+               // Make sure we're at the same point
+               mlt_producer_seek( producer, position );
+
+               // Get the frame from the producer
+               mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), frame, 0 );
+
+               // Indicate speed of this producer
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+               mlt_properties_set_double( properties, "_speed", speed );
+               mlt_properties_set_position( properties, "_position", position );
+               mlt_properties_set_int( properties, "hide", hide );
+       }
+       else
+       {
+               // Generate a test frame
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+
+               // Update position on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( parent ) );
+
+               // Move on to the next frame
+               if ( index >= this->count )
+               {
+                       // Let tractor know if we've reached the end
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "last_track", 1 );
+
+                       // Move to the next frame
+                       mlt_producer_prepare_next( parent );
+               }
+       }
+
+       return 0;
+}
+
+/** Close this instance and free its resources.
+ *
+ * \public \memberof mlt_multitrack_s
+ * \param this a multitrack
+ */
+
+void mlt_multitrack_close( mlt_multitrack this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( this ) ) <= 0 )
+       {
+               int i = 0;
+               for ( i = 0; i < this->count; i ++ )
+               {
+                       if ( this->list[ i ] != NULL )
+                       {
+                               mlt_event_close( this->list[ i ]->event );
+                               mlt_producer_close( this->list[ i ]->producer );
+                               free( this->list[ i ] );
+                       }
+               }
+
+               // Close the producer
+               this->parent.close = NULL;
+               mlt_producer_close( &this->parent );
+
+               // Free the list
+               free( this->list );
+
+               // Free the object
+               free( this );
+       }
+}
diff --git a/src/framework/mlt_multitrack.h b/src/framework/mlt_multitrack.h
new file mode 100644 (file)
index 0000000..427083c
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * \file mlt_multitrack.h
+ * \brief multitrack service class
+ * \see mlt_multitrack_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_MULITRACK_H_
+#define _MLT_MULITRACK_H_
+
+#include "mlt_producer.h"
+
+/** \brief Track class used by mlt_multitrack_s
+ */
+
+struct mlt_track_s
+{
+       mlt_producer producer;
+       mlt_event event;
+};
+
+typedef struct mlt_track_s *mlt_track;
+
+/** \brief Multitrack class
+ *
+ * A multitrack is a parallel container of producers that acts a single producer.
+ *
+ * \extends mlt_producer_s
+ * \properties \em log_id not currently used, but sets it to "mulitrack"
+ */
+
+struct mlt_multitrack_s
+{
+       /** We're extending producer here */
+       struct mlt_producer_s parent;
+       mlt_track *list;
+       int size;
+       int count;
+};
+
+#define MLT_MULTITRACK_PRODUCER( multitrack )  ( &( multitrack )->parent )
+#define MLT_MULTITRACK_SERVICE( multitrack )   MLT_PRODUCER_SERVICE( MLT_MULTITRACK_PRODUCER( multitrack ) )
+#define MLT_MULTITRACK_PROPERTIES( multitrack )        MLT_SERVICE_PROPERTIES( MLT_MULTITRACK_SERVICE( multitrack ) )
+
+extern mlt_multitrack mlt_multitrack_init( );
+extern mlt_producer mlt_multitrack_producer( mlt_multitrack self );
+extern mlt_service mlt_multitrack_service( mlt_multitrack self );
+extern mlt_properties mlt_multitrack_properties( mlt_multitrack self );
+extern int mlt_multitrack_connect( mlt_multitrack self, mlt_producer producer, int track );
+extern mlt_position mlt_multitrack_clip( mlt_multitrack self, mlt_whence whence, int index );
+extern void mlt_multitrack_close( mlt_multitrack self );
+extern int mlt_multitrack_count( mlt_multitrack self );
+extern void mlt_multitrack_refresh( mlt_multitrack self );
+extern mlt_producer mlt_multitrack_track( mlt_multitrack self, int track );
+
+#endif
+
diff --git a/src/framework/mlt_parser.c b/src/framework/mlt_parser.c
new file mode 100644 (file)
index 0000000..c5dd4ec
--- /dev/null
@@ -0,0 +1,245 @@
+/**
+ * \file mlt_parser.c
+ * \brief service parsing functionality
+ * \see mlt_parser_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt.h"
+#include <stdlib.h>
+
+static int on_invalid( mlt_parser this, mlt_service object )
+{
+       return 0;
+}
+
+static int on_unknown( mlt_parser this, mlt_service object )
+{
+       return 0;
+}
+
+static int on_start_producer( mlt_parser this, mlt_producer object )
+{
+       return 0;
+}
+
+static int on_end_producer( mlt_parser this, mlt_producer object )
+{
+       return 0;
+}
+
+static int on_start_playlist( mlt_parser this, mlt_playlist object )
+{
+       return 0;
+}
+
+static int on_end_playlist( mlt_parser this, mlt_playlist object )
+{
+       return 0;
+}
+
+static int on_start_tractor( mlt_parser this, mlt_tractor object )
+{
+       return 0;
+}
+
+static int on_end_tractor( mlt_parser this, mlt_tractor object )
+{
+       return 0;
+}
+
+static int on_start_multitrack( mlt_parser this, mlt_multitrack object )
+{
+       return 0;
+}
+
+static int on_end_multitrack( mlt_parser this, mlt_multitrack object )
+{
+       return 0;
+}
+
+static int on_start_track( mlt_parser this )
+{
+       return 0;
+}
+
+static int on_end_track( mlt_parser this )
+{
+       return 0;
+}
+
+static int on_start_filter( mlt_parser this, mlt_filter object )
+{
+       return 0;
+}
+
+static int on_end_filter( mlt_parser this, mlt_filter object )
+{
+       return 0;
+}
+
+static int on_start_transition( mlt_parser this, mlt_transition object )
+{
+       return 0;
+}
+
+static int on_end_transition( mlt_parser this, mlt_transition object )
+{
+       return 0;
+}
+
+mlt_parser mlt_parser_new( )
+{
+       mlt_parser this = calloc( 1, sizeof( struct mlt_parser_s ) );
+       if ( this != NULL && mlt_properties_init( &this->parent, this ) == 0 )
+       {
+               this->on_invalid = on_invalid;
+               this->on_unknown = on_unknown;
+               this->on_start_producer = on_start_producer;
+               this->on_end_producer = on_end_producer;
+               this->on_start_playlist = on_start_playlist;
+               this->on_end_playlist = on_end_playlist;
+               this->on_start_tractor = on_start_tractor;
+               this->on_end_tractor = on_end_tractor;
+               this->on_start_multitrack = on_start_multitrack;
+               this->on_end_multitrack = on_end_multitrack;
+               this->on_start_track = on_start_track;
+               this->on_end_track = on_end_track;
+               this->on_start_filter = on_start_filter;
+               this->on_end_filter = on_end_filter;
+               this->on_start_transition = on_start_transition;
+               this->on_end_transition = on_end_transition;
+       }
+       return this;
+}
+
+mlt_properties mlt_parser_properties( mlt_parser this )
+{
+       return &this->parent;
+}
+
+int mlt_parser_start( mlt_parser this, mlt_service object )
+{
+       int error = 0;
+       mlt_service_type type = mlt_service_identify( object );
+       switch( type )
+       {
+               case invalid_type:
+                       error = this->on_invalid( this, object );
+                       break;
+               case unknown_type:
+                       error = this->on_unknown( this, object );
+                       break;
+               case producer_type:
+                       if ( mlt_producer_is_cut( ( mlt_producer )object ) )
+                               error = mlt_parser_start( this, ( mlt_service )mlt_producer_cut_parent( ( mlt_producer )object ) );
+                       error = this->on_start_producer( this, ( mlt_producer )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_producer( this, ( mlt_producer )object );
+                       break;
+               case playlist_type:
+                       error = this->on_start_playlist( this, ( mlt_playlist )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               while ( error == 0 && i < mlt_playlist_count( ( mlt_playlist )object ) )
+                                       mlt_parser_start( this, ( mlt_service )mlt_playlist_get_clip( ( mlt_playlist )object, i ++ ) );
+                               i = 0;
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_playlist( this, ( mlt_playlist )object );
+                       break;
+               case tractor_type:
+                       error = this->on_start_tractor( this, ( mlt_tractor )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               mlt_service next = mlt_service_producer( object );
+                               mlt_parser_start( this, ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) );
+                               while ( next != ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) )
+                               {
+                                       mlt_parser_start( this, next );
+                                       next = mlt_service_producer( next );
+                               }
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_tractor( this, ( mlt_tractor )object );
+                       break;
+               case multitrack_type:
+                       error = this->on_start_multitrack( this, ( mlt_multitrack )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               while ( i < mlt_multitrack_count( ( mlt_multitrack )object ) )
+                               {
+                                       this->on_start_track( this );
+                                       mlt_parser_start( this, ( mlt_service )mlt_multitrack_track( ( mlt_multitrack )object , i ++ ) );
+                                       this->on_end_track( this );
+                               }
+                               i = 0;
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_multitrack( this, ( mlt_multitrack )object );
+                       break;
+               case filter_type:
+                       error = this->on_start_filter( this, ( mlt_filter )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_filter( this, ( mlt_filter )object );
+                       break;
+               case transition_type:
+                       error = this->on_start_transition( this, ( mlt_transition )object );
+                       if ( error == 0 )
+                       {
+                               int i = 0;
+                               while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL )
+                                       error = mlt_parser_start( this, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) );
+                       }
+                       error = this->on_end_transition( this, ( mlt_transition )object );
+                       break;
+               case field_type:
+                       break;
+               case consumer_type:
+                       break;
+       }
+       return error;
+}
+
+void mlt_parser_close( mlt_parser this )
+{
+       if ( this != NULL )
+       {
+               mlt_properties_close( &this->parent );
+               free( this );
+       }
+}
+
+
diff --git a/src/framework/mlt_parser.h b/src/framework/mlt_parser.h
new file mode 100644 (file)
index 0000000..e82149c
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * \file mlt_parser.h
+ * \brief service parsing functionality
+ * \see mlt_parser_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_PARSER_H_
+#define _MLT_PARSER_H_
+
+#include "mlt_types.h"
+
+/** \brief Parser class
+ *
+ * \extends mlt_properties_s
+ */
+
+struct mlt_parser_s
+{
+       struct mlt_properties_s parent;
+       int ( *on_invalid )( mlt_parser self, mlt_service object );
+       int ( *on_unknown )( mlt_parser self, mlt_service object );
+       int ( *on_start_producer )( mlt_parser self, mlt_producer object );
+       int ( *on_end_producer )( mlt_parser self, mlt_producer object );
+       int ( *on_start_playlist )( mlt_parser self, mlt_playlist object );
+       int ( *on_end_playlist )( mlt_parser self, mlt_playlist object );
+       int ( *on_start_tractor )( mlt_parser self, mlt_tractor object );
+       int ( *on_end_tractor )( mlt_parser self, mlt_tractor object );
+       int ( *on_start_multitrack )( mlt_parser self, mlt_multitrack object );
+       int ( *on_end_multitrack )( mlt_parser self, mlt_multitrack object );
+       int ( *on_start_track )( mlt_parser self );
+       int ( *on_end_track )( mlt_parser self );
+       int ( *on_start_filter )( mlt_parser self, mlt_filter object );
+       int ( *on_end_filter )( mlt_parser self, mlt_filter object );
+       int ( *on_start_transition )( mlt_parser self, mlt_transition object );
+       int ( *on_end_transition )( mlt_parser self, mlt_transition object );
+};
+
+extern mlt_parser mlt_parser_new( );
+extern mlt_properties mlt_parser_properties( mlt_parser self );
+extern int mlt_parser_start( mlt_parser self, mlt_service object );
+extern void mlt_parser_close( mlt_parser self );
+
+#endif
diff --git a/src/framework/mlt_playlist.c b/src/framework/mlt_playlist.c
new file mode 100644 (file)
index 0000000..91f9fa8
--- /dev/null
@@ -0,0 +1,1839 @@
+/**
+ * \file mlt_playlist.c
+ * \brief playlist service class
+ * \see mlt_playlist_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_playlist.h"
+#include "mlt_tractor.h"
+#include "mlt_multitrack.h"
+#include "mlt_field.h"
+#include "mlt_frame.h"
+#include "mlt_transition.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief Virtual playlist entry used by mlt_playlist_s
+*/
+
+struct playlist_entry_s
+{
+       mlt_producer producer;
+       mlt_position frame_in;
+       mlt_position frame_out;
+       mlt_position frame_count;
+       int repeat;
+       mlt_position producer_length;
+       mlt_event event;
+       int preservation_hack;
+};
+
+/* Forward declarations
+*/
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static int mlt_playlist_unmix( mlt_playlist this, int clip );
+static int mlt_playlist_resize_mix( mlt_playlist this, int clip, int in, int out );
+
+/** Construct a playlist.
+ *
+ * Sets the resource property to "<playlist>".
+ * Set the mlt_type to property to "mlt_producer".
+ * \public \memberof mlt_playlist_s
+ * \return a new playlist
+ */
+
+mlt_playlist mlt_playlist_init( )
+{
+       mlt_playlist this = calloc( sizeof( struct mlt_playlist_s ), 1 );
+       if ( this != NULL )
+       {
+               mlt_producer producer = &this->parent;
+
+               // Construct the producer
+               mlt_producer_init( producer, this );
+
+               // Override the producer get_frame
+               producer->get_frame = producer_get_frame;
+
+               // Define the destructor
+               producer->close = ( mlt_destructor )mlt_playlist_close;
+               producer->close_object = this;
+
+               // Initialise blank
+               mlt_producer_init( &this->blank, NULL );
+               mlt_properties_set( MLT_PRODUCER_PROPERTIES( &this->blank ), "mlt_service", "blank" );
+               mlt_properties_set( MLT_PRODUCER_PROPERTIES( &this->blank ), "resource", "blank" );
+
+               // Indicate that this producer is a playlist
+               mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( this ), "playlist", this, 0, NULL, NULL );
+
+               // Specify the eof condition
+               mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "eof", "pause" );
+               mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "resource", "<playlist>" );
+               mlt_properties_set( MLT_PLAYLIST_PROPERTIES( this ), "mlt_type", "mlt_producer" );
+               mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "in", 0 );
+               mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "out", -1 );
+               mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( this ), "length", 0 );
+
+               this->size = 10;
+               this->list = malloc( this->size * sizeof( playlist_entry * ) );
+       }
+
+       return this;
+}
+
+/** Get the producer associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the producer interface
+ * \see MLT_PLAYLIST_PRODUCER
+ */
+
+mlt_producer mlt_playlist_producer( mlt_playlist this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the service associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the service interface
+ * \see MLT_PLAYLIST_SERVICE
+ */
+
+mlt_service mlt_playlist_service( mlt_playlist this )
+{
+       return MLT_PRODUCER_SERVICE( &this->parent );
+}
+
+/** Get the properties associated to this playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the playlist's properties list
+ * \see MLT_PLAYLIST_PROPERTIES
+ */
+
+mlt_properties mlt_playlist_properties( mlt_playlist this )
+{
+       return MLT_PRODUCER_PROPERTIES( &this->parent );
+}
+
+/** Refresh the playlist after a clip has been changed.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return false
+ */
+
+static int mlt_playlist_virtual_refresh( mlt_playlist this )
+{
+       // Obtain the properties
+       mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+       int i = 0;
+       mlt_position frame_count = 0;
+
+       for ( i = 0; i < this->count; i ++ )
+       {
+               // Get the producer
+               mlt_producer producer = this->list[ i ]->producer;
+               if ( producer )
+               {
+                       int current_length = mlt_producer_get_out( producer ) - mlt_producer_get_in( producer ) + 1;
+
+                       // Check if the length of the producer has changed
+                       if ( this->list[ i ]->frame_in != mlt_producer_get_in( producer ) ||
+                               this->list[ i ]->frame_out != mlt_producer_get_out( producer ) )
+                       {
+                               // This clip should be removed...
+                               if ( current_length < 1 )
+                               {
+                                       this->list[ i ]->frame_in = 0;
+                                       this->list[ i ]->frame_out = -1;
+                                       this->list[ i ]->frame_count = 0;
+                               }
+                               else
+                               {
+                                       this->list[ i ]->frame_in = mlt_producer_get_in( producer );
+                                       this->list[ i ]->frame_out = mlt_producer_get_out( producer );
+                                       this->list[ i ]->frame_count = current_length;
+                               }
+
+                               // Update the producer_length
+                               this->list[ i ]->producer_length = current_length;
+                       }
+               }
+
+               // Calculate the frame_count
+               this->list[ i ]->frame_count = ( this->list[ i ]->frame_out - this->list[ i ]->frame_in + 1 ) * this->list[ i ]->repeat;
+
+               // Update the frame_count for this clip
+               frame_count += this->list[ i ]->frame_count;
+       }
+
+       // Refresh all properties
+       mlt_events_block( properties, properties );
+       mlt_properties_set_position( properties, "length", frame_count );
+       mlt_events_unblock( properties, properties );
+       mlt_properties_set_position( properties, "out", frame_count - 1 );
+
+       return 0;
+}
+
+/** Listener for producers on the playlist.
+ *
+ * Refreshes the playlist whenever an entry receives producer-changed.
+ * \private \memberof mlt_playlist_s
+ * \param producer a producer
+ * \param this a playlist
+ */
+
+static void mlt_playlist_listener( mlt_producer producer, mlt_playlist this )
+{
+       mlt_playlist_virtual_refresh( this );
+}
+
+/** Append to the virtual playlist.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param source a producer
+ * \param in the producer's starting time
+ * \param out the producer's ending time
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer source, mlt_position in, mlt_position out )
+{
+       mlt_producer producer = NULL;
+       mlt_properties properties = NULL;
+       mlt_properties parent = NULL;
+
+       // If we have a cut, then use the in/out points from the cut
+       if ( mlt_producer_is_blank( source )  )
+       {
+               // Make sure the blank is long enough to accomodate the length specified
+               if ( out - in + 1 > mlt_producer_get_length( &this->blank ) )
+               {
+                       mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &this->blank );
+                       mlt_events_block( blank_props, blank_props );
+                       mlt_producer_set_in_and_out( &this->blank, in, out );
+                       mlt_events_unblock( blank_props, blank_props );
+               }
+
+               // Now make sure the cut comes from this this->blank
+               if ( source == NULL )
+               {
+                       producer = mlt_producer_cut( &this->blank, in, out );
+               }
+               else if ( !mlt_producer_is_cut( source ) || mlt_producer_cut_parent( source ) != &this->blank )
+               {
+                       producer = mlt_producer_cut( &this->blank, in, out );
+               }
+               else
+               {
+                       producer = source;
+                       mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+               }
+
+               properties = MLT_PRODUCER_PROPERTIES( producer );
+       }
+       else if ( mlt_producer_is_cut( source ) )
+       {
+               producer = source;
+               if ( in == -1 )
+                       in = mlt_producer_get_in( producer );
+               if ( out == -1 || out > mlt_producer_get_out( producer ) )
+                       out = mlt_producer_get_out( producer );
+               properties = MLT_PRODUCER_PROPERTIES( producer );
+               mlt_properties_inc_ref( properties );
+       }
+       else
+       {
+               producer = mlt_producer_cut( source, in, out );
+               if ( in == -1 || in < mlt_producer_get_in( producer ) )
+                       in = mlt_producer_get_in( producer );
+               if ( out == -1 || out > mlt_producer_get_out( producer ) )
+                       out = mlt_producer_get_out( producer );
+               properties = MLT_PRODUCER_PROPERTIES( producer );
+       }
+
+       // Fetch the cuts parent properties
+       parent = MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( producer ) );
+
+       // Remove fezzik normalisers for fx cuts
+       if ( mlt_properties_get_int( parent, "meta.fx_cut" ) )
+       {
+               mlt_service service = MLT_PRODUCER_SERVICE( mlt_producer_cut_parent( producer ) );
+               mlt_filter filter = mlt_service_filter( service, 0 );
+               while ( filter != NULL && mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "_fezzik" ) )
+               {
+                       mlt_service_detach( service, filter );
+                       filter = mlt_service_filter( service, 0 );
+               }
+               mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "meta.fx_cut", 1 );
+       }
+
+       // Check that we have room
+       if ( this->count >= this->size )
+       {
+               int i;
+               this->list = realloc( this->list, ( this->size + 10 ) * sizeof( playlist_entry * ) );
+               for ( i = this->size; i < this->size + 10; i ++ ) this->list[ i ] = NULL;
+               this->size += 10;
+       }
+
+       // Create the entry
+       this->list[ this->count ] = calloc( sizeof( playlist_entry ), 1 );
+       if ( this->list[ this->count ] != NULL )
+       {
+               this->list[ this->count ]->producer = producer;
+               this->list[ this->count ]->frame_in = in;
+               this->list[ this->count ]->frame_out = out;
+               this->list[ this->count ]->frame_count = out - in + 1;
+               this->list[ this->count ]->repeat = 1;
+               this->list[ this->count ]->producer_length = mlt_producer_get_out( producer ) - mlt_producer_get_in( producer ) + 1;
+               this->list[ this->count ]->event = mlt_events_listen( parent, this, "producer-changed", ( mlt_listener )mlt_playlist_listener );
+               mlt_event_inc_ref( this->list[ this->count ]->event );
+               mlt_properties_set( properties, "eof", "pause" );
+               mlt_producer_set_speed( producer, 0 );
+               this->count ++;
+       }
+
+       return mlt_playlist_virtual_refresh( this );
+}
+
+/** Locate a producer by index.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param[in, out] position the time at which to locate the producer, returns the time relative to the producer's starting point
+ * \param[out] clip the index of the playlist entry
+ * \param[out] total the duration of the playlist up to and including this producer
+ * \return a producer or NULL if not found
+ */
+
+static mlt_producer mlt_playlist_locate( mlt_playlist this, mlt_position *position, int *clip, int *total )
+{
+       // Default producer to NULL
+       mlt_producer producer = NULL;
+
+       // Loop for each producer until found
+       for ( *clip = 0; *clip < this->count; *clip += 1 )
+       {
+               // Increment the total
+               *total += this->list[ *clip ]->frame_count;
+
+               // Check if the position indicates that we have found the clip
+               // Note that 0 length clips get skipped automatically
+               if ( *position < this->list[ *clip ]->frame_count )
+               {
+                       // Found it, now break
+                       producer = this->list[ *clip ]->producer;
+                       break;
+               }
+               else
+               {
+                       // Decrement position by length of this entry
+                       *position -= this->list[ *clip ]->frame_count;
+               }
+       }
+
+       return producer;
+}
+
+/** Seek in the virtual playlist.
+ *
+ * This gets the producer at the current position and seeks on the producer
+ * while doing repeat and end-of-file handling. This is also responsible for
+ * closing producers previous to the preceding playlist if the autoclose
+ * property is set.
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param[out] progressive true if the producer should be displayed progressively
+ * \return the service interface of the producer at the play head
+ * \see producer_get_frame
+ */
+
+static mlt_service mlt_playlist_virtual_seek( mlt_playlist this, int *progressive )
+{
+       // Map playlist position to real producer in virtual playlist
+       mlt_position position = mlt_producer_frame( &this->parent );
+
+       // Keep the original position since we change it while iterating through the list
+       mlt_position original = position;
+
+       // Clip index and total
+       int i = 0;
+       int total = 0;
+
+       // Locate the producer for the position
+       mlt_producer producer = mlt_playlist_locate( this, &position, &i, &total );
+
+       // Get the properties
+       mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+       // Automatically close previous producers if requested
+       if ( i > 1 // keep immediate previous in case app wants to get info about what just finished
+               && position < 2 // tolerate off-by-one error on going to next clip
+               && mlt_properties_get_int( properties, "autoclose" ) )
+       {
+               int j;
+               // They might have jumped ahead!
+               for ( j = 0; j < i - 1; j++ )
+               {
+                       mlt_service_lock( MLT_PRODUCER_SERVICE( this->list[ j ]->producer ) );
+                       mlt_producer p = this->list[ j ]->producer;
+                       if ( p )
+                       {
+                               this->list[ j ]->producer = NULL;
+                               mlt_service_unlock( MLT_PRODUCER_SERVICE( p ) );
+                               mlt_producer_close( p );
+                       }
+                       // If p is null, the lock will not have been "taken"
+               }
+       }
+
+       // Get the eof handling
+       char *eof = mlt_properties_get( properties, "eof" );
+
+       // Seek in real producer to relative position
+       if ( producer != NULL )
+       {
+               int count = this->list[ i ]->frame_count / this->list[ i ]->repeat;
+               *progressive = count == 1;
+               mlt_producer_seek( producer, (int)position % count );
+       }
+       else if ( !strcmp( eof, "pause" ) && total > 0 )
+       {
+               playlist_entry *entry = this->list[ this->count - 1 ];
+               int count = entry->frame_count / entry->repeat;
+               mlt_producer this_producer = MLT_PLAYLIST_PRODUCER( this );
+               mlt_producer_seek( this_producer, original - 1 );
+               producer = entry->producer;
+               mlt_producer_seek( producer, (int)entry->frame_out % count );
+               mlt_producer_set_speed( this_producer, 0 );
+               mlt_producer_set_speed( producer, 0 );
+               *progressive = count == 1;
+       }
+       else if ( !strcmp( eof, "loop" ) && total > 0 )
+       {
+               playlist_entry *entry = this->list[ 0 ];
+               mlt_producer this_producer = MLT_PLAYLIST_PRODUCER( this );
+               mlt_producer_seek( this_producer, 0 );
+               producer = entry->producer;
+               mlt_producer_seek( producer, 0 );
+       }
+       else
+       {
+               producer = &this->blank;
+       }
+
+       return MLT_PRODUCER_SERVICE( producer );
+}
+
+/** Invoked when a producer indicates that it has prematurely reached its end.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return a producer
+ * \see producer_get_frame
+ */
+
+static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist this )
+{
+       // Default producer to blank
+       mlt_producer producer = &this->blank;
+
+       // Map playlist position to real producer in virtual playlist
+       mlt_position position = mlt_producer_frame( &this->parent );
+
+       // Loop through the virtual playlist
+       int i = 0;
+
+       for ( i = 0; i < this->count; i ++ )
+       {
+               if ( position < this->list[ i ]->frame_count )
+               {
+                       // Found it, now break
+                       producer = this->list[ i ]->producer;
+                       break;
+               }
+               else
+               {
+                       // Decrement position by length of this entry
+                       position -= this->list[ i ]->frame_count;
+               }
+       }
+
+       // Seek in real producer to relative position
+       if ( i < this->count && this->list[ i ]->frame_out != position )
+       {
+               // Update the frame_count for the changed clip (hmmm)
+               this->list[ i ]->frame_out = position;
+               this->list[ i ]->frame_count = this->list[ i ]->frame_out - this->list[ i ]->frame_in + 1;
+
+               // Refresh the playlist
+               mlt_playlist_virtual_refresh( this );
+       }
+
+       return producer;
+}
+
+/** Obtain the current clips index.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the index of the playlist entry at the current position
+ */
+
+int mlt_playlist_current_clip( mlt_playlist this )
+{
+       // Map playlist position to real producer in virtual playlist
+       mlt_position position = mlt_producer_frame( &this->parent );
+
+       // Loop through the virtual playlist
+       int i = 0;
+
+       for ( i = 0; i < this->count; i ++ )
+       {
+               if ( position < this->list[ i ]->frame_count )
+               {
+                       // Found it, now break
+                       break;
+               }
+               else
+               {
+                       // Decrement position by length of this entry
+                       position -= this->list[ i ]->frame_count;
+               }
+       }
+
+       return i;
+}
+
+/** Obtain the current clips producer.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the producer at the current position
+ */
+
+mlt_producer mlt_playlist_current( mlt_playlist this )
+{
+       int i = mlt_playlist_current_clip( this );
+       if ( i < this->count )
+               return this->list[ i ]->producer;
+       else
+               return &this->blank;
+}
+
+/** Get the position which corresponds to the start of the next clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param whence the location from which to make the index relative:
+ * start of playlist, end of playlist, or current position
+ * \param index the playlist entry index relative to whence
+ * \return the time at which the referenced clip starts
+ */
+
+mlt_position mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
+{
+       mlt_position position = 0;
+       int absolute_clip = index;
+       int i = 0;
+
+       // Determine the absolute clip
+       switch ( whence )
+       {
+               case mlt_whence_relative_start:
+                       absolute_clip = index;
+                       break;
+
+               case mlt_whence_relative_current:
+                       absolute_clip = mlt_playlist_current_clip( this ) + index;
+                       break;
+
+               case mlt_whence_relative_end:
+                       absolute_clip = this->count - index;
+                       break;
+       }
+
+       // Check that we're in a valid range
+       if ( absolute_clip < 0 )
+               absolute_clip = 0;
+       else if ( absolute_clip > this->count )
+               absolute_clip = this->count;
+
+       // Now determine the position
+       for ( i = 0; i < absolute_clip; i ++ )
+               position += this->list[ i ]->frame_count;
+
+       return position;
+}
+
+/** Get all the info about the clip specified.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param info a clip info struct
+ * \param index a playlist entry index
+ * \return true if there was an error
+ */
+
+int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index )
+{
+       int error = index < 0 || index >= this->count || this->list[ index ]->producer == NULL;
+       memset( info, 0, sizeof( mlt_playlist_clip_info ) );
+       if ( !error )
+       {
+               mlt_producer producer = mlt_producer_cut_parent( this->list[ index ]->producer );
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+               info->clip = index;
+               info->producer = producer;
+               info->cut = this->list[ index ]->producer;
+               info->start = mlt_playlist_clip( this, mlt_whence_relative_start, index );
+               info->resource = mlt_properties_get( properties, "resource" );
+               info->frame_in = this->list[ index ]->frame_in;
+               info->frame_out = this->list[ index ]->frame_out;
+               info->frame_count = this->list[ index ]->frame_count;
+               info->repeat = this->list[ index ]->repeat;
+               info->length = mlt_producer_get_length( producer );
+               info->fps = mlt_producer_get_fps( producer );
+       }
+
+       return error;
+}
+
+/** Get number of clips in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return the number of playlist entries
+ */
+
+int mlt_playlist_count( mlt_playlist this )
+{
+       return this->count;
+}
+
+/** Clear the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \return true if there was an error
+ */
+
+int mlt_playlist_clear( mlt_playlist this )
+{
+       int i;
+       for ( i = 0; i < this->count; i ++ )
+       {
+               mlt_event_close( this->list[ i ]->event );
+               mlt_producer_close( this->list[ i ]->producer );
+       }
+       this->count = 0;
+       return mlt_playlist_virtual_refresh( this );
+}
+
+/** Append a producer to the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to append
+ * \return true if there was an error
+ */
+
+int mlt_playlist_append( mlt_playlist this, mlt_producer producer )
+{
+       // Append to virtual list
+       return mlt_playlist_virtual_append( this, producer, 0, mlt_producer_get_playtime( producer ) - 1 );
+}
+
+/** Append a producer to the playlist with in/out points.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to append
+ * \param in the starting point on the producer
+ * \param out the ending point on the producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out )
+{
+       // Append to virtual list
+       if ( in != -1 && out != -1 )
+               return mlt_playlist_virtual_append( this, producer, in, out );
+       else
+               return mlt_playlist_append( this, producer );
+}
+
+/** Append a blank to the playlist of a given length.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param length the ending time of the blank entry, not its duration
+ * \return true if there was an error
+ */
+
+int mlt_playlist_blank( mlt_playlist this, mlt_position length )
+{
+       // Append to the virtual list
+       if (length >= 0)
+               return mlt_playlist_virtual_append( this, &this->blank, 0, length );
+       else
+               return 1;
+}
+
+/** Insert a producer into the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param producer the producer to insert
+ * \param where the producer's playlist entry index
+ * \param in the starting point on the producer
+ * \param out the ending point on the producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_insert( mlt_playlist this, mlt_producer producer, int where, mlt_position in, mlt_position out )
+{
+       // Append to end
+       mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+       mlt_playlist_append_io( this, producer, in, out );
+
+       // Move to the position specified
+       mlt_playlist_move( this, this->count - 1, where );
+       mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+       return mlt_playlist_virtual_refresh( this );
+}
+
+/** Remove an entry in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param where the playlist entry index
+ * \return true if there was an error
+ */
+
+int mlt_playlist_remove( mlt_playlist this, int where )
+{
+       int error = where < 0 || where >= this->count;
+       if ( error == 0 && mlt_playlist_unmix( this, where ) != 0 )
+       {
+               // We need to know the current clip and the position within the playlist
+               int current = mlt_playlist_current_clip( this );
+               mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( this ) );
+
+               // We need all the details about the clip we're removing
+               mlt_playlist_clip_info where_info;
+               playlist_entry *entry = this->list[ where ];
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( entry->producer );
+
+               // Loop variable
+               int i = 0;
+
+               // Get the clip info
+               mlt_playlist_get_clip_info( this, &where_info, where );
+
+               // Make sure the clip to be removed is valid and correct if necessary
+               if ( where < 0 )
+                       where = 0;
+               if ( where >= this->count )
+                       where = this->count - 1;
+
+               // Reorganise the list
+               for ( i = where + 1; i < this->count; i ++ )
+                       this->list[ i - 1 ] = this->list[ i ];
+               this->count --;
+
+               if ( entry->preservation_hack == 0 )
+               {
+                       // Decouple from mix_in/out if necessary
+                       if ( mlt_properties_get_data( properties, "mix_in", NULL ) != NULL )
+                       {
+                               mlt_properties mix = mlt_properties_get_data( properties, "mix_in", NULL );
+                               mlt_properties_set_data( mix, "mix_out", NULL, 0, NULL, NULL );
+                       }
+                       if ( mlt_properties_get_data( properties, "mix_out", NULL ) != NULL )
+                       {
+                               mlt_properties mix = mlt_properties_get_data( properties, "mix_out", NULL );
+                               mlt_properties_set_data( mix, "mix_in", NULL, 0, NULL, NULL );
+                       }
+
+                       if ( mlt_properties_ref_count( MLT_PRODUCER_PROPERTIES( entry->producer ) ) == 1 )
+                               mlt_producer_clear( entry->producer );
+               }
+
+               // Close the producer associated to the clip info
+               mlt_event_close( entry->event );
+               mlt_producer_close( entry->producer );
+
+               // Correct position
+               if ( where == current )
+                       mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), where_info.start );
+               else if ( where < current && this->count > 0 )
+                       mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), position - where_info.frame_count );
+               else if ( this->count == 0 )
+                       mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), 0 );
+
+               // Free the entry
+               free( entry );
+
+               // Refresh the playlist
+               mlt_playlist_virtual_refresh( this );
+       }
+
+       return error;
+}
+
+/** Move an entry in the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param src an entry index
+ * \param dest an entry index
+ * \return false
+ */
+
+int mlt_playlist_move( mlt_playlist this, int src, int dest )
+{
+       int i;
+
+       /* We need to ensure that the requested indexes are valid and correct it as necessary */
+       if ( src < 0 )
+               src = 0;
+       if ( src >= this->count )
+               src = this->count - 1;
+
+       if ( dest < 0 )
+               dest = 0;
+       if ( dest >= this->count )
+               dest = this->count - 1;
+
+       if ( src != dest && this->count > 1 )
+       {
+               int current = mlt_playlist_current_clip( this );
+               mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( this ) );
+               playlist_entry *src_entry = NULL;
+
+               // We need all the details about the current clip
+               mlt_playlist_clip_info current_info;
+
+               mlt_playlist_get_clip_info( this, &current_info, current );
+               position -= current_info.start;
+
+               if ( current == src )
+                       current = dest;
+               else if ( current > src && current < dest )
+                       current ++;
+               else if ( current == dest )
+                       current = src;
+
+               src_entry = this->list[ src ];
+               if ( src > dest )
+               {
+                       for ( i = src; i > dest; i -- )
+                               this->list[ i ] = this->list[ i - 1 ];
+               }
+               else
+               {
+                       for ( i = src; i < dest; i ++ )
+                               this->list[ i ] = this->list[ i + 1 ];
+               }
+               this->list[ dest ] = src_entry;
+
+               mlt_playlist_get_clip_info( this, &current_info, current );
+               mlt_producer_seek( MLT_PLAYLIST_PRODUCER( this ), current_info.start + position );
+               mlt_playlist_virtual_refresh( this );
+       }
+
+       return 0;
+}
+
+/** Repeat the specified clip n times.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip a playlist entry index
+ * \param repeat the number of times to repeat the clip
+ * \return true if there was an error
+ */
+
+int mlt_playlist_repeat_clip( mlt_playlist this, int clip, int repeat )
+{
+       int error = repeat < 1 || clip < 0 || clip >= this->count;
+       if ( error == 0 )
+       {
+               playlist_entry *entry = this->list[ clip ];
+               entry->repeat = repeat;
+               mlt_playlist_virtual_refresh( this );
+       }
+       return error;
+}
+
+/** Resize the specified clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param in the new starting time on the clip's producer
+ * \param out the new ending time on the clip's producer
+ * \return true if there was an error
+ */
+
+int mlt_playlist_resize_clip( mlt_playlist this, int clip, mlt_position in, mlt_position out )
+{
+       int error = clip < 0 || clip >= this->count;
+       if ( error == 0 && mlt_playlist_resize_mix( this, clip, in, out ) != 0 )
+       {
+               playlist_entry *entry = this->list[ clip ];
+               mlt_producer producer = entry->producer;
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+               mlt_events_block( properties, properties );
+
+               if ( mlt_producer_is_blank( producer ) )
+               {
+                       // Make sure the blank is long enough to accomodate the length specified
+                       if ( out - in + 1 > mlt_producer_get_length( &this->blank ) )
+                       {
+                               mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &this->blank );
+                               mlt_properties_set_int( blank_props, "length", out - in + 1 );
+                               mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "length", out - in + 1 );
+                               mlt_producer_set_in_and_out( &this->blank, 0, out - in );
+                       }
+               }
+
+               if ( in <= -1 )
+                       in = 0;
+               if ( out <= -1 || out >= mlt_producer_get_length( producer ) )
+                       out = mlt_producer_get_length( producer ) - 1;
+
+               if ( out < in )
+               {
+                       mlt_position t = in;
+                       in = out;
+                       out = t;
+               }
+
+               mlt_producer_set_in_and_out( producer, in, out );
+               mlt_events_unblock( properties, properties );
+               mlt_playlist_virtual_refresh( this );
+       }
+       return error;
+}
+
+/** Split a clip on the playlist at the given position.
+ *
+ * This splits after the specified frame.
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param position the time at which to split relative to the beginning of the clip or its end if negative
+ * \return true if there was an error
+ */
+
+int mlt_playlist_split( mlt_playlist this, int clip, mlt_position position )
+{
+       int error = clip < 0 || clip >= this->count;
+       if ( error == 0 )
+       {
+               playlist_entry *entry = this->list[ clip ];
+               position = position < 0 ? entry->frame_count + position - 1 : position;
+               if ( position >= 0 && position < entry->frame_count - 1 )
+               {
+                       int in = entry->frame_in;
+                       int out = entry->frame_out;
+                       mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+                       mlt_playlist_resize_clip( this, clip, in, in + position );
+                       if ( !mlt_producer_is_blank( entry->producer ) )
+                       {
+                               int i = 0;
+                               mlt_properties entry_properties = MLT_PRODUCER_PROPERTIES( entry->producer );
+                               mlt_producer split = mlt_producer_cut( entry->producer, in + position + 1, out );
+                               mlt_properties split_properties = MLT_PRODUCER_PROPERTIES( split );
+                               mlt_playlist_insert( this, split, clip + 1, 0, -1 );
+                               for ( i = 0; i < mlt_properties_count( entry_properties ); i ++ )
+                               {
+                                       char *name = mlt_properties_get_name( entry_properties, i );
+                                       if ( name != NULL && !strncmp( name, "meta.", 5 ) )
+                                               mlt_properties_set( split_properties, name, mlt_properties_get_value( entry_properties, i ) );
+                               }
+                               mlt_producer_close( split );
+                       }
+                       else
+                       {
+                               mlt_playlist_insert( this, &this->blank, clip + 1, 0, out - position - 1 );
+                       }
+                       mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+                       mlt_playlist_virtual_refresh( this );
+               }
+               else
+               {
+                       error = 1;
+               }
+       }
+       return error;
+}
+
+/** Split the playlist at the absolute position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which to split relative to the beginning of the clip
+ * \param left true to split before the frame starting at position
+ * \return true if there was an error
+ */
+
+int mlt_playlist_split_at( mlt_playlist this, mlt_position position, int left )
+{
+       int result = this == NULL ? -1 : 0;
+       if ( !result )
+       {
+               if ( position >= 0 && position < mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) ) )
+               {
+                       int clip = mlt_playlist_get_clip_index_at( this, position );
+                       mlt_playlist_clip_info info;
+                       mlt_playlist_get_clip_info( this, &info, clip );
+                       if ( left && position != info.start )
+                               mlt_playlist_split( this, clip, position - info.start - 1 );
+                       else if ( !left )
+                               mlt_playlist_split( this, clip, position - info.start );
+                       result = position;
+               }
+               else if ( position <= 0 )
+               {
+                       result = 0;
+               }
+               else
+               {
+                       result = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+               }
+       }
+       return result;
+}
+
+/** Join 1 or more consecutive clips.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the starting playlist entry index
+ * \param count the number of entries to merge
+ * \param merge ignored
+ * \return true if there was an error
+ */
+
+int mlt_playlist_join( mlt_playlist this, int clip, int count, int merge )
+{
+       int error = clip < 0 || clip >= this->count;
+       if ( error == 0 )
+       {
+               int i = clip;
+               mlt_playlist new_clip = mlt_playlist_init( );
+               mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+               if ( clip + count >= this->count )
+                       count = this->count - clip - 1;
+               for ( i = 0; i <= count; i ++ )
+               {
+                       playlist_entry *entry = this->list[ clip ];
+                       mlt_playlist_append( new_clip, entry->producer );
+                       mlt_playlist_repeat_clip( new_clip, i, entry->repeat );
+                       entry->preservation_hack = 1;
+                       mlt_playlist_remove( this, clip );
+               }
+               mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+               mlt_playlist_insert( this, MLT_PLAYLIST_PRODUCER( new_clip ), clip, 0, -1 );
+               mlt_playlist_close( new_clip );
+       }
+       return error;
+}
+
+/** Mix consecutive clips for a specified length and apply transition if specified.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param length the number of frames over which to create the mix
+ * \param transition the transition to use for the mix
+ * \return true if there was an error
+ */
+
+int mlt_playlist_mix( mlt_playlist this, int clip, int length, mlt_transition transition )
+{
+       int error = ( clip < 0 || clip + 1 >= this->count );
+       if ( error == 0 )
+       {
+               playlist_entry *clip_a = this->list[ clip ];
+               playlist_entry *clip_b = this->list[ clip + 1 ];
+               mlt_producer track_a = NULL;
+               mlt_producer track_b = NULL;
+               mlt_tractor tractor = mlt_tractor_new( );
+               mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+               // Check length is valid for both clips and resize if necessary.
+               int max_size = clip_a->frame_count > clip_b->frame_count ? clip_a->frame_count : clip_b->frame_count;
+               length = length > max_size ? max_size : length;
+
+               // Create the a and b tracks/cuts if necessary - note that no cuts are required if the length matches
+               if ( length != clip_a->frame_count )
+                       track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out - length + 1, clip_a->frame_out );
+               else
+                       track_a = clip_a->producer;
+
+               if ( length != clip_b->frame_count )
+                       track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in, clip_b->frame_in + length - 1 );
+               else
+                       track_b = clip_b->producer;
+
+               // Set the tracks on the tractor
+               mlt_tractor_set_track( tractor, track_a, 0 );
+               mlt_tractor_set_track( tractor, track_b, 1 );
+
+               // Insert the mix object into the playlist
+               mlt_playlist_insert( this, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 );
+               mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL );
+
+               // Attach the transition
+               if ( transition != NULL )
+               {
+                       mlt_field field = mlt_tractor_field( tractor );
+                       mlt_field_plant_transition( field, transition, 0, 1 );
+                       mlt_transition_set_in_and_out( transition, 0, length - 1 );
+               }
+
+               // Close our references to the tracks if we created new cuts above (the tracks can still be used here)
+               if ( track_a != clip_a->producer )
+                       mlt_producer_close( track_a );
+               if ( track_b != clip_b->producer )
+                       mlt_producer_close( track_b );
+
+               // Check if we have anything left on the right hand clip
+               if ( track_b == clip_b->producer )
+               {
+                       clip_b->preservation_hack = 1;
+                       mlt_playlist_remove( this, clip + 2 );
+               }
+               else if ( clip_b->frame_out - clip_b->frame_in > length )
+               {
+                       mlt_playlist_resize_clip( this, clip + 2, clip_b->frame_in + length, clip_b->frame_out );
+                       mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_b->producer ), "mix_in", tractor, 0, NULL, NULL );
+                       mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_out", clip_b->producer, 0, NULL, NULL );
+               }
+               else
+               {
+                       mlt_producer_clear( clip_b->producer );
+                       mlt_playlist_remove( this, clip + 2 );
+               }
+
+               // Check if we have anything left on the left hand clip
+               if ( track_a == clip_a->producer )
+               {
+                       clip_a->preservation_hack = 1;
+                       mlt_playlist_remove( this, clip );
+               }
+               else if ( clip_a->frame_out - clip_a->frame_in > length )
+               {
+                       mlt_playlist_resize_clip( this, clip, clip_a->frame_in, clip_a->frame_out - length );
+                       mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( clip_a->producer ), "mix_out", tractor, 0, NULL, NULL );
+                       mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mix_in", clip_a->producer, 0, NULL, NULL );
+               }
+               else
+               {
+                       mlt_producer_clear( clip_a->producer );
+                       mlt_playlist_remove( this, clip );
+               }
+
+               // Unblock and force a fire off of change events to listeners
+               mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+               mlt_playlist_virtual_refresh( this );
+               mlt_tractor_close( tractor );
+       }
+       return error;
+}
+
+/** Add a transition to an existing mix.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param transition a transition
+ * \return true if there was an error
+ */
+
+int mlt_playlist_mix_add( mlt_playlist this, int clip, mlt_transition transition )
+{
+       mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( this, clip ) );
+       mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
+       mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+       int error = transition == NULL || tractor == NULL;
+       if ( error == 0 )
+       {
+               mlt_field field = mlt_tractor_field( tractor );
+               mlt_field_plant_transition( field, transition, 0, 1 );
+               mlt_transition_set_in_and_out( transition, 0, this->list[ clip ]->frame_count - 1 );
+       }
+       return error;
+}
+
+/** Return the clip at the clip index.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of a playlist entry
+ * \return a producer or NULL if there was an error
+ */
+
+mlt_producer mlt_playlist_get_clip( mlt_playlist this, int clip )
+{
+       if ( clip >= 0 && clip < this->count )
+               return this->list[ clip ]->producer;
+       return NULL;
+}
+
+/** Return the clip at the specified position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the beginning of the playlist
+ * \return a producer or NULL if not found
+ */
+
+mlt_producer mlt_playlist_get_clip_at( mlt_playlist this, mlt_position position )
+{
+       int index = 0, total = 0;
+       return mlt_playlist_locate( this, &position, &index, &total );
+}
+
+/** Return the clip index of the specified position.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the beginning of the playlist
+ * \return the index of the playlist entry
+ */
+
+int mlt_playlist_get_clip_index_at( mlt_playlist this, mlt_position position )
+{
+       int index = 0, total = 0;
+       mlt_playlist_locate( this, &position, &index, &total );
+       return index;
+}
+
+/** Determine if the clip is a mix.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if the producer is a mix
+ */
+
+int mlt_playlist_clip_is_mix( mlt_playlist this, int clip )
+{
+       mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( this, clip ) );
+       mlt_properties properties = producer != NULL ? MLT_PRODUCER_PROPERTIES( producer ) : NULL;
+       mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+       return tractor != NULL;
+}
+
+/** Remove a mixed clip - ensure that the cuts included in the mix find their way
+ * back correctly on to the playlist.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_unmix( mlt_playlist this, int clip )
+{
+       int error = ( clip < 0 || clip >= this->count );
+
+       // Ensure that the clip request is actually a mix
+       if ( error == 0 )
+       {
+               mlt_producer producer = mlt_producer_cut_parent( this->list[ clip ]->producer );
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+               error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL ||
+                           this->list[ clip ]->preservation_hack;
+       }
+
+       if ( error == 0 )
+       {
+               playlist_entry *mix = this->list[ clip ];
+               mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
+               mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+               mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
+               mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
+               int length = mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
+               mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+               if ( clip_a != NULL )
+               {
+                       mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) + length );
+               }
+               else
+               {
+                       mlt_producer cut = mlt_tractor_get_track( tractor, 0 );
+                       mlt_playlist_insert( this, cut, clip, -1, -1 );
+                       clip ++;
+               }
+
+               if ( clip_b != NULL )
+               {
+                       mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) - length, mlt_producer_get_out( clip_b ) );
+               }
+               else
+               {
+                       mlt_producer cut = mlt_tractor_get_track( tractor, 1 );
+                       mlt_playlist_insert( this, cut, clip + 1, -1, -1 );
+               }
+
+               mlt_properties_set_data( properties, "mlt_mix", NULL, 0, NULL, NULL );
+               mlt_playlist_remove( this, clip );
+               mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+               mlt_playlist_virtual_refresh( this );
+       }
+       return error;
+}
+
+/** Resize a mix clip.
+ *
+ * \private \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param in the new starting point
+ * \param out the new ending point
+ * \return true if there was an error
+ */
+
+static int mlt_playlist_resize_mix( mlt_playlist this, int clip, int in, int out )
+{
+       int error = ( clip < 0 || clip >= this->count );
+
+       // Ensure that the clip request is actually a mix
+       if ( error == 0 )
+       {
+               mlt_producer producer = mlt_producer_cut_parent( this->list[ clip ]->producer );
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+               error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL;
+       }
+
+       if ( error == 0 )
+       {
+               playlist_entry *mix = this->list[ clip ];
+               mlt_tractor tractor = ( mlt_tractor )mlt_producer_cut_parent( mix->producer );
+               mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+               mlt_producer clip_a = mlt_properties_get_data( properties, "mix_in", NULL );
+               mlt_producer clip_b = mlt_properties_get_data( properties, "mix_out", NULL );
+               mlt_producer track_a = mlt_tractor_get_track( tractor, 0 );
+               mlt_producer track_b = mlt_tractor_get_track( tractor, 1 );
+               int length = out - in + 1;
+               int length_diff = length - mlt_producer_get_playtime( MLT_TRACTOR_PRODUCER( tractor ) );
+               mlt_events_block( MLT_PLAYLIST_PROPERTIES( this ), this );
+
+               if ( clip_a != NULL )
+                       mlt_producer_set_in_and_out( clip_a, mlt_producer_get_in( clip_a ), mlt_producer_get_out( clip_a ) - length_diff );
+
+               if ( clip_b != NULL )
+                       mlt_producer_set_in_and_out( clip_b, mlt_producer_get_in( clip_b ) + length_diff, mlt_producer_get_out( clip_b ) );
+
+               mlt_producer_set_in_and_out( track_a, mlt_producer_get_in( track_a ) - length_diff, mlt_producer_get_out( track_a ) );
+               mlt_producer_set_in_and_out( track_b, mlt_producer_get_in( track_b ), mlt_producer_get_out( track_b ) + length_diff );
+               mlt_producer_set_in_and_out( MLT_MULTITRACK_PRODUCER( mlt_tractor_multitrack( tractor ) ), in, out );
+               mlt_producer_set_in_and_out( MLT_TRACTOR_PRODUCER( tractor ), in, out );
+               mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( mix->producer ), "length", out - in + 1 );
+               mlt_producer_set_in_and_out( mix->producer, in, out );
+
+               mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( this ), this );
+               mlt_playlist_virtual_refresh( this );
+       }
+       return error;
+}
+
+/** Consolidate adjacent blank producers.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param keep_length set false to remove the last entry if it is blank
+ */
+
+void mlt_playlist_consolidate_blanks( mlt_playlist this, int keep_length )
+{
+       if ( this != NULL )
+       {
+               int i = 0;
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+
+               mlt_events_block( properties, properties );
+               for ( i = 1; i < this->count; i ++ )
+               {
+                       playlist_entry *left = this->list[ i - 1 ];
+                       playlist_entry *right = this->list[ i ];
+
+                       if ( mlt_producer_is_blank( left->producer ) && mlt_producer_is_blank( right->producer ) )
+                       {
+                               mlt_playlist_resize_clip( this, i - 1, 0, left->frame_count + right->frame_count - 1 );
+                               mlt_playlist_remove( this, i -- );
+                       }
+               }
+
+               if ( !keep_length && this->count > 0 )
+               {
+                       playlist_entry *last = this->list[ this->count - 1 ];
+                       if ( mlt_producer_is_blank( last->producer ) )
+                               mlt_playlist_remove( this, this->count - 1 );
+               }
+
+               mlt_events_unblock( properties, properties );
+               mlt_playlist_virtual_refresh( this );
+       }
+}
+
+/** Determine if the specified clip index is a blank.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return true if there was an error
+ */
+
+int mlt_playlist_is_blank( mlt_playlist this, int clip )
+{
+       return this == NULL || mlt_producer_is_blank( mlt_playlist_get_clip( this, clip ) );
+}
+
+/** Determine if the specified position is a blank.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position a time relative to the start or end (negative) of the playlist
+ * \return true if there was an error
+ */
+
+int mlt_playlist_is_blank_at( mlt_playlist this, mlt_position position )
+{
+       return this == NULL || mlt_producer_is_blank( mlt_playlist_get_clip_at( this, position ) );
+}
+
+/** Replace the specified clip with a blank and return the clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return a producer or NULL if there was an error
+ */
+
+mlt_producer mlt_playlist_replace_with_blank( mlt_playlist this, int clip )
+{
+       mlt_producer producer = NULL;
+       if ( !mlt_playlist_is_blank( this, clip ) )
+       {
+               playlist_entry *entry = this->list[ clip ];
+               int in = entry->frame_in;
+               int out = entry->frame_out;
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+               producer = entry->producer;
+               mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) );
+               mlt_events_block( properties, properties );
+               mlt_playlist_remove( this, clip );
+               mlt_playlist_blank( this, out - in );
+               mlt_playlist_move( this, this->count - 1, clip );
+               mlt_events_unblock( properties, properties );
+               mlt_playlist_virtual_refresh( this );
+               mlt_producer_set_in_and_out( producer, in, out );
+       }
+       return producer;
+}
+
+/** Insert blank space.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the new blank section
+ * \param length the ending time of the new blank section (duration - 1)
+ */
+
+void mlt_playlist_insert_blank( mlt_playlist this, int clip, int length )
+{
+       if ( this != NULL && length >= 0 )
+       {
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+               mlt_events_block( properties, properties );
+               mlt_playlist_blank( this, length );
+               mlt_playlist_move( this, this->count - 1, clip );
+               mlt_events_unblock( properties, properties );
+               mlt_playlist_virtual_refresh( this );
+       }
+}
+
+/** Resize a blank entry.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which the blank entry exists relative to the start or end (negative) of the playlist.
+ * \param length the additional amount of blank frames to add
+ * \param find true to fist locate the blank after the clip at position
+ */
+void mlt_playlist_pad_blanks( mlt_playlist this, mlt_position position, int length, int find )
+{
+       if ( this != NULL && length != 0 )
+       {
+               int clip = mlt_playlist_get_clip_index_at( this, position );
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+               mlt_events_block( properties, properties );
+               if ( find && clip < this->count && !mlt_playlist_is_blank( this, clip ) )
+                       clip ++;
+               if ( clip < this->count && mlt_playlist_is_blank( this, clip ) )
+               {
+                       mlt_playlist_clip_info info;
+                       mlt_playlist_get_clip_info( this, &info, clip );
+                       if ( info.frame_out + length > info.frame_in )
+                               mlt_playlist_resize_clip( this, clip, info.frame_in, info.frame_out + length );
+                       else
+                               mlt_playlist_remove( this, clip );
+               }
+               else if ( find && clip < this->count && length > 0  )
+               {
+                       mlt_playlist_insert_blank( this, clip, length );
+               }
+               mlt_events_unblock( properties, properties );
+               mlt_playlist_virtual_refresh( this );
+       }
+}
+
+/** Insert a clip at a specific time.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the time at which to insert
+ * \param producer the producer to insert
+ * \param mode true if you want to overwrite any blank section
+ * \return true if there was an error
+ */
+
+int mlt_playlist_insert_at( mlt_playlist this, mlt_position position, mlt_producer producer, int mode )
+{
+       int ret = this == NULL || position < 0 || producer == NULL;
+       if ( ret == 0 )
+       {
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+               int length = mlt_producer_get_playtime( producer );
+               int clip = mlt_playlist_get_clip_index_at( this, position );
+               mlt_playlist_clip_info info;
+               mlt_playlist_get_clip_info( this, &info, clip );
+               mlt_events_block( properties, this );
+               if ( clip < this->count && mlt_playlist_is_blank( this, clip ) )
+               {
+                       // Split and move to new clip if need be
+                       if ( position != info.start && mlt_playlist_split( this, clip, position - info.start - 1 ) == 0 )
+                               mlt_playlist_get_clip_info( this, &info, ++ clip );
+
+                       // Split again if need be
+                       if ( length < info.frame_count )
+                               mlt_playlist_split( this, clip, length - 1 );
+
+                       // Remove
+                       mlt_playlist_remove( this, clip );
+
+                       // Insert
+                       mlt_playlist_insert( this, producer, clip, -1, -1 );
+                       ret = clip;
+               }
+               else if ( clip < this->count )
+               {
+                       if ( position > info.start + info.frame_count / 2 )
+                               clip ++;
+                       if ( mode == 1 && clip < this->count && mlt_playlist_is_blank( this, clip ) )
+                       {
+                               mlt_playlist_get_clip_info( this, &info, clip );
+                               if ( length < info.frame_count )
+                                       mlt_playlist_split( this, clip, length );
+                               mlt_playlist_remove( this, clip );
+                       }
+                       mlt_playlist_insert( this, producer, clip, -1, -1 );
+                       ret = clip;
+               }
+               else
+               {
+                       if ( mode == 1 ) {
+                               if ( position == info.start )
+                                       mlt_playlist_remove( this, clip );
+                               else
+                                       mlt_playlist_blank( this, position - mlt_properties_get_int( properties, "length" ) - 1 );
+                       }
+                       mlt_playlist_append( this, producer );
+                       ret = this->count - 1;
+               }
+               mlt_events_unblock( properties, this );
+               mlt_playlist_virtual_refresh( this );
+       }
+       else
+       {
+               ret = -1;
+       }
+       return ret;
+}
+
+/** Get the time at which the clip starts relative to the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return the starting time
+ */
+
+int mlt_playlist_clip_start( mlt_playlist this, int clip )
+{
+       mlt_playlist_clip_info info;
+       if ( mlt_playlist_get_clip_info( this, &info, clip ) == 0 )
+               return info.start;
+       return clip < 0 ? 0 : mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+}
+
+/** Get the playable duration of the clip.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \return the duration of the playlist entry
+ */
+
+int mlt_playlist_clip_length( mlt_playlist this, int clip )
+{
+       mlt_playlist_clip_info info;
+       if ( mlt_playlist_get_clip_info( this, &info, clip ) == 0 )
+               return info.frame_count;
+       return 0;
+}
+
+/** Get the duration of a blank space.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param clip the index of the playlist entry
+ * \param bounded the maximum number of blank entries or 0 for all
+ * \return the duration of a blank section
+ */
+
+int mlt_playlist_blanks_from( mlt_playlist this, int clip, int bounded )
+{
+       int count = 0;
+       mlt_playlist_clip_info info;
+       if ( this != NULL && clip < this->count )
+       {
+               mlt_playlist_get_clip_info( this, &info, clip );
+               if ( mlt_playlist_is_blank( this, clip ) )
+                       count += info.frame_count;
+               if ( bounded == 0 )
+                       bounded = this->count;
+               for ( clip ++; clip < this->count && bounded >= 0; clip ++ )
+               {
+                       mlt_playlist_get_clip_info( this, &info, clip );
+                       if ( mlt_playlist_is_blank( this, clip ) )
+                               count += info.frame_count;
+                       else
+                               bounded --;
+               }
+       }
+       return count;
+}
+
+/** Remove a portion of the playlist by time.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ * \param position the starting time
+ * \param length the duration of time to remove
+ * \return the new entry index at the position
+ */
+
+int mlt_playlist_remove_region( mlt_playlist this, mlt_position position, int length )
+{
+       int index = mlt_playlist_get_clip_index_at( this, position );
+       if ( index >= 0 && index < this->count )
+       {
+               mlt_properties properties = MLT_PLAYLIST_PROPERTIES( this );
+               int clip_start = mlt_playlist_clip_start( this, index );
+               int clip_length = mlt_playlist_clip_length( this, index );
+               int list_length = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( this ) );
+               mlt_events_block( properties, this );
+
+               if ( position + length > list_length )
+                       length -= ( position + length - list_length );
+
+               if ( clip_start < position )
+               {
+                       mlt_playlist_split( this, index ++, position - clip_start );
+                       clip_length -= position - clip_start;
+               }
+
+               while( length > 0 )
+               {
+                       if ( mlt_playlist_clip_length( this, index ) > length )
+                               mlt_playlist_split( this, index, length );
+                       length -= mlt_playlist_clip_length( this, index );
+                       mlt_playlist_remove( this, index );
+               }
+
+               mlt_playlist_consolidate_blanks( this, 0 );
+               mlt_events_unblock( properties, this );
+               mlt_playlist_virtual_refresh( this );
+
+               // Just to be sure, we'll get the clip index again...
+               index = mlt_playlist_get_clip_index_at( this, position );
+       }
+       return index;
+}
+
+/** Not implemented
+ *
+ * \deprecated not implemented
+ * \public \memberof mlt_playlist_s
+ * \param this
+ * \param position
+ * \param length
+ * \param new_position
+ * \return
+ */
+
+int mlt_playlist_move_region( mlt_playlist this, mlt_position position, int length, int new_position )
+{
+       if ( this != NULL )
+       {
+       }
+       return 0;
+}
+
+/** Get the current frame.
+ *
+ * The implementation of the get_frame virtual function.
+ * \private \memberof mlt_playlist_s
+ * \param producer a producer
+ * \param frame a frame by reference
+ * \param index the time at which to get the frame
+ * \return false
+ */
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Check that we have a producer
+       if ( producer == NULL )
+       {
+               *frame = NULL;
+               return -1;
+       }
+
+       // Get this mlt_playlist
+       mlt_playlist this = producer->child;
+
+       // Need to ensure the frame is deinterlaced when repeating 1 frame
+       int progressive = 0;
+
+       // Get the real producer
+       mlt_service real = mlt_playlist_virtual_seek( this, &progressive );
+
+       // Check that we have a producer
+       if ( real == NULL )
+       {
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+               return 0;
+       }
+
+       // Get the frame
+       if ( !mlt_properties_get_int( MLT_SERVICE_PROPERTIES( real ), "meta.fx_cut" ) )
+       {
+               mlt_service_get_frame( real, frame, index );
+       }
+       else
+       {
+               mlt_producer parent = mlt_producer_cut_parent( ( mlt_producer )real );
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "fx_cut", 1 );
+               mlt_frame_push_service( *frame, NULL );
+               mlt_frame_push_audio( *frame, NULL );
+               mlt_service_apply_filters( MLT_PRODUCER_SERVICE( parent ), *frame, 0 );
+               mlt_service_apply_filters( real, *frame, 0 );
+               mlt_deque_pop_front( MLT_FRAME_IMAGE_STACK( *frame ) );
+               mlt_deque_pop_front( MLT_FRAME_AUDIO_STACK( *frame ) );
+       }
+
+       // Check if we're at the end of the clip
+       mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+       if ( mlt_properties_get_int( properties, "end_of_clip" ) )
+               mlt_playlist_virtual_set_out( this );
+
+       // Set the consumer progressive property
+       if ( progressive )
+       {
+               mlt_properties_set_int( properties, "consumer_deinterlace", progressive );
+               mlt_properties_set_int( properties, "test_audio", 1 );
+       }
+
+       // Check for notifier and call with appropriate argument
+       mlt_properties playlist_properties = MLT_PRODUCER_PROPERTIES( producer );
+       void ( *notifier )( void * ) = mlt_properties_get_data( playlist_properties, "notifier", NULL );
+       if ( notifier != NULL )
+       {
+               void *argument = mlt_properties_get_data( playlist_properties, "notifier_arg", NULL );
+               notifier( argument );
+       }
+
+       // Update position on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_frame( producer ) );
+
+       // Position ourselves on the next frame
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+/** Close the playlist.
+ *
+ * \public \memberof mlt_playlist_s
+ * \param this a playlist
+ */
+
+void mlt_playlist_close( mlt_playlist this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_PLAYLIST_PROPERTIES( this ) ) <= 0 )
+       {
+               int i = 0;
+               this->parent.close = NULL;
+               for ( i = 0; i < this->count; i ++ )
+               {
+                       mlt_event_close( this->list[ i ]->event );
+                       mlt_producer_close( this->list[ i ]->producer );
+                       free( this->list[ i ] );
+               }
+               mlt_producer_close( &this->blank );
+               mlt_producer_close( &this->parent );
+               free( this->list );
+               free( this );
+       }
+}
diff --git a/src/framework/mlt_playlist.h b/src/framework/mlt_playlist.h
new file mode 100644 (file)
index 0000000..a636431
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * \file mlt_playlist.h
+ * \brief playlist service class
+ * \see mlt_playlist_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_PLAYLIST_H_
+#define _MLT_PLAYLIST_H_
+
+#include "mlt_producer.h"
+
+/** \brief structure for returning clip information from a playlist entry
+ */
+
+typedef struct
+{
+       int clip;                 /**< the index of the clip within the playlist */
+       mlt_producer producer;    /**< the clip's producer (or parent producer of a cut) */
+       mlt_producer cut;         /**< the clips' cut producer */
+       mlt_position start;       /**< the time this begins relative to the beginning of the playlist */
+       char *resource;           /**< the file name or address of the clip */
+       mlt_position frame_in;    /**< the clip's in point */
+       mlt_position frame_out;   /**< the clip's out point */
+       mlt_position frame_count; /**< the duration of the clip */
+       mlt_position length;      /**< the unedited duration of the clip */
+       float fps;                /**< the frame rate of the clip */
+       int repeat;               /**< the number of times the clip is repeated */
+}
+mlt_playlist_clip_info;
+
+/** Playlist Entry
+*/
+
+typedef struct playlist_entry_s playlist_entry;
+
+/** \brief Playlist class
+ *
+ * A playlist is a sequential container of producers and blank spaces. The class provides all
+ * sorts of playlist assembly and manipulation routines. A playlist is also a producer within
+ * the framework.
+ *
+ * \extends mlt_producer_s
+ * \properties \em autoclose Set this true if you are doing sequential processing and want to
+ * automatically close producers as they are finished being used to free resources.
+ * \properties \em meta.fx_cut Set true on a producer to indicate that it is a "fx_cut,"
+ * which is a way to add filters as a playlist entry - useful only in a multitrack. See FxCut on the wiki.
+ * \properties \em mix_in
+ * \properties \em mix_out
+ */
+
+struct mlt_playlist_s
+{
+       struct mlt_producer_s parent;
+       struct mlt_producer_s blank;
+
+       int size;
+       int count;
+       playlist_entry **list;
+};
+
+#define MLT_PLAYLIST_PRODUCER( playlist )      ( &( playlist )->parent )
+#define MLT_PLAYLIST_SERVICE( playlist )       MLT_PRODUCER_SERVICE( MLT_PLAYLIST_PRODUCER( playlist ) )
+#define MLT_PLAYLIST_PROPERTIES( playlist )    MLT_SERVICE_PROPERTIES( MLT_PLAYLIST_SERVICE( playlist ) )
+
+extern mlt_playlist mlt_playlist_init( );
+extern mlt_producer mlt_playlist_producer( mlt_playlist self );
+extern mlt_service mlt_playlist_service( mlt_playlist self );
+extern mlt_properties mlt_playlist_properties( mlt_playlist self );
+extern int mlt_playlist_count( mlt_playlist self );
+extern int mlt_playlist_clear( mlt_playlist self );
+extern int mlt_playlist_append( mlt_playlist self, mlt_producer producer );
+extern int mlt_playlist_append_io( mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out );
+extern int mlt_playlist_blank( mlt_playlist self, mlt_position length );
+extern mlt_position mlt_playlist_clip( mlt_playlist self, mlt_whence whence, int index );
+extern int mlt_playlist_current_clip( mlt_playlist self );
+extern mlt_producer mlt_playlist_current( mlt_playlist self );
+extern int mlt_playlist_get_clip_info( mlt_playlist self, mlt_playlist_clip_info *info, int index );
+extern int mlt_playlist_insert( mlt_playlist self, mlt_producer producer, int where, mlt_position in, mlt_position out );
+extern int mlt_playlist_remove( mlt_playlist self, int where );
+extern int mlt_playlist_move( mlt_playlist self, int from, int to );
+extern int mlt_playlist_resize_clip( mlt_playlist self, int clip, mlt_position in, mlt_position out );
+extern int mlt_playlist_repeat_clip( mlt_playlist self, int clip, int repeat );
+extern int mlt_playlist_split( mlt_playlist self, int clip, mlt_position position );
+extern int mlt_playlist_split_at( mlt_playlist self, mlt_position position, int left );
+extern int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge );
+extern int mlt_playlist_mix( mlt_playlist self, int clip, int length, mlt_transition transition );
+extern int mlt_playlist_mix_add( mlt_playlist self, int clip, mlt_transition transition );
+extern mlt_producer mlt_playlist_get_clip( mlt_playlist self, int clip );
+extern mlt_producer mlt_playlist_get_clip_at( mlt_playlist self, mlt_position position );
+extern int mlt_playlist_get_clip_index_at( mlt_playlist self, mlt_position position );
+extern int mlt_playlist_clip_is_mix( mlt_playlist self, int clip );
+extern void mlt_playlist_consolidate_blanks( mlt_playlist self, int keep_length );
+extern int mlt_playlist_is_blank( mlt_playlist self, int clip );
+extern int mlt_playlist_is_blank_at( mlt_playlist self, mlt_position position );
+extern void mlt_playlist_insert_blank( mlt_playlist self, int clip, int length );
+extern void mlt_playlist_pad_blanks( mlt_playlist self, mlt_position position, int length, int find );
+extern mlt_producer mlt_playlist_replace_with_blank( mlt_playlist self, int clip );
+extern int mlt_playlist_insert_at( mlt_playlist self, mlt_position position, mlt_producer producer, int mode );
+extern int mlt_playlist_clip_start( mlt_playlist self, int clip );
+extern int mlt_playlist_clip_length( mlt_playlist self, int clip );
+extern int mlt_playlist_blanks_from( mlt_playlist self, int clip, int bounded );
+extern int mlt_playlist_remove_region( mlt_playlist self, mlt_position position, int length );
+extern int mlt_playlist_move_region( mlt_playlist self, mlt_position position, int length, int new_position );
+extern void mlt_playlist_close( mlt_playlist self );
+
+#endif
+
diff --git a/src/framework/mlt_pool.c b/src/framework/mlt_pool.c
new file mode 100644 (file)
index 0000000..ebf0081
--- /dev/null
@@ -0,0 +1,391 @@
+/**
+ * \file mlt_pool.c
+ * \brief memory pooling functionality
+ * \see mlt_pool_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_properties.h"
+#include "mlt_deque.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// Not nice - memalign is defined here apparently?
+#ifdef linux
+#include <malloc.h>
+#endif
+
+/** global singleton for tracking pools */
+
+static mlt_properties pools = NULL;
+
+/** \brief Pool (memory) class
+ */
+
+typedef struct mlt_pool_s
+{
+       pthread_mutex_t lock; ///< lock to prevent race conditions
+       mlt_deque stack;      ///< a stack of addresses to memory blocks
+       int size;             ///< the size of the memory block as a power of 2
+       int count;            ///< the number of blocks in the pool
+}
+*mlt_pool;
+
+/** \brief private to mlt_pool_s, for tracking items to release
+ */
+
+typedef struct mlt_release_s
+{
+       mlt_pool pool;
+       int references;
+}
+*mlt_release;
+
+/** Create a pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param size the size of the memory blocks to hold as some power of two
+ * \return a new pool object
+ */
+
+static mlt_pool pool_init( int size )
+{
+       // Create the pool
+       mlt_pool this = calloc( 1, sizeof( struct mlt_pool_s ) );
+
+       // Initialise it
+       if ( this != NULL )
+       {
+               // Initialise the mutex
+               pthread_mutex_init( &this->lock, NULL );
+
+               // Create the stack
+               this->stack = mlt_deque_init( );
+
+               // Assign the size
+               this->size = size;
+       }
+
+       // Return it
+       return this;
+}
+
+/** Get an item from the pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param this a pool
+ * \return an opaque pointer
+ */
+
+static void *pool_fetch( mlt_pool this )
+{
+       // We will generate a release object
+       void *ptr = NULL;
+
+       // Sanity check
+       if ( this != NULL )
+       {
+               // Lock the pool
+               pthread_mutex_lock( &this->lock );
+
+               // Check if the stack is empty
+               if ( mlt_deque_count( this->stack ) != 0 )
+               {
+                       // Pop the top of the stack
+                       ptr = mlt_deque_pop_back( this->stack );
+
+                       // Assign the reference
+                       ( ( mlt_release )ptr )->references = 1;
+               }
+               else
+               {
+                       // We need to generate a release item
+#ifdef linux
+                       mlt_release release = memalign( 16, this->size );
+#else
+                       mlt_release release = malloc( this->size );
+#endif
+
+                       // Initialise it
+                       if ( release != NULL )
+                       {
+                               // Increment the number of items allocated to this pool
+                               this->count ++;
+
+                               // Assign the pool
+                               release->pool = this;
+
+                               // Assign the reference
+                               release->references = 1;
+
+                               // Determine the ptr
+                               ptr = ( char * )release + sizeof( struct mlt_release_s );
+                       }
+               }
+
+               // Unlock the pool
+               pthread_mutex_unlock( &this->lock );
+       }
+
+       // Return the generated release object
+       return ptr;
+}
+
+/** Return an item to the pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param ptr an opaque pointer
+ */
+
+static void pool_return( void *ptr )
+{
+       // Sanity checks
+       if ( ptr != NULL )
+       {
+               // Get the release pointer
+               mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
+
+               // Get the pool
+               mlt_pool this = that->pool;
+
+               if ( this != NULL )
+               {
+                       // Lock the pool
+                       pthread_mutex_lock( &this->lock );
+
+                       // Push the that back back on to the stack
+                       mlt_deque_push_back( this->stack, ptr );
+
+                       // Unlock the pool
+                       pthread_mutex_unlock( &this->lock );
+
+                       // Ensure that we don't clean up
+                       ptr = NULL;
+               }
+       }
+
+       // Tidy up - this will only occur if the returned item is incorrect
+       if ( ptr != NULL )
+       {
+               // Free the release itself
+               free( ( char * )ptr - sizeof( struct mlt_release_s ) );
+       }
+}
+
+/** Destroy a pool.
+ *
+ * \private \memberof mlt_pool_s
+ * \param this a pool
+ */
+
+static void pool_close( mlt_pool this )
+{
+       if ( this != NULL )
+       {
+               // We need to free up all items in the pool
+               void *release = NULL;
+
+               // Iterate through the stack until depleted
+               while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
+               {
+                       // We'll free this item now
+                       free( ( char * )release - sizeof( struct mlt_release_s ) );
+               }
+
+               // We can now close the stack
+               mlt_deque_close( this->stack );
+
+               // Destroy the mutex
+               pthread_mutex_destroy( &this->lock );
+
+               // Close the pool
+               free( this );
+       }
+}
+
+/** Initialise the global pool.
+ *
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_init( )
+{
+       // Loop variable used to create the pools
+       int i = 0;
+
+       // Create the pools
+       pools = mlt_properties_new( );
+
+       // Create the pools
+       for ( i = 8; i < 31; i ++ )
+       {
+               // Each properties item needs a name
+               char name[ 32 ];
+
+               // Construct a pool
+               mlt_pool pool = pool_init( 1 << i );
+
+               // Generate a name
+               sprintf( name, "%d", i );
+
+               // Register with properties
+               mlt_properties_set_data( pools, name, pool, 0, ( mlt_destructor )pool_close, NULL );
+       }
+}
+
+/** Allocate size bytes from the pool.
+ *
+ * \public \memberof mlt_pool_s
+ * \param size the number of bytes
+ */
+
+void *mlt_pool_alloc( int size )
+{
+       // This will be used to obtain the pool to use
+       mlt_pool pool = NULL;
+
+       // Determines the index of the pool to use
+       int index = 8;
+
+       // Minimum size pooled is 256 bytes
+       size = size + sizeof( mlt_release );
+       while ( ( 1 << index ) < size )
+               index ++;
+
+       // Now get the pool at the index
+       pool = mlt_properties_get_data_at( pools, index - 8, NULL );
+
+       // Now get the real item
+       return pool_fetch( pool );
+}
+
+/** Allocate size bytes from the pool.
+ *
+ * \public \memberof mlt_pool_s
+ * \param ptr an opaque pointer - can be in the pool or a new block to allocate
+ * \param size the number of bytes
+ */
+
+void *mlt_pool_realloc( void *ptr, int size )
+{
+       // Result to return
+       void *result = NULL;
+
+       // Check if we actually have an address
+       if ( ptr != NULL )
+       {
+               // Get the release pointer
+               mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
+
+               // If the current pool this ptr belongs to is big enough
+               if ( size > that->pool->size - sizeof( struct mlt_release_s ) )
+               {
+                       // Allocate
+                       result = mlt_pool_alloc( size );
+
+                       // Copy
+                       memcpy( result, ptr, that->pool->size - sizeof( struct mlt_release_s ) );
+
+                       // Release
+                       mlt_pool_release( ptr );
+               }
+               else
+               {
+                       // Nothing to do
+                       result = ptr;
+               }
+       }
+       else
+       {
+               // Simply allocate
+               result = mlt_pool_alloc( size );
+       }
+
+       return result;
+}
+
+/** Purge unused items in the pool.
+ *
+ * A form of garbage collection.
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_purge( )
+{
+       int i = 0;
+
+       // For each pool
+       for ( i = 0; i < mlt_properties_count( pools ); i ++ )
+       {
+               // Get the pool
+               mlt_pool this = mlt_properties_get_data_at( pools, i, NULL );
+
+               // Pointer to unused memory
+               void *release = NULL;
+
+               // Lock the pool
+               pthread_mutex_lock( &this->lock );
+
+               // We'll free all unused items now
+               while ( ( release = mlt_deque_pop_back( this->stack ) ) != NULL )
+                       free( ( char * )release - sizeof( struct mlt_release_s ) );
+
+               // Unlock the pool
+               pthread_mutex_unlock( &this->lock );
+       }
+}
+
+/** Release the allocated memory.
+ *
+ * \public \memberof mlt_pool_s
+ * \param release an opaque pointer of a block in the pool
+ */
+
+void mlt_pool_release( void *release )
+{
+       // Return to the pool
+       pool_return( release );
+}
+
+/** Close the pool.
+ *
+ * \public \memberof mlt_pool_s
+ */
+
+void mlt_pool_close( )
+{
+#ifdef _MLT_POOL_CHECKS_
+       // Stats dump on close
+       int i = 0;
+       for ( i = 0; i < mlt_properties_count( pools ); i ++ )
+       {
+               mlt_pool pool = mlt_properties_get_data_at( pools, i, NULL );
+               if ( pool->count )
+                       mlt_log( NULL, MLT_LOG_DEBUG, "%s: size %d allocated %d returned %d %c\n", __FUNCTION__,
+                               pool->size, pool->count, mlt_deque_count( pool->stack ),
+                               pool->count !=  mlt_deque_count( pool->stack ) ? '*' : ' ' );
+       }
+#endif
+
+       // Close the properties
+       mlt_properties_close( pools );
+}
+
diff --git a/src/framework/mlt_pool.h b/src/framework/mlt_pool.h
new file mode 100644 (file)
index 0000000..caf983d
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * \file mlt_pool.h
+ * \brief memory pooling functionality
+ * \see mlt_pool_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_POOL_H
+#define _MLT_POOL_H
+
+extern void mlt_pool_init( );
+extern void *mlt_pool_alloc( int size );
+extern void *mlt_pool_realloc( void *ptr, int size );
+extern void mlt_pool_release( void *release );
+extern void mlt_pool_purge( );
+extern void mlt_pool_close( );
+
+#endif
diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c
new file mode 100644 (file)
index 0000000..f43738b
--- /dev/null
@@ -0,0 +1,1031 @@
+/**
+ * \file mlt_producer.c
+ * \brief abstraction for all producer services
+ * \see mlt_producer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_producer.h"
+#include "mlt_factory.h"
+#include "mlt_frame.h"
+#include "mlt_parser.h"
+#include "mlt_profile.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Forward references. */
+
+static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+static void mlt_producer_property_changed( mlt_service owner, mlt_producer this, char *name );
+static void mlt_producer_service_changed( mlt_service owner, mlt_producer this );
+
+/* for debugging */
+//#define _MLT_PRODUCER_CHECKS_ 1
+#ifdef _MLT_PRODUCER_CHECKS_
+static int producers_created = 0;
+static int producers_destroyed = 0;
+#endif
+
+/** Initialize a producer service.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this the producer structure to initialize
+ * \param child a pointer to the child object for the subclass
+ * \return true if there was an error
+ */
+
+int mlt_producer_init( mlt_producer this, void *child )
+{
+       // Check that we haven't received NULL
+       int error = this == NULL;
+
+       // Continue if no error
+       if ( error == 0 )
+       {
+#ifdef _MLT_PRODUCER_CHECKS_
+               producers_created ++;
+#endif
+
+               // Initialise the producer
+               memset( this, 0, sizeof( struct mlt_producer_s ) );
+
+               // Associate with the child
+               this->child = child;
+
+               // Initialise the service
+               if ( mlt_service_init( &this->parent, this ) == 0 )
+               {
+                       // The parent is the service
+                       mlt_service parent = &this->parent;
+
+                       // Define the parent close
+                       parent->close = ( mlt_destructor )mlt_producer_close;
+                       parent->close_object = this;
+
+                       // For convenience, we'll assume the close_object is this
+                       this->close_object = this;
+
+                       // Get the properties of the parent
+                       mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
+
+                       // Set the default properties
+                       mlt_properties_set( properties, "mlt_type", "mlt_producer" );
+                       mlt_properties_set_position( properties, "_position", 0.0 );
+                       mlt_properties_set_double( properties, "_frame", 0 );
+                       mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( NULL ) );
+                       mlt_properties_set_double( properties, "_speed", 1.0 );
+                       mlt_properties_set_position( properties, "in", 0 );
+                       mlt_properties_set_position( properties, "out", 14999 );
+                       mlt_properties_set_position( properties, "length", 15000 );
+                       mlt_properties_set( properties, "eof", "pause" );
+                       mlt_properties_set( properties, "resource", "<producer>" );
+
+                       // Override service get_frame
+                       parent->get_frame = producer_get_frame;
+
+                       mlt_events_listen( properties, this, "service-changed", ( mlt_listener )mlt_producer_service_changed );
+                       mlt_events_listen( properties, this, "property-changed", ( mlt_listener )mlt_producer_property_changed );
+                       mlt_events_register( properties, "producer-changed", NULL );
+               }
+       }
+
+       return error;
+}
+
+/** Listener for property changes.
+ *
+ * If the in, out, or length properties changed, fire a "producer-changed" event.
+ *
+ * \private \memberof mlt_producer_s
+ * \param owner a service (ignored)
+ * \param this the producer
+ * \param name the property that changed
+ */
+
+static void mlt_producer_property_changed( mlt_service owner, mlt_producer this, char *name )
+{
+       if ( !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "length" ) )
+               mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "producer-changed", NULL );
+}
+
+/** Listener for service changes.
+ *
+ * Fires the "producer-changed" event.
+ *
+ * \private \memberof mlt_producer_s
+ * \param owner a service (ignored)
+ * \param this the producer
+ */
+
+static void mlt_producer_service_changed( mlt_service owner, mlt_producer this )
+{
+       mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "producer-changed", NULL );
+}
+
+/** Create and initialize a new producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \return the new producer
+ */
+
+mlt_producer mlt_producer_new( )
+{
+       mlt_producer this = malloc( sizeof( struct mlt_producer_s ) );
+       mlt_producer_init( this, NULL );
+       return this;
+}
+
+/** Determine if producer is a cut.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if \p this is a "cut" producer
+ * \see mlt_producer_cut
+ */
+
+int mlt_producer_is_cut( mlt_producer this )
+{
+       return mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( this ), "_cut" );
+}
+
+/** Determine if producer is a mix.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if this is a "mix" producer
+ * \todo Define a mix producer.
+ */
+
+int mlt_producer_is_mix( mlt_producer this )
+{
+       mlt_properties properties = this != NULL ? MLT_PRODUCER_PROPERTIES( this ) : NULL;
+       mlt_tractor tractor = properties != NULL ? mlt_properties_get_data( properties, "mlt_mix", NULL ) : NULL;
+       return tractor != NULL;
+}
+
+/** Determine if the producer is a blank.
+ *
+ * Blank producers should only appear as an item in a playlist.
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if this is a "blank" producer
+ * \see mlt_playlist_insert_blank
+ */
+
+int mlt_producer_is_blank( mlt_producer this )
+{
+       return this == NULL || !strcmp( mlt_properties_get( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), "resource" ), "blank" );
+}
+
+/** Obtain the parent producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return either the parent producer if this is a "cut" producer or \p this otherwise.
+ */
+
+mlt_producer mlt_producer_cut_parent( mlt_producer this )
+{
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+       if ( mlt_producer_is_cut( this ) )
+               return mlt_properties_get_data( properties, "_cut_parent", NULL );
+       else
+               return this;
+}
+
+/** Create a cut of this producer.
+ *
+ * A "cut" is a portion of another (parent) producer.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param in the beginning
+ * \param out the end
+ * \return the new producer
+ * \todo Expand on the value of a cut.
+ */
+
+mlt_producer mlt_producer_cut( mlt_producer this, int in, int out )
+{
+       mlt_producer result = mlt_producer_new( );
+       mlt_producer parent = mlt_producer_cut_parent( this );
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( result );
+       mlt_properties parent_props = MLT_PRODUCER_PROPERTIES( parent );
+
+       mlt_events_block( MLT_PRODUCER_PROPERTIES( result ), MLT_PRODUCER_PROPERTIES( result ) );
+       // Special case - allow for a cut of the entire producer (this will squeeze all other cuts to 0)
+       if ( in <= 0 )
+               in = 0;
+       if ( ( out < 0 || out >= mlt_producer_get_length( parent ) ) && !mlt_producer_is_blank( this ) )
+               out = mlt_producer_get_length( parent ) - 1;
+
+       mlt_properties_inc_ref( parent_props );
+       mlt_properties_set_int( properties, "_cut", 1 );
+       mlt_properties_set_data( properties, "_cut_parent", parent, 0, ( mlt_destructor )mlt_producer_close, NULL );
+       mlt_properties_set_position( properties, "length", mlt_properties_get_position( parent_props, "length" ) );
+       mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( parent_props, "aspect_ratio" ) );
+       mlt_producer_set_in_and_out( result, in, out );
+
+       return result;
+}
+
+/** Get the parent service object.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the service parent class
+ * \see MLT_PRODUCER_SERVICE
+ */
+
+mlt_service mlt_producer_service( mlt_producer this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the producer properties.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the producer's property list
+ * \see MLT_PRODUCER_PROPERTIES
+ */
+
+mlt_properties mlt_producer_properties( mlt_producer this )
+{
+       return MLT_SERVICE_PROPERTIES( &this->parent );
+}
+
+/** Seek to a specified position.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param position set the "play head" position of the producer
+ * \return false
+ * \todo Document how the properties affect behavior.
+ */
+
+int mlt_producer_seek( mlt_producer this, mlt_position position )
+{
+       // Determine eof handling
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+       char *eof = mlt_properties_get( properties, "eof" );
+       int use_points = 1 - mlt_properties_get_int( properties, "ignore_points" );
+
+       // Recursive behaviour for cuts - repositions parent and then repositions cut
+       // hence no return on this condition
+       if ( mlt_producer_is_cut( this ) )
+               mlt_producer_seek( mlt_producer_cut_parent( this ), position + mlt_producer_get_in( this ) );
+
+       // Check bounds
+       if ( position < 0 || mlt_producer_get_playtime( this ) == 0 )
+       {
+               position = 0;
+       }
+       else if ( use_points && ( eof == NULL || !strcmp( eof, "pause" ) ) && position >= mlt_producer_get_playtime( this ) )
+       {
+               mlt_producer_set_speed( this, 0 );
+               position = mlt_producer_get_playtime( this ) - 1;
+       }
+       else if ( use_points && !strcmp( eof, "loop" ) && position >= mlt_producer_get_playtime( this ) )
+       {
+               position = (int)position % (int)mlt_producer_get_playtime( this );
+       }
+
+       // Set the position
+       mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "_position", position );
+
+       // Calculate the absolute frame
+       mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "_frame", use_points * mlt_producer_get_in( this ) + position );
+
+       return 0;
+}
+
+/** Get the current position (relative to in point).
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the position of the "play head" relative to its beginning
+ */
+
+mlt_position mlt_producer_position( mlt_producer this )
+{
+       return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "_position" );
+}
+
+/** Get the current position (relative to start of producer).
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the position of the "play head" regardless of the in point
+ */
+
+mlt_position mlt_producer_frame( mlt_producer this )
+{
+       return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "_frame" );
+}
+
+/** Set the playing speed.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param speed the new speed as a relative factor (1.0 = normal)
+ * \return
+ */
+
+int mlt_producer_set_speed( mlt_producer this, double speed )
+{
+       return mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( this ), "_speed", speed );
+}
+
+/** Get the playing speed.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the speed as a relative factor (1.0 = normal)
+ */
+
+double mlt_producer_get_speed( mlt_producer this )
+{
+       return mlt_properties_get_double( MLT_PRODUCER_PROPERTIES( this ), "_speed" );
+}
+
+/** Get the frames per second.
+ *
+ * This is determined by the producer's profile.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the video refresh rate
+ */
+
+double mlt_producer_get_fps( mlt_producer this )
+{
+       mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+       return mlt_profile_fps( profile );
+}
+
+/** Set the in and out points.
+ *
+ * The in point is where play out should start relative to the natural start
+ * of the underlying file. The out point is where play out should end, also
+ * relative to the start of the underlying file. If the underlying resource is
+ * a live stream, then the in point is an offset relative to first usable
+ * sample.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param in the relative starting time
+ * \param out the relative ending time
+ * \return false
+ */
+
+int mlt_producer_set_in_and_out( mlt_producer this, mlt_position in, mlt_position out )
+{
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Correct ins and outs if necessary
+       if ( in < 0 )
+               in = 0;
+       else if ( in >= mlt_producer_get_length( this ) )
+               in = mlt_producer_get_length( this ) - 1;
+
+       if ( out < 0 )
+               out = 0;
+       else if ( out >= mlt_producer_get_length( this ) && !mlt_producer_is_blank( this ) )
+               out = mlt_producer_get_length( this ) - 1;
+       else if ( out >= mlt_producer_get_length( this ) && mlt_producer_is_blank( this ) )
+               mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( this ), "length", out + 1 );
+
+       // Swap ins and outs if wrong
+       if ( out < in )
+       {
+               mlt_position t = in;
+               in = out;
+               out = t;
+       }
+
+       // Set the values
+       mlt_events_block( properties, properties );
+       mlt_properties_set_position( properties, "in", in );
+       mlt_events_unblock( properties, properties );
+       mlt_properties_set_position( properties, "out", out );
+
+       return 0;
+}
+
+/** Physically reduce the producer (typically a cut) to a 0 length.
+ *  Essentially, all 0 length cuts should be immediately removed by containers.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return false
+ */
+
+int mlt_producer_clear( mlt_producer this )
+{
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+               mlt_events_block( properties, properties );
+               mlt_properties_set_position( properties, "in", 0 );
+               mlt_events_unblock( properties, properties );
+               mlt_properties_set_position( properties, "out", -1 );
+       }
+       return 0;
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the in point
+ */
+
+mlt_position mlt_producer_get_in( mlt_producer this )
+{
+       return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the out point
+ */
+
+mlt_position mlt_producer_get_out( mlt_producer this )
+{
+       return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "out" );
+}
+
+/** Get the total play time.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the playable (based on in and out points) duration
+ */
+
+mlt_position mlt_producer_get_playtime( mlt_producer this )
+{
+       return mlt_producer_get_out( this ) - mlt_producer_get_in( this ) + 1;
+}
+
+/** Get the total, unedited length of the producer.
+ *
+ * The value returned by a live streaming producer is unknown.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return the duration of the producer regardless of in and out points
+ */
+
+mlt_position mlt_producer_get_length( mlt_producer this )
+{
+       return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( this ), "length" );
+}
+
+/** Prepare for next frame.
+ *
+ * Advance the play out position. If the speed is less than zero, it will
+ * move the play out position in the reverse direction.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ */
+
+void mlt_producer_prepare_next( mlt_producer this )
+{
+       if ( mlt_producer_get_speed( this ) != 0 )
+               mlt_producer_seek( this, mlt_producer_position( this ) + mlt_producer_get_speed( this ) );
+}
+
+/** Get a frame.
+ *
+ * This is the implementation of the \p get_frame virtual function.
+ * It requests a new frame object from the actual producer for the current
+ * play out position. The producer and its filters can add information and
+ * operations to the frame object in their get_frame handlers.
+ *
+ * \private \memberof mlt_producer_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the actual producer
+ * \return true if there was an error
+ * \todo Learn more about the details and document how certain properties affect
+ * its behavior.
+ */
+
+static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+       int result = 1;
+       mlt_producer this = service != NULL ? service->child : NULL;
+
+       if ( this != NULL && !mlt_producer_is_cut( this ) )
+       {
+               // Get the properties of this producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+               // Determine eof handling
+               char *eof = mlt_properties_get( MLT_PRODUCER_PROPERTIES( this ), "eof" );
+
+               // Get the speed of the producer
+               double speed = mlt_producer_get_speed( this );
+
+               // We need to use the clone if it's specified
+               mlt_producer clone = mlt_properties_get_data( properties, "use_clone", NULL );
+
+               // If no clone is specified, use this
+               clone = clone == NULL ? this : clone;
+
+               // A properly instatiated producer will have a get_frame method...
+               if ( this->get_frame == NULL || ( !strcmp( eof, "continue" ) && mlt_producer_position( this ) > mlt_producer_get_out( this ) ) )
+               {
+                       // Generate a test frame
+                       *frame = mlt_frame_init( service );
+
+                       // Set the position
+                       result = mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+                       // Mark as a test card
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 1 );
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", 1 );
+
+                       // Calculate the next position
+                       mlt_producer_prepare_next( this );
+               }
+               else
+               {
+                       // Get the frame from the implementation
+                       result = this->get_frame( clone, frame, index );
+               }
+
+               // Copy the fps and speed of the producer onto the frame
+               properties = MLT_FRAME_PROPERTIES( *frame );
+               mlt_properties_set_double( properties, "_speed", speed );
+               mlt_properties_set_int( properties, "test_audio", mlt_frame_is_test_audio( *frame ) );
+               mlt_properties_set_int( properties, "test_image", mlt_frame_is_test_card( *frame ) );
+               if ( mlt_properties_get_data( properties, "_producer", NULL ) == NULL )
+                       mlt_properties_set_data( properties, "_producer", service, 0, NULL, NULL );
+       }
+       else if ( this != NULL )
+       {
+               // Get the speed of the cut
+               double speed = mlt_producer_get_speed( this );
+
+               // Get the parent of this cut
+               mlt_producer parent = mlt_producer_cut_parent( this );
+
+               // Get the properties of the parent
+               mlt_properties parent_properties = MLT_PRODUCER_PROPERTIES( parent );
+
+               // Get the properties of the cut
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+               // Determine the clone index
+               int clone_index = mlt_properties_get_int( properties, "_clone" );
+
+               // Determine the clone to use
+               mlt_producer clone = this;
+
+               if ( clone_index > 0 )
+               {
+                       char key[ 25 ];
+                       sprintf( key, "_clone.%d", clone_index - 1 );
+                       clone = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( this ) ), key, NULL );
+                       if ( clone == NULL ) mlt_log( service, MLT_LOG_ERROR, "requested clone doesn't exist %d\n", clone_index );
+                       clone = clone == NULL ? this : clone;
+               }
+               else
+               {
+                       clone = parent;
+               }
+
+               // We need to seek to the correct position in the clone
+               mlt_producer_seek( clone, mlt_producer_get_in( this ) + mlt_properties_get_int( properties, "_position" ) );
+
+               // Assign the clone property to the parent
+               mlt_properties_set_data( parent_properties, "use_clone", clone, 0, NULL, NULL );
+
+               // Now get the frame from the parents service
+               result = mlt_service_get_frame( MLT_PRODUCER_SERVICE( parent ), frame, index );
+
+               // We're done with the clone now
+               mlt_properties_set_data( parent_properties, "use_clone", NULL, 0, NULL, NULL );
+
+               // This is useful and required by always_active transitions to determine in/out points of the cut
+               if ( mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", NULL ) == MLT_PRODUCER_SERVICE( parent ) )
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", this, 0, NULL, NULL );
+
+               mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "_speed", speed );
+               mlt_producer_prepare_next( this );
+       }
+       else
+       {
+               *frame = mlt_frame_init( service );
+               result = 0;
+       }
+
+       // Pass on all meta properties from the producer/cut on to the frame
+       if ( *frame != NULL && this != NULL )
+       {
+               int i = 0;
+               mlt_properties p_props = MLT_PRODUCER_PROPERTIES( this );
+               mlt_properties f_props = MLT_FRAME_PROPERTIES( *frame );
+               int count = mlt_properties_count( p_props );
+               for ( i = 0; i < count; i ++ )
+               {
+                       char *name = mlt_properties_get_name( p_props, i );
+                       if ( !strncmp( name, "meta.", 5 ) )
+                               mlt_properties_set( f_props, name, mlt_properties_get( p_props, name ) );
+                       else if ( !strncmp( name, "set.", 4 ) )
+                               mlt_properties_set( f_props, name + 4, mlt_properties_get( p_props, name ) );
+               }
+       }
+
+       return result;
+}
+
+/** Attach a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \param filter the filter to attach
+ * \return true if there was an error
+ */
+
+int mlt_producer_attach( mlt_producer this, mlt_filter filter )
+{
+       return mlt_service_attach( MLT_PRODUCER_SERVICE( this ), filter );
+}
+
+/** Detach a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a service
+ * \param filter the filter to detach
+ * \return true if there was an error
+ */
+
+int mlt_producer_detach( mlt_producer this, mlt_filter filter )
+{
+       return mlt_service_detach( MLT_PRODUCER_SERVICE( this ), filter );
+}
+
+/** Retrieve a filter.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a service
+ * \param index which filter to retrieve
+ * \return the filter or null if there was an error
+ */
+
+mlt_filter mlt_producer_filter( mlt_producer this, int index )
+{
+       return mlt_service_filter( MLT_PRODUCER_SERVICE( this ), index );
+}
+
+/** Clone this producer.
+ *
+ * \private \memberof mlt_producer_s
+ * \param this a producer
+ * \return a new producer that is a copy of \p this
+ * \see mlt_producer_set_clones
+ */
+
+static mlt_producer mlt_producer_clone( mlt_producer this )
+{
+       mlt_producer clone = NULL;
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+       char *resource = mlt_properties_get( properties, "resource" );
+       char *service = mlt_properties_get( properties, "mlt_service" );
+       mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+
+       mlt_events_block( mlt_factory_event_object( ), mlt_factory_event_object( ) );
+
+       if ( service != NULL )
+               clone = mlt_factory_producer( profile, service, resource );
+
+       if ( clone == NULL && resource != NULL )
+               clone = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), resource );
+
+       if ( clone != NULL )
+               mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( clone ), properties );
+
+       mlt_events_unblock( mlt_factory_event_object( ), mlt_factory_event_object( ) );
+
+       return clone;
+}
+
+/** Create clones.
+ *
+ * \private \memberof mlt_producer_s
+ * \param this a producer
+ * \param clones the number of copies to make
+ * \see mlt_producer_optimise
+ */
+
+static void mlt_producer_set_clones( mlt_producer this, int clones )
+{
+       mlt_producer parent = mlt_producer_cut_parent( this );
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent );
+       int existing = mlt_properties_get_int( properties, "_clones" );
+       int i = 0;
+       char key[ 25 ];
+
+       // If the number of existing clones is different, then create/remove as necessary
+       if ( existing != clones )
+       {
+               if ( existing < clones )
+               {
+                       for ( i = existing; i < clones; i ++ )
+                       {
+                               mlt_producer clone = mlt_producer_clone( parent );
+                               sprintf( key, "_clone.%d", i );
+                               mlt_properties_set_data( properties, key, clone, 0, ( mlt_destructor )mlt_producer_close, NULL );
+                       }
+               }
+               else
+               {
+                       for ( i = clones; i < existing; i ++ )
+                       {
+                               sprintf( key, "_clone.%d", i );
+                               mlt_properties_set_data( properties, key, NULL, 0, NULL, NULL );
+                       }
+               }
+       }
+
+       // Ensure all properties on the parent are passed to the clones
+       for ( i = 0; i < clones; i ++ )
+       {
+               mlt_producer clone = NULL;
+               sprintf( key, "_clone.%d", i );
+               clone = mlt_properties_get_data( properties, key, NULL );
+               if ( clone != NULL )
+                       mlt_properties_pass( MLT_PRODUCER_PROPERTIES( clone ), properties, "" );
+       }
+
+       // Update the number of clones on the properties
+       mlt_properties_set_int( properties, "_clones", clones );
+}
+
+/** \brief private to mlt_producer_s, used by mlt_producer_optimise() */
+
+typedef struct
+{
+       int multitrack;
+       int track;
+       int position;
+       int length;
+       int offset;
+}
+track_info;
+
+/** \brief private to mlt_producer_s, used by mlt_producer_optimise() */
+
+typedef struct
+{
+       mlt_producer cut;
+       int start;
+       int end;
+}
+clip_references;
+
+static int intersect( clip_references *a, clip_references *b )
+{
+       int diff = ( a->start - b->start ) + ( a->end - b->end );
+       return diff >= 0 && diff < ( a->end - a->start + 1 );
+}
+
+static int push( mlt_parser this, int multitrack, int track, int position )
+{
+       mlt_properties properties = mlt_parser_properties( this );
+       mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+       track_info *info = malloc( sizeof( track_info ) );
+       info->multitrack = multitrack;
+       info->track = track;
+       info->position = position;
+       info->length = 0;
+       info->offset = 0;
+       return mlt_deque_push_back( stack, info );
+}
+
+static track_info *pop( mlt_parser this )
+{
+       mlt_properties properties = mlt_parser_properties( this );
+       mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+       return mlt_deque_pop_back( stack );
+}
+
+static track_info *peek( mlt_parser this )
+{
+       mlt_properties properties = mlt_parser_properties( this );
+       mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL );
+       return mlt_deque_peek_back( stack );
+}
+
+static int on_start_multitrack( mlt_parser this, mlt_multitrack object )
+{
+       track_info *info = peek( this );
+       return push( this, info->multitrack ++, info->track, info->position );
+}
+
+static int on_start_track( mlt_parser this )
+{
+       track_info *info = peek( this );
+       info->position -= info->offset;
+       info->length -= info->offset;
+       return push( this, info->multitrack, info->track ++, info->position );
+}
+
+static int on_start_producer( mlt_parser this, mlt_producer object )
+{
+       mlt_properties properties = mlt_parser_properties( this );
+       mlt_properties producers = mlt_properties_get_data( properties, "producers", NULL );
+       mlt_producer parent = mlt_producer_cut_parent( object );
+       if ( mlt_service_identify( ( mlt_service )mlt_producer_cut_parent( object ) ) == producer_type && mlt_producer_is_cut( object ) )
+       {
+               int ref_count = 0;
+               clip_references *old_refs = NULL;
+               clip_references *refs = NULL;
+               char key[ 50 ];
+               int count = 0;
+               track_info *info = peek( this );
+               sprintf( key, "%p", parent );
+               mlt_properties_get_data( producers, key, &count );
+               mlt_properties_set_data( producers, key, parent, ++ count, NULL, NULL );
+               old_refs = mlt_properties_get_data( properties, key, &ref_count );
+               refs = malloc( ( ref_count + 1 ) * sizeof( clip_references ) );
+               if ( old_refs != NULL )
+                       memcpy( refs, old_refs, ref_count * sizeof( clip_references ) );
+               mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( object ), "_clone", -1 );
+               refs[ ref_count ].cut = object;
+               refs[ ref_count ].start = info->position;
+               refs[ ref_count ].end = info->position + mlt_producer_get_playtime( object ) - 1;
+               mlt_properties_set_data( properties, key, refs, ++ ref_count, free, NULL );
+               info->position += mlt_producer_get_playtime( object );
+               info->length += mlt_producer_get_playtime( object );
+       }
+       return 0;
+}
+
+static int on_end_track( mlt_parser this )
+{
+       track_info *track = pop( this );
+       track_info *multi = peek( this );
+       multi->length += track->length;
+       multi->position += track->length;
+       multi->offset = track->length;
+       free( track );
+       return 0;
+}
+
+static int on_end_multitrack( mlt_parser this, mlt_multitrack object )
+{
+       track_info *multi = pop( this );
+       track_info *track = peek( this );
+       track->position += multi->length;
+       track->length += multi->length;
+       free( multi );
+       return 0;
+}
+
+/** Optimise for overlapping cuts from the same clip.
+ *
+ * \todo learn more about this
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ * \return true if there was an error
+ */
+
+int mlt_producer_optimise( mlt_producer this )
+{
+       int error = 1;
+       mlt_parser parser = mlt_parser_new( );
+       if ( parser != NULL )
+       {
+               int i = 0, j = 0, k = 0;
+               mlt_properties properties = mlt_parser_properties( parser );
+               mlt_properties producers = mlt_properties_new( );
+               mlt_deque stack = mlt_deque_init( );
+               mlt_properties_set_data( properties, "producers", producers, 0, ( mlt_destructor )mlt_properties_close, NULL );
+               mlt_properties_set_data( properties, "stack", stack, 0, ( mlt_destructor )mlt_deque_close, NULL );
+               parser->on_start_producer = on_start_producer;
+               parser->on_start_track = on_start_track;
+               parser->on_end_track = on_end_track;
+               parser->on_start_multitrack = on_start_multitrack;
+               parser->on_end_multitrack = on_end_multitrack;
+               push( parser, 0, 0, 0 );
+               mlt_parser_start( parser, MLT_PRODUCER_SERVICE( this ) );
+               free( pop( parser ) );
+               for ( k = 0; k < mlt_properties_count( producers ); k ++ )
+               {
+                       char *name = mlt_properties_get_name( producers, k );
+                       int count = 0;
+                       int clones = 0;
+                       int max_clones = 0;
+                       mlt_producer producer = mlt_properties_get_data( producers, name, &count );
+                       if ( producer != NULL && count > 1 )
+                       {
+                               clip_references *refs = mlt_properties_get_data( properties, name, &count );
+                               for ( i = 0; i < count; i ++ )
+                               {
+                                       clones = 0;
+                                       for ( j = i + 1; j < count; j ++ )
+                                       {
+                                               if ( intersect( &refs[ i ], &refs[ j ] ) )
+                                               {
+                                                       clones ++;
+                                                       mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( refs[ j ].cut ), "_clone", clones );
+                                               }
+                                       }
+                                       if ( clones > max_clones )
+                                               max_clones = clones;
+                               }
+
+                               for ( i = 0; i < count; i ++ )
+                               {
+                                       mlt_producer cut = refs[ i ].cut;
+                                       if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone" ) == -1 )
+                                               mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone", 0 );
+                               }
+
+                               mlt_producer_set_clones( producer, max_clones );
+                       }
+                       else if ( producer != NULL )
+                       {
+                               clip_references *refs = mlt_properties_get_data( properties, name, &count );
+                               for ( i = 0; i < count; i ++ )
+                               {
+                                       mlt_producer cut = refs[ i ].cut;
+                                       mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( cut ), "_clone", 0 );
+                               }
+                               mlt_producer_set_clones( producer, 0 );
+                       }
+               }
+               mlt_parser_close( parser );
+       }
+       return error;
+}
+
+/** Close the producer.
+ *
+ * Destroys the producer and deallocates its resources managed by its
+ * properties list. This will call the close virtual function. Therefore, a
+ * subclass that defines its own close function should set its virtual close
+ * function to NULL prior to calling this to avoid circular calls.
+ *
+ * \public \memberof mlt_producer_s
+ * \param this a producer
+ */
+
+void mlt_producer_close( mlt_producer this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_PRODUCER_PROPERTIES( this ) ) <= 0 )
+       {
+               this->parent.close = NULL;
+
+               if ( this->close != NULL )
+               {
+                       this->close( this->close_object );
+               }
+               else
+               {
+                       int destroy = mlt_producer_is_cut( this );
+
+#if _MLT_PRODUCER_CHECKS_ == 1
+                       // Show debug info
+                       mlt_properties_debug( MLT_PRODUCER_PROPERTIES( this ), "Producer closing", stderr );
+#endif
+
+#ifdef _MLT_PRODUCER_CHECKS_
+                       // Show current stats - these should match when the app is closed
+                       mlt_log( MLT_PRODUCER_SERVICE( this ), MLT_LOG_DEBUG, "Producers created %d, destroyed %d\n", producers_created, ++producers_destroyed );
+#endif
+
+                       mlt_service_close( &this->parent );
+
+                       if ( destroy )
+                               free( this );
+               }
+       }
+}
diff --git a/src/framework/mlt_producer.h b/src/framework/mlt_producer.h
new file mode 100644 (file)
index 0000000..37ba2ba
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * \file mlt_producer.h
+ * \brief abstraction for all producer services
+ * \see mlt_producer_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_PRODUCER_H_
+#define _MLT_PRODUCER_H_
+
+#include "mlt_service.h"
+#include "mlt_filter.h"
+
+/** \brief Producer abstract service class
+ *
+ * A producer is a service that generates audio, video, and metadata.
+ * Some day it may also generate text (subtitles). This is not to say
+ * a producer "synthesizes," rather that is an origin of data within the
+ * service network - that could be through synthesis or reading a stream.
+ *
+ * \extends mlt_service
+ * \event \em producer-changed
+ * \properties \em mlt_type the name of the service subclass, e.g. mlt_producer
+ * \properties \em mlt_service the name of a producer subclass
+ * \properties \em _position the current position of the play head, relative to the in point
+ * \properties \em _frame the current position of the play head, relative to the beginning of the resource
+ * \properties \em _speed the current speed factor, where 1.0 is normal
+ * \properties \em aspect_ratio sample aspect ratio
+ * \properties \em length the duration of the cut in frames
+ * \properties \em eof the end-of-file behavior, one of: pause, continue, loop
+ * \properties \em resource the file name, stream address, or the class name in angle brackets
+ * \properties \em _cut set if this producer is a "cut" producer
+ * \properties \em mlt_mix stores the data for a "mix" producer
+ * \properties \em _cut_parent holds a reference to the cut's parent producer
+ * \properties \em ignore_points Set this to temporarily disable the in and out points.
+ * \properties \em use_clone holds a reference to a clone's producer, as created by mlt_producer_optimise
+ * \properties \em _clone is the index of the clone in the list of clones stored on the clone's producer
+ * \properties \em _clones is the number of clones of the producer, as created by mlt_producer_optimise
+ * \properties \em _clone.{N} holds a reference to the N'th clone of the producer, as created by mlt_producer_optimise
+ * \properties \em meta.* holds metadata - there is a loose taxonomy to be defined
+ * \properties \em set.* holds properties to set on a frame produced
+ * \todo define the media metadata taxonomy
+ */
+
+struct mlt_producer_s
+{
+       /** A producer is a service. */
+       struct mlt_service_s parent;
+
+       /** Get a frame of data (virtual function).
+        *
+        * \param mlt_producer a producer
+        * \param mlt_frame_ptr a frame pointer by reference
+        * \param int an index
+        * \return true if there was an error
+        */
+       int ( *get_frame )( mlt_producer, mlt_frame_ptr, int );
+
+       /** the destructor virtual function */
+       mlt_destructor close;
+       void *close_object; /**< the object supplied to the close virtual function */
+
+       void *local; /**< \private instance object */
+       void *child; /**< \private the object of a subclass */
+};
+
+/*
+ *  Public final methods
+ */
+
+#define MLT_PRODUCER_SERVICE( producer )       ( &( producer )->parent )
+#define MLT_PRODUCER_PROPERTIES( producer )    MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) )
+
+extern int mlt_producer_init( mlt_producer self, void *child );
+extern mlt_producer mlt_producer_new( );
+extern mlt_service mlt_producer_service( mlt_producer self );
+extern mlt_properties mlt_producer_properties( mlt_producer self );
+extern int mlt_producer_seek( mlt_producer self, mlt_position position );
+extern mlt_position mlt_producer_position( mlt_producer self );
+extern mlt_position mlt_producer_frame( mlt_producer self );
+extern int mlt_producer_set_speed( mlt_producer self, double speed );
+extern double mlt_producer_get_speed( mlt_producer self );
+extern double mlt_producer_get_fps( mlt_producer self );
+extern int mlt_producer_set_in_and_out( mlt_producer self, mlt_position in, mlt_position out );
+extern int mlt_producer_clear( mlt_producer self );
+extern mlt_position mlt_producer_get_in( mlt_producer self );
+extern mlt_position mlt_producer_get_out( mlt_producer self );
+extern mlt_position mlt_producer_get_playtime( mlt_producer self );
+extern mlt_position mlt_producer_get_length( mlt_producer self );
+extern void mlt_producer_prepare_next( mlt_producer self );
+extern int mlt_producer_attach( mlt_producer self, mlt_filter filter );
+extern int mlt_producer_detach( mlt_producer self, mlt_filter filter );
+extern mlt_filter mlt_producer_filter( mlt_producer self, int index );
+extern mlt_producer mlt_producer_cut( mlt_producer self, int in, int out );
+extern int mlt_producer_is_cut( mlt_producer self );
+extern int mlt_producer_is_mix( mlt_producer self );
+extern int mlt_producer_is_blank( mlt_producer self );
+extern mlt_producer mlt_producer_cut_parent( mlt_producer self );
+extern int mlt_producer_optimise( mlt_producer self );
+extern void mlt_producer_close( mlt_producer self );
+
+#endif
diff --git a/src/framework/mlt_profile.c b/src/framework/mlt_profile.c
new file mode 100644 (file)
index 0000000..3c01626
--- /dev/null
@@ -0,0 +1,303 @@
+/**
+ * \file mlt_profile.c
+ * \brief video output definition
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "mlt_profile.h"
+#include "mlt_factory.h"
+#include "mlt_properties.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+
+/** the default subdirectory of the prefix for holding profiles */
+#define PROFILES_DIR "/share/mlt/profiles/"
+
+/** Load a profile from the system folder.
+ *
+ * The environment variable MLT_PROFILES_PATH overrides the default \p PROFILES_DIR.
+ *
+ * \private \memberof mlt_profile_s
+ * \param name the name of a profile settings file located in the standard location or
+ * the full path name to a profile settings file
+ * \return a profile or NULL on error
+ */
+
+static mlt_profile mlt_profile_select( const char *name )
+{
+       char *filename = NULL;
+       const char *prefix = getenv( "MLT_PROFILES_PATH" );
+       mlt_properties properties = mlt_properties_load( name );
+       mlt_profile profile = NULL;
+
+       // Try to load from file specification
+       if ( properties && mlt_properties_get_int( properties, "width" ) )
+       {
+               filename = calloc( 1, strlen( name ) + 1 );
+       }
+       // Load from $prefix/share/mlt/profiles
+       else if ( prefix == NULL )
+       {
+               prefix = PREFIX;
+               filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 2 );
+               strcpy( filename, prefix );
+               if ( filename[ strlen( filename ) - 1 ] != '/' )
+                       filename[ strlen( filename ) ] = '/';
+               strcat( filename, PROFILES_DIR );
+       }
+       // Use environment variable instead
+       else
+       {
+               filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 );
+               strcpy( filename, prefix );
+               if ( filename[ strlen( filename ) - 1 ] != '/' )
+                       filename[ strlen( filename ) ] = '/';
+       }
+
+       // Finish loading
+       strcat( filename, name );
+       profile = mlt_profile_load_file( filename );
+
+       // Cleanup
+       mlt_properties_close( properties );
+       free( filename );
+
+       return profile;
+}
+
+/** Construct a profile.
+ *
+ * This will never return NULL as it uses the dv_pal settings as hard-coded fallback default.
+ *
+ * \public \memberof mlt_profile_s
+ * @param name the name of a profile settings file located in the standard location or
+ * the full path name to a profile settings file
+ * @return a profile
+ */
+
+mlt_profile mlt_profile_init( const char *name )
+{
+       mlt_profile profile = NULL;
+
+       // Explicit profile by name gets priority over environment variables
+       if ( name )
+               profile = mlt_profile_select( name );
+
+       // Try to load by environment variable
+       if ( profile == NULL )
+       {
+               // MLT_PROFILE is preferred environment variable
+               if ( getenv( "MLT_PROFILE" ) )
+                       profile = mlt_profile_select( getenv( "MLT_PROFILE" ) );
+               // MLT_NORMALISATION backwards compatibility
+               else if ( getenv( "MLT_NORMALISATION" ) && strcmp( getenv( "MLT_NORMALISATION" ), "PAL" ) )
+                       profile = mlt_profile_select( "dv_ntsc" );
+               else
+                       profile = mlt_profile_select( "dv_pal" );
+
+               // If still not loaded (no profile files), default to PAL
+               if ( profile == NULL )
+               {
+                       profile = calloc( 1, sizeof( struct mlt_profile_s ) );
+                       if ( profile )
+                       {
+                               mlt_environment_set( "MLT_PROFILE", "dv_pal" );
+                               profile->description = strdup( "PAL 4:3 DV or DVD" );
+                               profile->frame_rate_num = 25;
+                               profile->frame_rate_den = 1;
+                               profile->width = 720;
+                               profile->height = 576;
+                               profile->progressive = 0;
+                               profile->sample_aspect_num = 16;
+                               profile->sample_aspect_den = 15;
+                               profile->display_aspect_num = 4;
+                               profile->display_aspect_den = 3;
+                       }
+               }
+       }
+       return profile;
+}
+
+/** Load a profile from specific file.
+ *
+ * \public \memberof mlt_profile_s
+ * @param file the full path name to a properties file
+ * @return a profile or NULL on error
+ */
+
+mlt_profile mlt_profile_load_file( const char *file )
+{
+       mlt_profile profile = NULL;
+
+       // Load the profile as properties
+       mlt_properties properties = mlt_properties_load( file );
+       if ( properties )
+       {
+               // Simple check if the profile is valid
+               if ( mlt_properties_get_int( properties, "width" ) )
+               {
+                       profile = mlt_profile_load_properties( properties );
+
+                       // Set MLT_PROFILE to basename
+                       char *filename = strdup( file );
+                       mlt_environment_set( "MLT_PROFILE", basename( filename ) );
+                       free( filename );
+               }
+               mlt_properties_close( properties );
+       }
+
+       // Set MLT_NORMALISATION to appease legacy modules
+       char *profile_name = mlt_environment( "MLT_PROFILE" );
+       if ( profile_name )
+       {
+               if ( strstr( profile_name, "_ntsc" ) ||
+                       strstr( profile_name, "_60" ) ||
+                       strstr( profile_name, "_30" ) )
+               {
+                       mlt_environment_set( "MLT_NORMALISATION", "NTSC" );
+               }
+               else if ( strstr( profile_name, "_pal" ) ||
+                               strstr( profile_name, "_50" ) ||
+                               strstr( profile_name, "_25" ) )
+               {
+                       mlt_environment_set( "MLT_NORMALISATION", "PAL" );
+               }
+       }
+       return profile;
+}
+
+/** Load a profile from a properties object.
+ *
+ * \public \memberof mlt_profile_s
+ * @param properties a properties list
+ * @return a profile or NULL if out of memory
+ */
+
+mlt_profile mlt_profile_load_properties( mlt_properties properties )
+{
+       mlt_profile profile = calloc( 1, sizeof( struct mlt_profile_s ) );
+       if ( profile )
+       {
+               if ( mlt_properties_get( properties, "name" ) )
+                       mlt_environment_set( "MLT_PROFILE", mlt_properties_get( properties, "name" ) );
+               if ( mlt_properties_get( properties, "description" ) )
+                       profile->description = strdup( mlt_properties_get( properties, "description" ) );
+               profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
+               profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
+               profile->width = mlt_properties_get_int( properties, "width" );
+               profile->height = mlt_properties_get_int( properties, "height" );
+               profile->progressive = mlt_properties_get_int( properties, "progressive" );
+               profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
+               profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
+               profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
+               profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
+       }
+       return profile;
+}
+
+/** Load an anonymous profile from string.
+ *
+ * \public \memberof mlt_profile_s
+ * @param string a newline-delimited list of properties as name=value pairs
+ * @return a profile or NULL if out of memory
+ */
+
+mlt_profile mlt_profile_load_string( const char *string )
+{
+       mlt_properties properties = mlt_properties_new();
+       if ( properties )
+       {
+               const char *p = string;
+               while ( p )
+               {
+                       if ( strcmp( p, "" ) && p[ 0 ] != '#' )
+                               mlt_properties_parse( properties, p );
+                       p = strchr( p, '\n' );
+                       if ( p ) p++;
+               }
+       }
+       return mlt_profile_load_properties( properties );
+}
+
+/** Get the video frame rate as a floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the frame rate
+ */
+
+double mlt_profile_fps( mlt_profile aprofile )
+{
+       if ( aprofile )
+               return ( double ) aprofile->frame_rate_num / aprofile->frame_rate_den;
+       else
+               return 0;
+}
+
+/** Get the sample aspect ratio as a floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the pixel aspect ratio
+ */
+
+double mlt_profile_sar( mlt_profile aprofile )
+{
+       if ( aprofile )
+               return ( double ) aprofile->sample_aspect_num / aprofile->sample_aspect_den;
+       else
+               return 0;
+}
+
+/** Get the display aspect ratio as floating point value.
+ *
+ * \public \memberof mlt_profile_s
+ * @param aprofile a profile
+ * @return the image aspect ratio
+ */
+
+double mlt_profile_dar( mlt_profile aprofile )
+{
+       if ( aprofile )
+               return ( double ) aprofile->display_aspect_num / aprofile->display_aspect_den;
+       else
+               return 0;
+}
+
+/** Free up the global profile resources.
+ *
+ * \public \memberof mlt_profile_s
+ * @param profile a profile
+ */
+
+void mlt_profile_close( mlt_profile profile )
+{
+       if ( profile )
+       {
+               if ( profile->description )
+                       free( profile->description );
+               profile->description = NULL;
+               free( profile );
+               profile = NULL;
+       }
+}
diff --git a/src/framework/mlt_profile.h b/src/framework/mlt_profile.h
new file mode 100644 (file)
index 0000000..6245640
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * \file mlt_profile.h
+ * \brief video output definition
+ * \see mlt_profile_s
+ *
+ * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 _MLT_PROFILE_H
+#define _MLT_PROFILE_H
+
+#include "mlt_types.h"
+
+/** \brief Profile class
+ *
+ */
+
+struct mlt_profile_s
+{
+       char* description;      /**< a brief description suitable as a label in UI menu */
+       int frame_rate_num;     /**< the numerator of the video frame rate */
+       int frame_rate_den;     /**< the denominator of the video frame rate */
+       int width;              /**< the horizontal resolution of the video */
+       int height;             /**< the vertical resolution of the video */
+       int progressive;        /**< a flag to indicate if the video is progressive scan, interlace if not set */
+       int sample_aspect_num;  /**< the numerator of the pixel aspect ratio */
+       int sample_aspect_den;  /**< the denominator of the pixel aspect ratio */
+       int display_aspect_num; /**< the numerator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */
+       int display_aspect_den; /**< the denominator of the image aspect ratio in case it can not be simply derived (e.g. ITU-R 601) */
+};
+
+extern mlt_profile mlt_profile_init( const char *name );
+extern mlt_profile mlt_profile_load_file( const char *file );
+extern mlt_profile mlt_profile_load_properties( mlt_properties properties );
+extern mlt_profile mlt_profile_load_string( const char *string );
+extern double mlt_profile_fps( mlt_profile profile );
+extern double mlt_profile_sar( mlt_profile profile );
+extern double mlt_profile_dar( mlt_profile profile );
+extern void mlt_profile_close( mlt_profile profile );
+#endif
diff --git a/src/framework/mlt_properties.c b/src/framework/mlt_properties.c
new file mode 100644 (file)
index 0000000..97ee02d
--- /dev/null
@@ -0,0 +1,1754 @@
+/**
+ * \file mlt_properties.c
+ * \brief Properties class definition
+ * \see mlt_properties_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "mlt_properties.h"
+#include "mlt_property.h"
+#include "mlt_deque.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+
+/** \brief private implementation of the property list */
+
+typedef struct
+{
+       int hash[ 199 ];
+       char **name;
+       mlt_property *value;
+       int count;
+       int size;
+       mlt_properties mirror;
+       int ref_count;
+       pthread_mutex_t mutex;
+}
+property_list;
+
+/* Memory leak checks */
+
+//#define _MLT_PROPERTY_CHECKS_ 2
+#ifdef _MLT_PROPERTY_CHECKS_
+static int properties_created = 0;
+static int properties_destroyed = 0;
+#endif
+
+/** Initialize a properties object that was already allocated.
+ *
+ * This does allocate its ::property_list, and it adds a reference count.
+ * \public \memberof mlt_properties_s
+ * \param this the properties structure to initialize
+ * \param child an opaque pointer to a subclass object
+ * \return true if failed
+ */
+
+int mlt_properties_init( mlt_properties this, void *child )
+{
+       if ( this != NULL )
+       {
+#ifdef _MLT_PROPERTY_CHECKS_
+               // Increment number of properties created
+               properties_created ++;
+#endif
+
+               // NULL all methods
+               memset( this, 0, sizeof( struct mlt_properties_s ) );
+
+               // Assign the child of the object
+               this->child = child;
+
+               // Allocate the local structure
+               this->local = calloc( sizeof( property_list ), 1 );
+
+               // Increment the ref count
+               ( ( property_list * )this->local )->ref_count = 1;
+               pthread_mutex_init( &( ( property_list * )this->local )->mutex, NULL );;
+       }
+
+       // Check that initialisation was successful
+       return this != NULL && this->local == NULL;
+}
+
+/** Create a properties object.
+ *
+ * This allocates the properties structure and calls mlt_properties_init() on it.
+ * Free the properties object with mlt_properties_close().
+ * \public \memberof mlt_properties_s
+ * \return a new properties object
+ */
+
+mlt_properties mlt_properties_new( )
+{
+       // Construct a standalone properties object
+       mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
+
+       // Initialise this
+       mlt_properties_init( this, NULL );
+
+       // Return the pointer
+       return this;
+}
+
+/** Create a properties object by reading a .properties text file.
+ *
+ * Free the properties object with mlt_properties_close().
+ * \deprecated Please start using mlt_properties_parse_yaml().
+ * \public \memberof mlt_properties_s
+ * \param filename a string contain the absolute file name
+ * \return a new properties object
+ */
+
+mlt_properties mlt_properties_load( const char *filename )
+{
+       // Construct a standalone properties object
+       mlt_properties this = mlt_properties_new( );
+
+       if ( this != NULL )
+       {
+               // Open the file
+               FILE *file = fopen( filename, "r" );
+
+               // Load contents of file
+               if ( file != NULL )
+               {
+                       // Temp string
+                       char temp[ 1024 ];
+                       char last[ 1024 ] = "";
+
+                       // Read each string from the file
+                       while( fgets( temp, 1024, file ) )
+                       {
+                               // Chomp the string
+                               temp[ strlen( temp ) - 1 ] = '\0';
+
+                               // Check if the line starts with a .
+                               if ( temp[ 0 ] == '.' )
+                               {
+                                       char temp2[ 1024 ];
+                                       sprintf( temp2, "%s%s", last, temp );
+                                       strcpy( temp, temp2 );
+                               }
+                               else if ( strchr( temp, '=' ) )
+                               {
+                                       strcpy( last, temp );
+                                       *( strchr( last, '=' ) ) = '\0';
+                               }
+
+                               // Parse and set the property
+                               if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
+                                       mlt_properties_parse( this, temp );
+                       }
+
+                       // Close the file
+                       fclose( file );
+               }
+       }
+
+       // Return the pointer
+       return this;
+}
+
+/** Generate a hash key.
+ *
+ * \private \memberof mlt_properties_s
+ * \param name a string
+ * \return an integer
+ */
+
+static inline int generate_hash( const char *name )
+{
+       int hash = 0;
+       int i = 1;
+       while ( *name )
+               hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
+       return hash;
+}
+
+/** Copy a serializable property to properties list that is mirroring this one.
+ *
+ * Special case - when a container (such as fezzik) is protecting another
+ * producer, we need to ensure that properties are passed through to the
+ * real producer.
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the name of the property to copy
+ */
+
+static inline void mlt_properties_do_mirror( mlt_properties this, const char *name )
+{
+       property_list *list = this->local;
+       if ( list->mirror != NULL )
+       {
+               char *value = mlt_properties_get( this, name );
+               if ( value != NULL )
+                       mlt_properties_set( list->mirror, name, value );
+       }
+}
+
+/** Increment the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the new reference count
+ */
+
+int mlt_properties_inc_ref( mlt_properties this )
+{
+       int result = 0;
+       if ( this != NULL )
+       {
+               property_list *list = this->local;
+               pthread_mutex_lock( &list->mutex );
+               result = ++ list->ref_count;
+               pthread_mutex_unlock( &list->mutex );
+       }
+       return result;
+}
+
+/** Decrement the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the new reference count
+ */
+
+int mlt_properties_dec_ref( mlt_properties this )
+{
+       int result = 0;
+       if ( this != NULL )
+       {
+               property_list *list = this->local;
+               pthread_mutex_lock( &list->mutex );
+               result = -- list->ref_count;
+               pthread_mutex_unlock( &list->mutex );
+       }
+       return result;
+}
+
+/** Get the reference count.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the current reference count
+ */
+
+int mlt_properties_ref_count( mlt_properties this )
+{
+       if ( this != NULL )
+       {
+               property_list *list = this->local;
+               return list->ref_count;
+       }
+       return 0;
+}
+
+/** Set a properties list to be a mirror copy of another.
+ *
+ * Note that this does not copy all existing properties. Rather, you must
+ * call this before setting the properties that you wish to copy.
+ * \public \memberof mlt_properties_s
+ * \param that the properties which will receive copies of the properties as they are set.
+ * \param this the properties to mirror
+ */
+
+void mlt_properties_mirror( mlt_properties this, mlt_properties that )
+{
+       property_list *list = this->local;
+       list->mirror = that;
+}
+
+/** Copy all serializable properties to another properties list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this The properties to copy to
+ * \param that The properties to copy from
+ * \return false
+ */
+
+int mlt_properties_inherit( mlt_properties this, mlt_properties that )
+{
+       int count = mlt_properties_count( that );
+       int i = 0;
+       for ( i = 0; i < count; i ++ )
+       {
+               char *value = mlt_properties_get_value( that, i );
+               if ( value != NULL )
+               {
+                       char *name = mlt_properties_get_name( that, i );
+                       mlt_properties_set( this, name, value );
+               }
+       }
+       return 0;
+}
+
+/** Pass all serializable properties that match a prefix to another properties object
+ *
+ * \public \memberof mlt_properties_s
+ * \param this the properties to copy to
+ * \param that The properties to copy from
+ * \param prefix the property names to match (required)
+ * \return false
+ */
+
+int mlt_properties_pass( mlt_properties this, mlt_properties that, const char *prefix )
+{
+       int count = mlt_properties_count( that );
+       int length = strlen( prefix );
+       int i = 0;
+       for ( i = 0; i < count; i ++ )
+       {
+               char *name = mlt_properties_get_name( that, i );
+               if ( !strncmp( name, prefix, length ) )
+               {
+                       char *value = mlt_properties_get_value( that, i );
+                       if ( value != NULL )
+                               mlt_properties_set( this, name + length, value );
+               }
+       }
+       return 0;
+}
+
+/** Locate a property by name.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to lookup by name
+ * \return the property or NULL for failure
+ */
+
+static inline mlt_property mlt_properties_find( mlt_properties this, const char *name )
+{
+       property_list *list = this->local;
+       mlt_property value = NULL;
+       int key = generate_hash( name );
+       int i = list->hash[ key ] - 1;
+
+       if ( i >= 0 )
+       {
+               // Check if we're hashed
+               if ( list->count > 0 &&
+                       name[ 0 ] == list->name[ i ][ 0 ] &&
+                       !strcmp( list->name[ i ], name ) )
+                       value = list->value[ i ];
+
+               // Locate the item
+               for ( i = list->count - 1; value == NULL && i >= 0; i -- )
+                       if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
+                               value = list->value[ i ];
+       }
+
+       return value;
+}
+
+/** Add a new property.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the name of the new property
+ * \return the new property
+ */
+
+static mlt_property mlt_properties_add( mlt_properties this, const char *name )
+{
+       property_list *list = this->local;
+       int key = generate_hash( name );
+
+       // Check that we have space and resize if necessary
+       if ( list->count == list->size )
+       {
+               list->size += 50;
+               list->name = realloc( list->name, list->size * sizeof( const char * ) );
+               list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
+       }
+
+       // Assign name/value pair
+       list->name[ list->count ] = strdup( name );
+       list->value[ list->count ] = mlt_property_init( );
+
+       // Assign to hash table
+       if ( list->hash[ key ] == 0 )
+               list->hash[ key ] = list->count + 1;
+
+       // Return and increment count accordingly
+       return list->value[ list->count ++ ];
+}
+
+/** Fetch a property by name and add one if not found.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to lookup or add
+ * \return the property
+ */
+
+static mlt_property mlt_properties_fetch( mlt_properties this, const char *name )
+{
+       // Try to find an existing property first
+       mlt_property property = mlt_properties_find( this, name );
+
+       // If it wasn't found, create one
+       if ( property == NULL )
+               property = mlt_properties_add( this, name );
+
+       // Return the property
+       return property;
+}
+
+/** Copy a property to another properties list.
+ *
+ * \public \memberof mlt_properties_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this the properties to copy to
+ * \param that the properties to copy from
+ * \param name the name of the property to copy
+ */
+
+void mlt_properties_pass_property( mlt_properties this, mlt_properties that, const char *name )
+{
+       // Make sure the source property isn't null.
+       mlt_property that_prop = mlt_properties_find( that, name );
+       if( that_prop == NULL )
+               return;
+
+       mlt_property_pass( mlt_properties_fetch( this, name ), that_prop );
+}
+
+/** Copy all properties specified in a comma-separated list to another properties list.
+ *
+ * White space is also a delimiter.
+ * \public \memberof mlt_properties_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this the properties to copy to
+ * \param that the properties to copy from
+ * \param list a delimited list of property names
+ * \return false
+ */
+
+
+int mlt_properties_pass_list( mlt_properties this, mlt_properties that, const char *list )
+{
+       char *props = strdup( list );
+       char *ptr = props;
+       const char *delim = " ,\t\n";   // Any combination of spaces, commas, tabs, and newlines
+       int count, done = 0;
+
+       while( !done )
+       {
+               count = strcspn( ptr, delim );
+
+               if( ptr[count] == '\0' )
+                       done = 1;
+               else
+                       ptr[count] = '\0';      // Make it a real string
+
+               mlt_properties_pass_property( this, that, ptr );
+
+               ptr += count + 1;
+               ptr += strspn( ptr, delim );
+       }
+
+       free( props );
+
+       return 0;
+}
+
+
+/** Set a property to a string.
+ *
+ * This makes a copy of the string value you supply.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the property's new value
+ * \return true if error
+ */
+
+int mlt_properties_set( mlt_properties this, const char *name, const char *value )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property == NULL )
+       {
+               mlt_log( NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name );
+       }
+       else if ( value == NULL )
+       {
+               error = mlt_property_set_string( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+       else if ( *value != '@' )
+       {
+               error = mlt_property_set_string( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+       else if ( value[ 0 ] == '@' )
+       {
+               double total = 0;
+               double current = 0;
+               char id[ 255 ];
+               char op = '+';
+
+               value ++;
+
+               while ( *value != '\0' )
+               {
+                       int length = strcspn( value, "+-*/" );
+
+                       // Get the identifier
+                       strncpy( id, value, length );
+                       id[ length ] = '\0';
+                       value += length;
+
+                       // Determine the value
+                       if ( isdigit( id[ 0 ] ) )
+                               current = atof( id );
+                       else
+                               current = mlt_properties_get_double( this, id );
+
+                       // Apply the operation
+                       switch( op )
+                       {
+                               case '+':
+                                       total += current;
+                                       break;
+                               case '-':
+                                       total -= current;
+                                       break;
+                               case '*':
+                                       total *= current;
+                                       break;
+                               case '/':
+                                       total = total / current;
+                                       break;
+                       }
+
+                       // Get the next op
+                       op = *value != '\0' ? *value ++ : ' ';
+               }
+
+               error = mlt_property_set_double( property, total );
+               mlt_properties_do_mirror( this, name );
+       }
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Set or default a property to a string.
+ *
+ * This makes a copy of the string value you supply.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the string value to set or NULL to use the default
+ * \param def the default string if value is NULL
+ * \return true if error
+ */
+
+int mlt_properties_set_or_default( mlt_properties this, const char *name, const char *value, const char *def )
+{
+       return mlt_properties_set( this, name, value == NULL ? def : value );
+}
+
+/** Get a string value by name.
+ *
+ * Do not free the returned string. It's lifetime is controlled by the property
+ * and this properties object.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the property's string value or NULL if it does not exist
+ */
+
+char *mlt_properties_get( mlt_properties this, const char *name )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? NULL : mlt_property_get_string( value );
+}
+
+/** Get a property name by index.
+ *
+ * Do not free the returned string.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \return the name of the property or NULL if index is out of range
+ */
+
+char *mlt_properties_get_name( mlt_properties this, int index )
+{
+       property_list *list = this->local;
+       if ( index >= 0 && index < list->count )
+               return list->name[ index ];
+       return NULL;
+}
+
+/** Get a property's string value by index.
+ *
+ * Do not free the returned string.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \return the property value as a string or NULL if the index is out of range
+ */
+
+char *mlt_properties_get_value( mlt_properties this, int index )
+{
+       property_list *list = this->local;
+       if ( index >= 0 && index < list->count )
+               return mlt_property_get_string( list->value[ index ] );
+       return NULL;
+}
+
+/** Get a data value by index.
+ *
+ * Do not free the returned pointer if you supplied a destructor function when you
+ * set this property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param index the numeric index of the property
+ * \param[out] size the size of the binary data in bytes or NULL if the index is out of range
+ */
+
+void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
+{
+       property_list *list = this->local;
+       if ( index >= 0 && index < list->count )
+               return mlt_property_get_data( list->value[ index ], size );
+       return NULL;
+}
+
+/** Return the number of items in the list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return the number of property objects
+ */
+
+int mlt_properties_count( mlt_properties this )
+{
+       property_list *list = this->local;
+       return list->count;
+}
+
+/** Set a value by parsing a name=value string.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param namevalue a string containing name and value delimited by '='
+ * \return true if there was an error
+ */
+
+int mlt_properties_parse( mlt_properties this, const char *namevalue )
+{
+       char *name = strdup( namevalue );
+       char *value = NULL;
+       int error = 0;
+       char *ptr = strchr( name, '=' );
+
+       if ( ptr )
+       {
+               *( ptr ++ ) = '\0';
+
+               if ( *ptr != '\"' )
+               {
+                       value = strdup( ptr );
+               }
+               else
+               {
+                       ptr ++;
+                       value = strdup( ptr );
+                       if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
+                               value[ strlen( value ) - 1 ] = '\0';
+               }
+       }
+       else
+       {
+               value = strdup( "" );
+       }
+
+       error = mlt_properties_set( this, name, value );
+
+       free( name );
+       free( value );
+
+       return error;
+}
+
+/** Get an integer associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return The integer value, 0 if not found (which may also be a legitimate value)
+ */
+
+int mlt_properties_get_int( mlt_properties this, const char *name )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? 0 : mlt_property_get_int( value );
+}
+
+/** Set a property to an integer value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the integer
+ * \return true if error
+ */
+
+int mlt_properties_set_int( mlt_properties this, const char *name, int value )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               error = mlt_property_set_int( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a 64-bit integer associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the integer value, 0 if not found (which may also be a legitimate value)
+ */
+
+int64_t mlt_properties_get_int64( mlt_properties this, const char *name )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? 0 : mlt_property_get_int64( value );
+}
+
+/** Set a property to a 64-bit integer value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the integer
+ * \return true if error
+ */
+
+int mlt_properties_set_int64( mlt_properties this, const char *name, int64_t value )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               error = mlt_property_set_int64( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a floating point value associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the floating point, 0 if not found (which may also be a legitimate value)
+ */
+
+double mlt_properties_get_double( mlt_properties this, const char *name )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? 0 : mlt_property_get_double( value );
+}
+
+/** Set a property to a floating point value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value the floating point value
+ * \return true if error
+ */
+
+int mlt_properties_set_double( mlt_properties this, const char *name, double value )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               error = mlt_property_set_double( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a position value associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \return the position, 0 if not found (which may also be a legitimate value)
+ */
+
+mlt_position mlt_properties_get_position( mlt_properties this, const char *name )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? 0 : mlt_property_get_position( value );
+}
+
+/** Set a property to a position value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \param value the position
+ * \return true if error
+ */
+
+int mlt_properties_set_position( mlt_properties this, const char *name, mlt_position value )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               error = mlt_property_set_position( property, value );
+               mlt_properties_do_mirror( this, name );
+       }
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a binary data value associated to the name.
+ *
+ * Do not free the returned pointer if you supplied a destructor function
+ * when you set this property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to get
+ * \param[out] length The size of the binary data in bytes, if available (often it is not, you should know)
+ */
+
+void *mlt_properties_get_data( mlt_properties this, const char *name, int *length )
+{
+       mlt_property value = mlt_properties_find( this, name );
+       return value == NULL ? NULL : mlt_property_get_data( value, length );
+}
+
+/** Store binary data as a property.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param name the property to set
+ * \param value an opaque pointer to binary data
+ * \param length the size of the binary data in bytes (optional)
+ * \param destroy a function to dellacate the binary data when the property is closed (optional)
+ * \param serialise a function that can serialize the binary data as text (optional)
+ * \return true if error
+ */
+
+int mlt_properties_set_data( mlt_properties this, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
+{
+       int error = 1;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( this, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+               error = mlt_property_set_data( property, value, length, destroy, serialise );
+
+       mlt_events_fire( this, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Rename a property.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param source the property to rename
+ * \param dest the new name
+ * \return true if the name is already in use
+ */
+
+int mlt_properties_rename( mlt_properties this, const char *source, const char *dest )
+{
+       mlt_property value = mlt_properties_find( this, dest );
+
+       if ( value == NULL )
+       {
+               property_list *list = this->local;
+               int i = 0;
+
+               // Locate the item
+               for ( i = 0; i < list->count; i ++ )
+               {
+                       if ( !strcmp( list->name[ i ], source ) )
+                       {
+                               free( list->name[ i ] );
+                               list->name[ i ] = strdup( dest );
+                               list->hash[ generate_hash( dest ) ] = i + 1;
+                               break;
+                       }
+               }
+       }
+
+       return value != NULL;
+}
+
+/** Dump the properties to a file handle.
+ *
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param output a file handle
+ */
+
+void mlt_properties_dump( mlt_properties this, FILE *output )
+{
+       property_list *list = this->local;
+       int i = 0;
+       for ( i = 0; i < list->count; i ++ )
+               if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
+                       fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
+}
+
+/** Output the properties to a file handle.
+ *
+ * This version includes reference counts and does not put each property on a new line.
+ * \public \memberof mlt_properties_s
+ * \param this a properties pointer
+ * \param title a string to preface the output
+ * \param output a file handle
+ */
+void mlt_properties_debug( mlt_properties this, const char *title, FILE *output )
+{
+       if ( output == NULL ) output = stderr;
+       fprintf( output, "%s: ", title );
+       if ( this != NULL )
+       {
+               property_list *list = this->local;
+               int i = 0;
+               fprintf( output, "[ ref=%d", list->ref_count );
+               for ( i = 0; i < list->count; i ++ )
+                       if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
+                               fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
+                       else
+                               fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
+               fprintf( output, " ]" );
+       }
+       fprintf( output, "\n" );
+}
+
+/** Save the properties to a file by name.
+ *
+ * This uses the dump format - one line per property.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param filename the name of a file to create or overwrite
+ * \return true if there was an error
+ */
+
+int mlt_properties_save( mlt_properties this, const char *filename )
+{
+       int error = 1;
+       FILE *f = fopen( filename, "w" );
+       if ( f != NULL )
+       {
+               mlt_properties_dump( this, f );
+               fclose( f );
+               error = 0;
+       }
+       return error;
+}
+
+/* This is a very basic cross platform fnmatch replacement - it will fail in
+ * many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
+ */
+
+/** Test whether a filename or pathname matches a shell-style pattern.
+ *
+ * \private \memberof mlt_properties_s
+ * \param wild a string containing a wildcard pattern
+ * \param file the name of a file to test against
+ * \return true if the file name matches the wildcard pattern
+ */
+
+static int mlt_fnmatch( const char *wild, const char *file )
+{
+       int f = 0;
+       int w = 0;
+
+       while( f < strlen( file ) && w < strlen( wild ) )
+       {
+               if ( wild[ w ] == '*' )
+               {
+                       w ++;
+                       if ( w == strlen( wild ) )
+                               f = strlen( file );
+                       while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
+                               f ++;
+               }
+               else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
+               {
+                       f ++;
+                       w ++;
+               }
+               else if ( wild[ 0 ] == '*' )
+               {
+                       w = 0;
+               }
+               else
+               {
+                       return 0;
+               }
+       }
+
+       return strlen( file ) == f &&  strlen( wild ) == w;
+}
+
+/** Compare the string or serialized value of two properties.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a property
+ * \param that a property
+ * \return < 0 if 'this' less than 'that', 0 if equal, or > 0 if 'this' is greater than 'that'
+ */
+
+static int mlt_compare( const void *this, const void *that )
+{
+       return strcmp( mlt_property_get_string( *( const mlt_property * )this ), mlt_property_get_string( *( const mlt_property * )that ) );
+}
+
+/** Get the contents of a directory.
+ *
+ * Obtains an optionally sorted list of the files found in a directory with a specific wild card.
+ * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
+ * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \param dirname the name of the directory
+ * \param pattern a wildcard pattern to filter the directory listing
+ * \param sort Do you want to sort the directory listing?
+ * \return the number of items in the directory listing
+ */
+
+int mlt_properties_dir_list( mlt_properties this, const char *dirname, const char *pattern, int sort )
+{
+       DIR *dir = opendir( dirname );
+
+       if ( dir )
+       {
+               char key[ 20 ];
+               struct dirent *de = readdir( dir );
+               char fullname[ 1024 ];
+               while( de != NULL )
+               {
+                       sprintf( key, "%d", mlt_properties_count( this ) );
+                       snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
+                       if ( pattern == NULL )
+                               mlt_properties_set( this, key, fullname );
+                       else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
+                               mlt_properties_set( this, key, fullname );
+                       de = readdir( dir );
+               }
+
+               closedir( dir );
+       }
+
+       if ( sort && mlt_properties_count( this ) )
+       {
+               property_list *list = this->local;
+               qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
+       }
+
+       return mlt_properties_count( this );
+}
+
+/** Close a properties object.
+ *
+ * Deallocates the properties object and everything it contains.
+ * \public \memberof mlt_properties_s
+ * \param this a properties object
+ */
+
+void mlt_properties_close( mlt_properties this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
+       {
+               if ( this->close != NULL )
+               {
+                       this->close( this->close_object );
+               }
+               else
+               {
+                       property_list *list = this->local;
+                       int index = 0;
+
+#if _MLT_PROPERTY_CHECKS_ == 1
+                       // Show debug info
+                       mlt_properties_debug( this, "Closing", stderr );
+#endif
+
+#ifdef _MLT_PROPERTY_CHECKS_
+                       // Increment destroyed count
+                       properties_destroyed ++;
+
+                       // Show current stats - these should match when the app is closed
+                       mlt_log( NULL, MLT_LOG_DEBUG, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
+#endif
+
+                       // Clean up names and values
+                       for ( index = list->count - 1; index >= 0; index -- )
+                       {
+                               free( list->name[ index ] );
+                               mlt_property_close( list->value[ index ] );
+                       }
+
+                       // Clear up the list
+                       pthread_mutex_destroy( &list->mutex );
+                       free( list->name );
+                       free( list->value );
+                       free( list );
+
+                       // Free this now if this has no child
+                       if ( this->child == NULL )
+                               free( this );
+               }
+       }
+}
+
+/** Determine if the properties list is really just a sequence or ordered list.
+ *
+ * \public \memberof mlt_properties_s
+ * \param properties a properties list
+ * \return true if all of the property names are numeric (a sequence)
+ */
+
+int mlt_properties_is_sequence( mlt_properties properties )
+{
+       int i;
+       int n = mlt_properties_count( properties );
+       for ( i = 0; i < n; i++ )
+               if ( ! isdigit( mlt_properties_get_name( properties, i )[0] ) )
+                       return 0;
+       return 1;
+}
+
+/** \brief YAML Tiny Parser context structure
+ *
+ * YAML is a nifty text format popular in the Ruby world as a cleaner,
+ * less verbose alternative to XML. See this Wikipedia topic for an overview:
+ * http://en.wikipedia.org/wiki/YAML
+ * The YAML specification is at:
+ * http://yaml.org/
+ * YAML::Tiny is a Perl module that specifies a subset of YAML that we are
+ * using here (for the same reasons):
+ * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm
+ * \private
+ */
+
+struct yaml_parser_context
+{
+       mlt_deque stack;
+       unsigned int level;
+       unsigned int index;
+       char block;
+       char *block_name;
+       unsigned int block_indent;
+
+};
+typedef struct yaml_parser_context *yaml_parser;
+
+/** Remove spaces from the left side of a string.
+ *
+ * \param s the string to trim
+ * \return the number of characters removed
+ */
+
+static unsigned int ltrim( char **s )
+{
+       unsigned int i = 0;
+       char *c = *s;
+       int n = strlen( c );
+       for ( i = 0; i < n && *c == ' '; i++, c++ );
+       *s = c;
+       return i;
+}
+
+/** Remove spaces from the right side of a string.
+ *
+ * \param s the string to trim
+ * \return the number of characters removed
+ */
+
+static unsigned int rtrim( char *s )
+{
+       int n = strlen( s );
+       int i;
+       for ( i = n; i > 0 && s[i - 1] == ' '; --i )
+               s[i - 1] = 0;
+       return n - i;
+}
+
+/** Parse a line of YAML Tiny.
+ *
+ * Adds a property if needed.
+ * \private \memberof yaml_parser_context
+ * \param context a YAML Tiny Parser context
+ * \param namevalue a line of YAML Tiny
+ * \return true if there was an error
+ */
+
+static int parse_yaml( yaml_parser context, const char *namevalue )
+{
+       char *name_ = strdup( namevalue );
+       char *name = name_;
+       char *value = NULL;
+       int error = 0;
+       char *ptr = strchr( name, ':' );
+       unsigned int indent = ltrim( &name );
+       mlt_properties properties = mlt_deque_peek_front( context->stack );
+
+       // Ascending one more levels in the tree
+       if ( indent < context->level )
+       {
+               unsigned int i;
+               unsigned int n = ( context->level - indent ) / 2;
+               for ( i = 0; i < n; i++ )
+                       mlt_deque_pop_front( context->stack );
+               properties = mlt_deque_peek_front( context->stack );
+               context->level = indent;
+       }
+
+       // Descending a level in the tree
+       else if ( indent > context->level && context->block == 0 )
+       {
+               context->level = indent;
+       }
+
+       // If there is a colon that is not part of a block
+       if ( ptr && ( indent == context->level ) )
+       {
+               // Reset block processing
+               if ( context->block_name )
+               {
+                       free( context->block_name );
+                       context->block_name = NULL;
+                       context->block = 0;
+               }
+
+               // Terminate the name and setup the value pointer
+               *( ptr ++ ) = 0;
+
+               // Trim comment
+               char *comment = strchr( ptr, '#' );
+               if ( comment )
+               {
+                       *comment = 0;
+               }
+
+               // Trim leading and trailing spaces from bare value
+               ltrim( &ptr );
+               rtrim( ptr );
+
+               // No value means a child
+               if ( strcmp( ptr, "" ) == 0 )
+               {
+                       mlt_properties child = mlt_properties_new();
+                       mlt_properties_set_data( properties, name, child, 0,
+                               ( mlt_destructor )mlt_properties_close, NULL );
+                       mlt_deque_push_front( context->stack, child );
+                       context->index = 0;
+                       free( name_ );
+                       return error;
+               }
+
+               // A dash indicates a sequence item
+               if ( name[0] == '-' )
+               {
+                       mlt_properties child = mlt_properties_new();
+                       char key[20];
+
+                       snprintf( key, sizeof(key), "%d", context->index++ );
+                       mlt_properties_set_data( properties, key, child, 0,
+                               ( mlt_destructor )mlt_properties_close, NULL );
+                       mlt_deque_push_front( context->stack, child );
+
+                       name ++;
+                       context->level += ltrim( &name ) + 1;
+                       properties = child;
+               }
+
+               // Value is quoted
+               if ( *ptr == '\"' )
+               {
+                       ptr ++;
+                       value = strdup( ptr );
+                       if ( value && value[ strlen( value ) - 1 ] == '\"' )
+                               value[ strlen( value ) - 1 ] = 0;
+               }
+
+               // Value is folded or unfolded block
+               else if ( *ptr == '|' || *ptr == '>' )
+               {
+                       context->block = *ptr;
+                       context->block_name = strdup( name );
+                       context->block_indent = 0;
+                       value = strdup( "" );
+               }
+
+               // Bare value
+               else
+               {
+                       value = strdup( ptr );
+               }
+       }
+
+       // A list of scalars
+       else if ( name[0] == '-' )
+       {
+               // Reset block processing
+               if ( context->block_name )
+               {
+                       free( context->block_name );
+                       context->block_name = NULL;
+                       context->block = 0;
+               }
+
+               char key[20];
+
+               snprintf( key, sizeof(key), "%d", context->index++ );
+               ptr = name + 1;
+
+               // Trim comment
+               char *comment = strchr( ptr, '#' );
+               if ( comment )
+                       *comment = 0;
+
+               // Trim leading and trailing spaces from bare value
+               ltrim( &ptr );
+               rtrim( ptr );
+
+               // Value is quoted
+               if ( *ptr == '\"' )
+               {
+                       ptr ++;
+                       value = strdup( ptr );
+                       if ( value && value[ strlen( value ) - 1 ] == '\"' )
+                               value[ strlen( value ) - 1 ] = 0;
+               }
+
+               // Value is folded or unfolded block
+               else if ( *ptr == '|' || *ptr == '>' )
+               {
+                       context->block = *ptr;
+                       context->block_name = strdup( key );
+                       context->block_indent = 0;
+                       value = strdup( "" );
+               }
+
+               // Bare value
+               else
+               {
+                       value = strdup( ptr );
+               }
+
+               free( name_ );
+               name = name_ = strdup( key );
+       }
+
+       // Non-folded block
+       else if ( context->block == '|' )
+       {
+               if ( context->block_indent == 0 )
+                       context->block_indent = indent;
+               if ( indent > context->block_indent )
+                       name = &name_[ context->block_indent ];
+               rtrim( name );
+               char *old_value = mlt_properties_get( properties, context->block_name );
+               value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
+               strcpy( value, old_value );
+               if ( strcmp( old_value, "" ) )
+                       strcat( value, "\n" );
+               strcat( value, name );
+               name = context->block_name;
+       }
+
+       // Folded block
+       else if ( context->block == '>' )
+       {
+               ltrim( &name );
+               rtrim( name );
+               char *old_value = mlt_properties_get( properties, context->block_name );
+
+               // Blank line (prepended with spaces) is new line
+               if ( strcmp( name, "" ) == 0 )
+               {
+                       value = calloc( 1, strlen( old_value ) + 2 );
+                       strcat( value, old_value );
+                       strcat( value, "\n" );
+               }
+               // Concatenate with space
+               else
+               {
+                       value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
+                       strcat( value, old_value );
+                       if ( strcmp( old_value, "" ) && old_value[ strlen( old_value ) - 1 ] != '\n' )
+                               strcat( value, " " );
+                       strcat( value, name );
+               }
+               name = context->block_name;
+       }
+
+       else
+       {
+               value = strdup( "" );
+       }
+
+       error = mlt_properties_set( properties, name, value );
+
+       free( name_ );
+       free( value );
+
+       return error;
+}
+
+/** Parse a YAML Tiny file by name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param filename the name of a text file containing YAML Tiny
+ * \return a new properties list
+ */
+
+mlt_properties mlt_properties_parse_yaml( const char *filename )
+{
+       // Construct a standalone properties object
+       mlt_properties this = mlt_properties_new( );
+
+       if ( this )
+       {
+               // Open the file
+               FILE *file = fopen( filename, "r" );
+
+               // Load contents of file
+               if ( file )
+               {
+                       // Temp string
+                       char temp[ 1024 ];
+                       char *ptemp = &temp[ 0 ];
+
+                       // Parser context
+                       yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) );
+                       context->stack = mlt_deque_init();
+                       mlt_deque_push_front( context->stack, this );
+
+                       // Read each string from the file
+                       while( fgets( temp, 1024, file ) )
+                       {
+                               // Check for end-of-stream
+                               if ( strncmp( ptemp, "...", 3 ) == 0 )
+                                       break;
+
+                               // Chomp the string
+                               temp[ strlen( temp ) - 1 ] = '\0';
+
+                               // Skip blank lines, comment lines, and document separator
+                               if ( strcmp( ptemp, "" ) && ptemp[ 0 ] != '#' && strncmp( ptemp, "---", 3 )
+                                    && strncmp( ptemp, "%YAML", 5 ) && strncmp( ptemp, "% YAML", 6 ) )
+                                       parse_yaml( context, temp );
+                       }
+
+                       // Close the file
+                       fclose( file );
+                       mlt_deque_close( context->stack );
+                       if ( context->block_name )
+                               free( context->block_name );
+                       free( context );
+               }
+       }
+
+       // Return the pointer
+       return this;
+}
+
+/*
+ * YAML Tiny Serializer
+ */
+
+/** How many bytes to grow at a time */
+#define STRBUF_GROWTH (1024)
+
+/** \brief Private to mlt_properties_s, a self-growing buffer for building strings
+ * \private
+ */
+
+struct strbuf_s
+{
+       size_t size;
+       char *string;
+};
+
+typedef struct strbuf_s *strbuf;
+
+/** Create a new string buffer
+ *
+ * \private \memberof strbuf_s
+ * \return a new string buffer
+ */
+
+static strbuf strbuf_new( )
+{
+       strbuf buffer = calloc( 1, sizeof( struct strbuf_s ) );
+       buffer->size = STRBUF_GROWTH;
+       buffer->string = calloc( 1, buffer->size );
+       return buffer;
+}
+
+/** Destroy a string buffer
+ *
+ * \private \memberof strbuf_s
+ * \param buffer the string buffer to close
+ */
+
+static void strbuf_close( strbuf buffer )
+{
+       // We do not free buffer->string; strbuf user must save that pointer
+       // and free it.
+       if ( buffer )
+               free( buffer );
+}
+
+/** Format a string into a string buffer
+ *
+ * A variable number of arguments follows the format string - one for each
+ * format specifier.
+ * \private \memberof strbuf_s
+ * \param buffer the string buffer to write into
+ * \param format a string that contains text and formatting instructions
+ * \return the formatted string
+ */
+
+static char *strbuf_printf( strbuf buffer, const char *format, ... )
+{
+       while ( buffer->string )
+       {
+               va_list ap;
+               va_start( ap, format );
+               size_t len = strlen( buffer->string );
+               size_t remain = buffer->size - len - 1;
+               int need = vsnprintf( buffer->string + len, remain, format, ap );
+               va_end( ap );
+               if ( need > -1 && need < remain )
+                       break;
+               buffer->string[ len ] = 0;
+               buffer->size += need + STRBUF_GROWTH;
+               buffer->string = realloc( buffer->string, buffer->size );
+       }
+       return buffer->string;
+}
+
+/** Indent a line of YAML Tiny.
+ *
+ * \private \memberof strbuf_s
+ * \param output a string buffer
+ * \param indent the number of spaces to indent
+ */
+
+static inline void indent_yaml( strbuf output, int indent )
+{
+       int j;
+       for ( j = 0; j < indent; j++ )
+               strbuf_printf( output, " " );
+}
+
+/** Convert a line string into a YAML block literal.
+ *
+ * \private \memberof strbuf_s
+ * \param output a string buffer
+ * \param value the string to format as a block literal
+ * \param indent the number of spaces to indent
+ */
+
+static void output_yaml_block_literal( strbuf output, const char *value, int indent )
+{
+       char *v = strdup( value );
+       char *sol = v;
+       char *eol = strchr( sol, '\n' );
+
+       while ( eol )
+       {
+               indent_yaml( output, indent );
+               *eol = '\0';
+               strbuf_printf( output, "%s\n", sol );
+               sol = eol + 1;
+               eol = strchr( sol, '\n' );
+       }
+       indent_yaml( output, indent );
+       strbuf_printf( output, "%s\n", sol );
+}
+
+/** Recursively serialize a properties list into a string buffer as YAML Tiny.
+ *
+ * \private \memberof mlt_properties_s
+ * \param this a properties list
+ * \param output a string buffer to hold the serialized YAML Tiny
+ * \param indent the number of spaces to indent (for recursion, initialize to 0)
+ * \param is_parent_sequence Is 'this' properties list really just a sequence (for recursion, initialize to 0)?
+ */
+
+static void serialise_yaml( mlt_properties this, strbuf output, int indent, int is_parent_sequence )
+{
+       property_list *list = this->local;
+       int i = 0;
+
+       for ( i = 0; i < list->count; i ++ )
+       {
+               // This implementation assumes that all data elements are property lists.
+               // Unfortunately, we do not have run time type identification.
+               mlt_properties child = mlt_property_get_data( list->value[ i ], NULL );
+
+               if ( mlt_properties_is_sequence( this ) )
+               {
+                       // Ignore hidden/non-serialisable items
+                       if ( list->name[ i ][ 0 ] != '_' )
+                       {
+                               // Indicate a sequence item
+                               indent_yaml( output, indent );
+                               strbuf_printf( output, "- " );
+
+                               // If the value can be represented as a string
+                               const char *value = mlt_properties_get( this, list->name[ i ] );
+                               if ( value && strcmp( value, "" ) )
+                               {
+                                       // Determine if this is an unfolded block literal
+                                       if ( strchr( value, '\n' ) )
+                                       {
+                                               strbuf_printf( output, "|\n" );
+                                               output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( "|" ) );
+                                       }
+                                       else
+                                       {
+                                               strbuf_printf( output, "%s\n", value );
+                                       }
+                               }
+                       }
+                       // Recurse on child
+                       if ( child )
+                               serialise_yaml( child, output, indent + 2, 1 );
+               }
+               else
+               {
+                       // Assume this is a normal map-oriented properties list
+                       const char *value = mlt_properties_get( this, list->name[ i ] );
+
+                       // Ignore hidden/non-serialisable items
+                       // If the value can be represented as a string
+                       if ( list->name[ i ][ 0 ] != '_' && value && strcmp( value, "" ) )
+                       {
+                               if ( is_parent_sequence == 0 )
+                                       indent_yaml( output, indent );
+                               else
+                                       is_parent_sequence = 0;
+
+                               // Determine if this is an unfolded block literal
+                               if ( strchr( value, '\n' ) )
+                               {
+                                       strbuf_printf( output, "%s: |\n", list->name[ i ] );
+                                       output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( ": " ) );
+                               }
+                               else
+                               {
+                                       strbuf_printf( output, "%s: %s\n", list->name[ i ], value );
+                               }
+                       }
+
+                       // Output a child as a map item
+                       if ( child )
+                       {
+                               indent_yaml( output, indent );
+                               strbuf_printf( output, "%s:\n", list->name[ i ] );
+
+                               // Recurse on child
+                               serialise_yaml( child, output, indent + 2, 0 );
+                       }
+               }
+       }
+}
+
+/** Serialize a properties list as a string of YAML Tiny.
+ *
+ * The caller MUST free the returned string!
+ * This operates on properties containing properties as a hierarchical data
+ * structure.
+ * \public \memberof mlt_properties_s
+ * \param this a properties list
+ * \return a string containing YAML Tiny that represents the properties list
+ */
+
+char *mlt_properties_serialise_yaml( mlt_properties this )
+{
+       strbuf b = strbuf_new();
+       strbuf_printf( b, "---\n" );
+       serialise_yaml( this, b, 0, 0 );
+       strbuf_printf( b, "...\n" );
+       char *ret = b->string;
+       strbuf_close( b );
+       return ret;
+}
diff --git a/src/framework/mlt_properties.h b/src/framework/mlt_properties.h
new file mode 100644 (file)
index 0000000..c5e15cc
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * \file mlt_properties.h
+ * \brief Properties class declaration
+ * \see mlt_properties_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 _MLT_PROPERTIES_H_
+#define _MLT_PROPERTIES_H_
+
+#include "mlt_types.h"
+#include "mlt_events.h"
+#include <stdio.h>
+
+/** \brief Properties class
+ *
+ * Properties is a combination list/dictionary of name/::mlt_property pairs.
+ * It is also a base class for many of the other MLT classes.
+ */
+
+struct mlt_properties_s
+{
+       void *child; /**< \private the object of a subclass */
+       void *local; /**< \private instance object */
+
+       /** the destructor virtual function */
+       mlt_destructor close;
+       void *close_object;  /**< the object supplied to the close virtual function */
+};
+
+extern int mlt_properties_init( mlt_properties, void *child );
+extern mlt_properties mlt_properties_new( );
+extern mlt_properties mlt_properties_load( const char *file );
+extern int mlt_properties_inc_ref( mlt_properties self );
+extern int mlt_properties_dec_ref( mlt_properties self );
+extern int mlt_properties_ref_count( mlt_properties self );
+extern void mlt_properties_mirror( mlt_properties self, mlt_properties that );
+extern int mlt_properties_inherit( mlt_properties self, mlt_properties that );
+extern int mlt_properties_pass( mlt_properties self, mlt_properties that, const char *prefix );
+extern void mlt_properties_pass_property( mlt_properties self, mlt_properties that, const char *name );
+extern int mlt_properties_pass_list( mlt_properties self, mlt_properties that, const char *list );
+extern int mlt_properties_set( mlt_properties self, const char *name, const char *value );
+extern int mlt_properties_set_or_default( mlt_properties self, const char *name, const char *value, const char *def );
+extern int mlt_properties_parse( mlt_properties self, const char *namevalue );
+extern char *mlt_properties_get( mlt_properties self, const char *name );
+extern char *mlt_properties_get_name( mlt_properties self, int index );
+extern char *mlt_properties_get_value( mlt_properties self, int index );
+extern void *mlt_properties_get_data_at( mlt_properties self, int index, int *size );
+extern int mlt_properties_get_int( mlt_properties self, const char *name );
+extern int mlt_properties_set_int( mlt_properties self, const char *name, int value );
+extern int64_t mlt_properties_get_int64( mlt_properties self, const char *name );
+extern int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t value );
+extern double mlt_properties_get_double( mlt_properties self, const char *name );
+extern int mlt_properties_set_double( mlt_properties self, const char *name, double value );
+extern mlt_position mlt_properties_get_position( mlt_properties self, const char *name );
+extern int mlt_properties_set_position( mlt_properties self, const char *name, mlt_position value );
+extern int mlt_properties_set_data( mlt_properties self, const char *name, void *value, int length, mlt_destructor, mlt_serialiser );
+extern void *mlt_properties_get_data( mlt_properties self, const char *name, int *length );
+extern int mlt_properties_rename( mlt_properties self, const char *source, const char *dest );
+extern int mlt_properties_count( mlt_properties self );
+extern void mlt_properties_dump( mlt_properties self, FILE *output );
+extern void mlt_properties_debug( mlt_properties self, const char *title, FILE *output );
+extern int mlt_properties_save( mlt_properties, const char * );
+extern int mlt_properties_dir_list( mlt_properties, const char *, const char *, int );
+extern void mlt_properties_close( mlt_properties self );
+extern int mlt_properties_is_sequence( mlt_properties self );
+extern mlt_properties mlt_properties_parse_yaml( const char *file );
+extern char *mlt_properties_serialise_yaml( mlt_properties self );
+
+#endif
diff --git a/src/framework/mlt_property.c b/src/framework/mlt_property.c
new file mode 100644 (file)
index 0000000..7cbb681
--- /dev/null
@@ -0,0 +1,512 @@
+/**
+ * \file mlt_property.c
+ * \brief Property class definition
+ * \see mlt_property_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/** Bit pattern used internally to indicated representations available.
+*/
+
+typedef enum
+{
+       mlt_prop_none = 0,    //!< not set
+       mlt_prop_int = 1,     //!< set as an integer
+       mlt_prop_string = 2,  //!< set as string or already converted to string
+       mlt_prop_position = 4,//!< set as a position
+       mlt_prop_double = 8,  //!< set as a floating point
+       mlt_prop_data = 16,   //!< set as opaque binary
+       mlt_prop_int64 = 32   //!< set as a 64-bit integer
+}
+mlt_property_type;
+
+/** \brief Property class
+ *
+ * A property is like a variant or dynamic type. They are used for many things
+ * in MLT, but in particular they are the parameter mechanism for the plugins.
+ */
+
+struct mlt_property_s
+{
+       /// Stores a bit pattern of types available for this property
+       mlt_property_type types;
+
+       /// Atomic type handling
+       int prop_int;
+       mlt_position prop_position;
+       double prop_double;
+       int64_t prop_int64;
+
+       /// String handling
+       char *prop_string;
+
+       /// Generic type handling
+       void *data;
+       int length;
+       mlt_destructor destructor;
+       mlt_serialiser serialiser;
+};
+
+/** Construct a property and initialize it
+ * \public \memberof mlt_property_s
+ */
+
+mlt_property mlt_property_init( )
+{
+       mlt_property this = malloc( sizeof( struct mlt_property_s ) );
+       if ( this != NULL )
+       {
+               this->types = 0;
+               this->prop_int = 0;
+               this->prop_position = 0;
+               this->prop_double = 0;
+               this->prop_int64 = 0;
+               this->prop_string = NULL;
+               this->data = NULL;
+               this->length = 0;
+               this->destructor = NULL;
+               this->serialiser = NULL;
+       }
+       return this;
+}
+
+/** Clear (0/null) a property.
+ *
+ * Frees up any associated resources in the process.
+ * \private \memberof mlt_property_s
+ * \param this a property
+ */
+
+static inline void mlt_property_clear( mlt_property this )
+{
+       // Special case data handling
+       if ( this->types & mlt_prop_data && this->destructor != NULL )
+               this->destructor( this->data );
+
+       // Special case string handling
+       if ( this->types & mlt_prop_string )
+               free( this->prop_string );
+
+       // Wipe stuff
+       this->types = 0;
+       this->prop_int = 0;
+       this->prop_position = 0;
+       this->prop_double = 0;
+       this->prop_int64 = 0;
+       this->prop_string = NULL;
+       this->data = NULL;
+       this->length = 0;
+       this->destructor = NULL;
+       this->serialiser = NULL;
+}
+
+/** Set the property to an integer value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value an integer
+ * \return false
+ */
+
+int mlt_property_set_int( mlt_property this, int value )
+{
+       mlt_property_clear( this );
+       this->types = mlt_prop_int;
+       this->prop_int = value;
+       return 0;
+}
+
+/** Set the property to a floating point value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a double precision floating point value
+ * \return false
+ */
+
+int mlt_property_set_double( mlt_property this, double value )
+{
+       mlt_property_clear( this );
+       this->types = mlt_prop_double;
+       this->prop_double = value;
+       return 0;
+}
+
+/** Set the property to a position value.
+ *
+ * Position is a relative time value in frame units.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a position value
+ * \return false
+ */
+
+int mlt_property_set_position( mlt_property this, mlt_position value )
+{
+       mlt_property_clear( this );
+       this->types = mlt_prop_position;
+       this->prop_position = value;
+       return 0;
+}
+
+/** Set the property to a string value.
+ *
+ * This makes a copy of the string you supply so you do not need to track
+ * a new reference to it.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value the string to copy to the property
+ * \return true if it failed
+ */
+
+int mlt_property_set_string( mlt_property this, const char *value )
+{
+       if ( value != this->prop_string )
+       {
+               mlt_property_clear( this );
+               this->types = mlt_prop_string;
+               if ( value != NULL )
+                       this->prop_string = strdup( value );
+       }
+       else
+       {
+               this->types = mlt_prop_string;
+       }
+       return this->prop_string == NULL;
+}
+
+/** Set the property to a 64-bit integer value.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value a 64-bit integer
+ * \return false
+ */
+
+int mlt_property_set_int64( mlt_property this, int64_t value )
+{
+       mlt_property_clear( this );
+       this->types = mlt_prop_int64;
+       this->prop_int64 = value;
+       return 0;
+}
+
+/** Set a property to an opaque binary value.
+ *
+ * This does not make a copy of the data. You can use a Properties object
+ * with its reference tracking and the destructor function to control
+ * the lifetime of the data. Otherwise, pass NULL for the destructor
+ * function and control the lifetime yourself.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param value an opaque pointer
+ * \param length the number of bytes pointed to by value (optional)
+ * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
+ * \param serialiser a function to use to convert this binary data to a string (optional)
+ * \return false
+ */
+
+int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
+{
+       if ( this->data == value )
+               this->destructor = NULL;
+       mlt_property_clear( this );
+       this->types = mlt_prop_data;
+       this->data = value;
+       this->length = length;
+       this->destructor = destructor;
+       this->serialiser = serialiser;
+       return 0;
+}
+
+/** Convert a base 10 or base 16 string to an integer.
+ *
+ * The string must begin with '0x' to be interpreted as hexadecimal.
+ * Otherwise, it is interpreted as base 10.
+ * If the string begins with '#' it is interpreted as a hexadecimal color value
+ * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
+ * always in the form RRGGBBAA where the alpha components are not optional.
+ * Applications and services should expect the binary color value in bytes to
+ * be in the following order: RGBA. This means they will have to cast the int
+ * to an unsigned int. This is especially important when they need to shift
+ * right to obtain RGB without alpha in order to make it do a logical instead
+ * of arithmetic shift.
+ *
+ * \private \memberof mlt_property_s
+ * \param value a string to convert
+ * \return the resultant integer
+ */
+static inline int mlt_property_atoi( const char *value )
+{
+       if ( value == NULL )
+               return 0;
+       // Parse a hex color value as #RRGGBB or #AARRGGBB.
+       if ( value[0] == '#' )
+       {
+               unsigned int rgb = strtoul( value + 1, NULL, 16 );
+               unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
+               return ( rgb << 8 ) | alpha;
+       }
+       // Do hex and decimal explicitly to avoid decimal value with leading zeros
+       // interpreted as octal.
+       else if ( value[0] == '0' && value[1] == 'x' )
+       {
+               return strtoul( value + 2, NULL, 16 );
+       }
+       else
+       {
+               return strtol( value, NULL, 10 );
+       }
+}
+
+/** Get the property as an integer.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return an integer value
+ */
+
+int mlt_property_get_int( mlt_property this )
+{
+       if ( this->types & mlt_prop_int )
+               return this->prop_int;
+       else if ( this->types & mlt_prop_double )
+               return ( int )this->prop_double;
+       else if ( this->types & mlt_prop_position )
+               return ( int )this->prop_position;
+       else if ( this->types & mlt_prop_int64 )
+               return ( int )this->prop_int64;
+       else if ( this->types & mlt_prop_string )
+               return mlt_property_atoi( this->prop_string );
+       return 0;
+}
+
+/** Get the property as a floating point.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a floating point value
+ */
+
+double mlt_property_get_double( mlt_property this )
+{
+       if ( this->types & mlt_prop_double )
+               return this->prop_double;
+       else if ( this->types & mlt_prop_int )
+               return ( double )this->prop_int;
+       else if ( this->types & mlt_prop_position )
+               return ( double )this->prop_position;
+       else if ( this->types & mlt_prop_int64 )
+               return ( double )this->prop_int64;
+       else if ( this->types & mlt_prop_string )
+               return atof( this->prop_string );
+       return 0;
+}
+
+/** Get the property as a position.
+ *
+ * A position is an offset time in terms of frame units.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return the position in frames
+ */
+
+mlt_position mlt_property_get_position( mlt_property this )
+{
+       if ( this->types & mlt_prop_position )
+               return this->prop_position;
+       else if ( this->types & mlt_prop_int )
+               return ( mlt_position )this->prop_int;
+       else if ( this->types & mlt_prop_double )
+               return ( mlt_position )this->prop_double;
+       else if ( this->types & mlt_prop_int64 )
+               return ( mlt_position )this->prop_int64;
+       else if ( this->types & mlt_prop_string )
+               return ( mlt_position )atol( this->prop_string );
+       return 0;
+}
+
+/** Convert a string to a 64-bit integer.
+ *
+ * If the string begins with '0x' it is interpreted as a hexadecimal value.
+ * \private \memberof mlt_property_s
+ * \param value a string
+ * \return a 64-bit integer
+ */
+
+static inline int64_t mlt_property_atoll( const char *value )
+{
+       if ( value == NULL )
+               return 0;
+       else if ( value[0] == '0' && value[1] == 'x' )
+               return strtoll( value + 2, NULL, 16 );
+       else
+               return strtoll( value, NULL, 10 );
+}
+
+/** Get the property as a signed integer.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a 64-bit integer
+ */
+
+int64_t mlt_property_get_int64( mlt_property this )
+{
+       if ( this->types & mlt_prop_int64 )
+               return this->prop_int64;
+       else if ( this->types & mlt_prop_int )
+               return ( int64_t )this->prop_int;
+       else if ( this->types & mlt_prop_double )
+               return ( int64_t )this->prop_double;
+       else if ( this->types & mlt_prop_position )
+               return ( int64_t )this->prop_position;
+       else if ( this->types & mlt_prop_string )
+               return mlt_property_atoll( this->prop_string );
+       return 0;
+}
+
+/** Get the property as a string.
+ *
+ * The caller is not responsible for deallocating the returned string!
+ * The string is deallocated when the Property is closed.
+ * This tries its hardest to convert the property to string including using
+ * a serialization function for binary data, if supplied.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \return a string representation of the property or NULL if failed
+ */
+
+char *mlt_property_get_string( mlt_property this )
+{
+       // Construct a string if need be
+       if ( ! ( this->types & mlt_prop_string ) )
+       {
+               if ( this->types & mlt_prop_int )
+               {
+                       this->types |= mlt_prop_string;
+                       this->prop_string = malloc( 32 );
+                       sprintf( this->prop_string, "%d", this->prop_int );
+               }
+               else if ( this->types & mlt_prop_double )
+               {
+                       this->types |= mlt_prop_string;
+                       this->prop_string = malloc( 32 );
+                       sprintf( this->prop_string, "%f", this->prop_double );
+               }
+               else if ( this->types & mlt_prop_position )
+               {
+                       this->types |= mlt_prop_string;
+                       this->prop_string = malloc( 32 );
+                       sprintf( this->prop_string, "%d", (int)this->prop_position ); /* I don't know if this is wanted. -Zach */
+               }
+               else if ( this->types & mlt_prop_int64 )
+               {
+                       this->types |= mlt_prop_string;
+                       this->prop_string = malloc( 32 );
+                        sprintf( this->prop_string, "%lld", (long long int)this->prop_int64 );
+               }
+               else if ( this->types & mlt_prop_data && this->serialiser != NULL )
+               {
+                       this->types |= mlt_prop_string;
+                       this->prop_string = this->serialiser( this->data, this->length );
+               }
+       }
+
+       // Return the string (may be NULL)
+       return this->prop_string;
+}
+
+/** Get the binary data from a property.
+ *
+ * This only works if you previously put binary data into the property.
+ * This does not return a copy of the data; it returns a pointer to it.
+ * If you supplied a destructor function when setting the binary data,
+ * the destructor is used when the Property is closed to free the memory.
+ * Therefore, only free the returned pointer if you did not supply a
+ * destructor function.
+ * \public \memberof mlt_property_s
+ * \param this a property
+ * \param[out] length the size of the binary object in bytes (optional)
+ * \return an opaque data pointer or NULL if not available
+ */
+
+void *mlt_property_get_data( mlt_property this, int *length )
+{
+       // Assign length if not NULL
+       if ( length != NULL )
+               *length = this->length;
+
+       // Return the data (note: there is no conversion here)
+       return this->data;
+}
+
+/** Destroy a property and free all related resources.
+ *
+ * \public \memberof mlt_property_s
+ * \param this a property
+ */
+
+void mlt_property_close( mlt_property this )
+{
+       mlt_property_clear( this );
+       free( this );
+}
+
+/** Copy a property.
+ *
+ * A Property holding binary data only copies the data if a serialiser
+ * function was supplied when you set the Property.
+ * \public \memberof mlt_property_s
+ * \author Zach <zachary.drew@gmail.com>
+ * \param this a property
+ * \param that another property
+ */
+void mlt_property_pass( mlt_property this, mlt_property that )
+{
+       mlt_property_clear( this );
+
+       this->types = that->types;
+
+       if ( this->types & mlt_prop_int64 )
+               this->prop_int64 = that->prop_int64;
+       else if ( this->types & mlt_prop_int )
+               this->prop_int = that->prop_int;
+       else if ( this->types & mlt_prop_double )
+               this->prop_double = that->prop_double;
+       else if ( this->types & mlt_prop_position )
+               this->prop_position = that->prop_position;
+       else if ( this->types & mlt_prop_string )
+       {
+               if ( that->prop_string != NULL )
+                       this->prop_string = strdup( that->prop_string );
+       }
+       else if ( this->types & mlt_prop_data && this->serialiser != NULL )
+       {
+               this->types = mlt_prop_string;
+               this->prop_string = this->serialiser( this->data, this->length );
+       }
+}
diff --git a/src/framework/mlt_property.h b/src/framework/mlt_property.h
new file mode 100644 (file)
index 0000000..f33aa88
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * \file mlt_property.h
+ * \brief Property class declaration
+ * \see mlt_property_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_PROPERTY_H_
+#define _MLT_PROPERTY_H_
+
+#include "mlt_types.h"
+
+extern mlt_property mlt_property_init( );
+extern int mlt_property_set_int( mlt_property self, int value );
+extern int mlt_property_set_double( mlt_property self, double value );
+extern int mlt_property_set_position( mlt_property self, mlt_position value );
+extern int mlt_property_set_int64( mlt_property self, int64_t value );
+extern int mlt_property_set_string( mlt_property self, const char *value );
+extern int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser );
+extern int mlt_property_get_int( mlt_property self );
+extern double mlt_property_get_double( mlt_property self );
+extern mlt_position mlt_property_get_position( mlt_property self );
+extern int64_t mlt_property_get_int64( mlt_property self );
+extern char *mlt_property_get_string( mlt_property self );
+extern void *mlt_property_get_data( mlt_property self, int *length );
+extern void mlt_property_close( mlt_property self );
+
+extern void mlt_property_pass( mlt_property this, mlt_property that );
+
+#endif
diff --git a/src/framework/mlt_repository.c b/src/framework/mlt_repository.c
new file mode 100644 (file)
index 0000000..c843ecb
--- /dev/null
@@ -0,0 +1,428 @@
+/**
+ * \file mlt_repository.c
+ * \brief provides a map between service and shared objects
+ * \see mlt_repository_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "mlt_repository.h"
+#include "mlt_properties.h"
+#include "mlt_tokeniser.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+
+/** \brief Repository class
+ *
+ * The Repository is a collection of plugin modules and their services and service metadata.
+ *
+ * \extends mlt_properties_s
+ * \properties \p language a cached list of user locales
+ */
+
+struct mlt_repository_s
+{
+       struct mlt_properties_s parent; /// a list of object files
+       mlt_properties consumers;       /// a list of entry points for consumers
+       mlt_properties filters;         /// a list of entry points for filters
+       mlt_properties producers;       /// a list of entry points for producers
+       mlt_properties transitions;     /// a list of entry points for transitions
+};
+
+/** Construct a new repository.
+ *
+ * \public \memberof mlt_repository_s
+ * \param directory the full path of a directory from which to read modules
+ * \return a new repository or NULL if failed
+ */
+
+mlt_repository mlt_repository_init( const char *directory )
+{
+       // Safety check
+       if ( directory == NULL || strcmp( directory, "" ) == 0 )
+               return NULL;
+
+       // Construct the repository
+       mlt_repository this = calloc( sizeof( struct mlt_repository_s ), 1 );
+       mlt_properties_init( &this->parent, this );
+       this->consumers = mlt_properties_new();
+       this->filters = mlt_properties_new();
+       this->producers = mlt_properties_new();
+       this->transitions = mlt_properties_new();
+
+       // Get the directory list
+       mlt_properties dir = mlt_properties_new();
+       int count = mlt_properties_dir_list( dir, directory, NULL, 0 );
+       int i;
+
+       // Iterate over files
+       for ( i = 0; i < count; i++ )
+       {
+               int flags = RTLD_NOW;
+               const char *object_name = mlt_properties_get_value( dir, i);
+
+               // Very temporary hack to allow the quicktime plugins to work
+               // TODO: extend repository to allow this to be used on a case by case basis
+               if ( strstr( object_name, "libmltkino" ) )
+                       flags |= RTLD_GLOBAL;
+
+               // Open the shared object
+               void *object = dlopen( object_name, flags );
+               if ( object != NULL )
+               {
+                       // Get the registration function
+                       mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" );
+
+                       // Call the registration function
+                       if ( symbol_ptr != NULL )
+                       {
+                               symbol_ptr( this );
+
+                               // Register the object file for closure
+                               mlt_properties_set_data( &this->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
+                       }
+                       else
+                       {
+                               dlclose( object );
+                       }
+               }
+               else if ( strstr( object_name, "libmlt" ) )
+               {
+                       mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n  (%s)\n", __FUNCTION__, object_name, dlerror() );
+               }
+       }
+
+       mlt_properties_close( dir );
+
+       return this;
+}
+
+/** Create a properties list for a service holding a function pointer to its constructor function.
+ *
+ * \private \memberof mlt_repository_s
+ * \param symbol a pointer to a function that can create the service.
+ * \return a properties list
+ */
+
+static mlt_properties new_service( void *symbol )
+{
+       mlt_properties properties = mlt_properties_new();
+       mlt_properties_set_data( properties, "symbol", symbol, 0, NULL, NULL );
+       return properties;
+}
+
+/** Register a service with the repository.
+ *
+ * Typically, this is invoked by a module within its mlt_register().
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ * \param service_type a service class
+ * \param service the name of a service
+ * \param symbol a pointer to a function to create the service
+ */
+
+void mlt_repository_register( mlt_repository this, mlt_service_type service_type, const char *service, mlt_register_callback symbol )
+{
+       // Add the entry point to the corresponding service list
+       switch ( service_type )
+       {
+               case consumer_type:
+                       mlt_properties_set_data( this->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+                       break;
+               case filter_type:
+                       mlt_properties_set_data( this->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+                       break;
+               case producer_type:
+                       mlt_properties_set_data( this->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+                       break;
+               case transition_type:
+                       mlt_properties_set_data( this->transitions, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+                       break;
+               default:
+                       break;
+       }
+}
+
+/** Get the repository properties for particular service class.
+ *
+ * \private \memberof mlt_repository_s
+ * \param this a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \return a properties list or NULL if error
+ */
+
+static mlt_properties get_service_properties( mlt_repository this, mlt_service_type type, const char *service )
+{
+       mlt_properties service_properties = NULL;
+
+       // Get the entry point from the corresponding service list
+       switch ( type )
+       {
+               case consumer_type:
+                       service_properties = mlt_properties_get_data( this->consumers, service, NULL );
+                       break;
+               case filter_type:
+                       service_properties = mlt_properties_get_data( this->filters, service, NULL );
+                       break;
+               case producer_type:
+                       service_properties = mlt_properties_get_data( this->producers, service, NULL );
+                       break;
+               case transition_type:
+                       service_properties = mlt_properties_get_data( this->transitions, service, NULL );
+                       break;
+               default:
+                       break;
+       }
+       return service_properties;
+}
+
+/** Construct a new instance of a service.
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ * \param profile a \p mlt_profile to give the service
+ * \param type a service class
+ * \param service the name of the service
+ * \param input an optional argument to the service constructor
+ */
+
+void *mlt_repository_create( mlt_repository this, mlt_profile profile, mlt_service_type type, const char *service, const void *input )
+{
+       mlt_properties properties = get_service_properties( this, type, service );
+       if ( properties != NULL )
+       {
+               mlt_register_callback symbol_ptr = mlt_properties_get_data( properties, "symbol", NULL );
+
+               // Construct the service
+               return ( symbol_ptr != NULL ) ? symbol_ptr( profile, type, service, input ) : NULL;
+       }
+       return NULL;
+}
+
+/** Destroy a repository and free its resources.
+ *
+ * \public \memberof mlt_repository_s
+ * \param this a repository
+ */
+
+void mlt_repository_close( mlt_repository this )
+{
+       mlt_properties_close( this->consumers );
+       mlt_properties_close( this->filters );
+       mlt_properties_close( this->producers );
+       mlt_properties_close( this->transitions );
+       mlt_properties_close( &this->parent );
+       free( this );
+}
+
+/** Get the list of registered consumers.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list containing all of the consumers
+ */
+
+mlt_properties mlt_repository_consumers( mlt_repository self )
+{
+       return self->consumers;
+}
+
+/** Get the list of registered filters.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the filters
+ */
+
+mlt_properties mlt_repository_filters( mlt_repository self )
+{
+       return self->filters;
+}
+
+/** Get the list of registered producers.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the producers
+ */
+
+mlt_properties mlt_repository_producers( mlt_repository self )
+{
+       return self->producers;
+}
+
+/** Get the list of registered transitions.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list of all of the transitions
+ */
+
+mlt_properties mlt_repository_transitions( mlt_repository self )
+{
+       return self->transitions;
+}
+
+/** Register the metadata for a service.
+ *
+ * IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties
+ * that you supply!
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \param callback the pointer to a function that can supply metadata
+ * \param callback_data an opaque user data pointer to be supplied on the callback
+ */
+
+void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data )
+{
+       mlt_properties service_properties = get_service_properties( self, type, service );
+       mlt_properties_set_data( service_properties, "metadata_cb", callback, 0, NULL, NULL );
+       mlt_properties_set_data( service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL );
+}
+
+/** Get the metadata about a service.
+ *
+ * Returns NULL if service or its metadata are unavailable.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \param type a service class
+ * \param service the name of a service
+ * \return the service metadata as a structured properties list
+ */
+
+mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service )
+{
+       mlt_properties metadata = NULL;
+       mlt_properties properties = get_service_properties( self, type, service );
+
+       // If this is a valid service
+       if ( properties )
+       {
+               // Lookup cached metadata
+               metadata = mlt_properties_get_data( properties, "metadata", NULL );
+               if ( ! metadata )
+               {
+                       // Not cached, so get the registered metadata callback function
+                       mlt_metadata_callback callback = mlt_properties_get_data( properties, "metadata_cb", NULL );
+
+                       // If a metadata callback function is registered
+                       if ( callback )
+                       {
+                               // Fetch the callback data arg
+                               void *data = mlt_properties_get_data( properties, "metadata_cb_data", NULL );
+
+                               // Fetch the metadata through the callback
+                               metadata = callback( type, service, data );
+
+                               // Cache the metadata
+                               if ( metadata )
+                                       // Include dellocation and serialisation
+                                       mlt_properties_set_data( properties, "metadata", metadata, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
+                       }
+               }
+       }
+       return metadata;
+}
+
+/** Try to determine the locale from some commonly used environment variables.
+ *
+ * \private \memberof mlt_repository_s
+ * \return a string containing the locale id or NULL if unknown
+ */
+
+static char *getenv_locale()
+{
+       char *s = getenv( "LANGUAGE" );
+       if ( s && s[0] )
+               return s;
+       s = getenv( "LC_ALL" );
+       if ( s && s[0] )
+               return s;
+       s = getenv( "LC_MESSAGES" );
+       if ( s && s[0] )
+               return s;
+       s = getenv( "LANG" );
+       if ( s && s[0] )
+               return s;
+       return NULL;
+}
+
+/** Return a list of user-preferred language codes taken from environment variables.
+ *
+ * A module should use this to locate a localized YAML Tiny file from which to build
+ * its metadata strucutured properties.
+ *
+ * \public \memberof mlt_repository_s
+ * \param self a repository
+ * \return a properties list that is a list (not a map) of locales, defaults to "en" if not
+ * overridden by environment variables, in order: LANGUAGE, LC_ALL, LC_MESSAGES, LANG
+ */
+
+mlt_properties mlt_repository_languages( mlt_repository self )
+{
+       mlt_properties languages = mlt_properties_get_data( &self->parent, "languages", NULL );
+       if ( languages )
+               return languages;
+
+       languages = mlt_properties_new();
+       char *locale = getenv_locale();
+       if ( locale )
+       {
+               locale = strdup( locale );
+               mlt_tokeniser tokeniser = mlt_tokeniser_init();
+               int count = mlt_tokeniser_parse_new( tokeniser, locale, ":" );
+               if ( count )
+               {
+                       int i;
+                       for ( i = 0; i < count; i++ )
+                       {
+                               char *locale = mlt_tokeniser_get_string( tokeniser, i );
+                               if ( strcmp( locale, "C" ) == 0 || strcmp( locale, "POSIX" ) == 0 )
+                                       locale = "en";
+                               else if ( strlen( locale ) > 2 )
+                                       locale[2] = 0;
+                               char string[21];
+                               snprintf( string, sizeof(string), "%d", i );
+                               mlt_properties_set( languages, string, locale );
+                       }
+               }
+               else
+               {
+                       mlt_properties_set( languages, "0", "en" );
+               }
+               free( locale );
+               mlt_tokeniser_close( tokeniser );
+       }
+       else
+       {
+               mlt_properties_set( languages, "0", "en" );
+       }
+       mlt_properties_set_data( &self->parent, "languages", languages, 0, ( mlt_destructor )mlt_properties_close, NULL );
+       return languages;
+}
diff --git a/src/framework/mlt_repository.h b/src/framework/mlt_repository.h
new file mode 100644 (file)
index 0000000..a653b49
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * \file mlt_repository.h
+ * \brief provides a map between service and shared objects
+ * \see mlt_repository_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 _MLT_REPOSITORY_H_
+#define _MLT_REPOSITORY_H_
+
+#include "mlt_types.h"
+#include "mlt_profile.h"
+
+/** This callback is the main entry point into a module, which must be exported
+ *  with the symbol "mlt_register".
+ *
+ *  Inside the callback, the module registers the additional callbacks below.
+ */
+
+typedef void ( *mlt_repository_callback )( mlt_repository );
+
+/** The callback function that modules implement to construct a service.
+ */
+
+typedef void *( *mlt_register_callback )( mlt_profile, mlt_service_type, const char * /* service name */, const void * /* arg */ );
+
+/** The callback function that modules implement to supply metadata as a properties list.
+ */
+
+typedef mlt_properties ( *mlt_metadata_callback )( mlt_service_type, const char * /* service name */, void * /* callback_data */ );
+
+/** A convenience macro to create an entry point for service registration. */
+#define MLT_REPOSITORY void mlt_register( mlt_repository repository )
+
+/** A convenience macro to a register service in a more declarative manner. */
+#define MLT_REGISTER( type, service, symbol  ) ( mlt_repository_register( repository, (type), (service), ( mlt_register_callback )(symbol) ) )
+
+/** A convenience macro to a register metadata in a more declarative manner. */
+#define MLT_REGISTER_METADATA( type, service, callback, data ) ( mlt_repository_register_metadata( repository, (type), (service), ( mlt_metadata_callback )(callback), (data) ) )
+
+extern mlt_repository mlt_repository_init( const char *directory );
+extern void mlt_repository_register( mlt_repository self, mlt_service_type service_type, const char *service, mlt_register_callback );
+extern void *mlt_repository_create( mlt_repository self, mlt_profile profile, mlt_service_type type, const char *service, const void *arg );
+extern void mlt_repository_close( mlt_repository self );
+extern mlt_properties mlt_repository_consumers( mlt_repository self );
+extern mlt_properties mlt_repository_filters( mlt_repository self );
+extern mlt_properties mlt_repository_producers( mlt_repository self );
+extern mlt_properties mlt_repository_transitions( mlt_repository self );
+extern void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback, void *callback_data );
+extern mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service );
+extern mlt_properties mlt_repository_languages( mlt_repository self );
+
+#endif
+
diff --git a/src/framework/mlt_service.c b/src/framework/mlt_service.c
new file mode 100644 (file)
index 0000000..b5e8f82
--- /dev/null
@@ -0,0 +1,738 @@
+/**
+ * \file mlt_service.c
+ * \brief interface definition for all service classes
+ * \see mlt_service_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_service.h"
+#include "mlt_filter.h"
+#include "mlt_frame.h"
+#include "mlt_cache.h"
+#include "mlt_factory.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+
+/*  IMPORTANT NOTES
+
+       The base service implements a null frame producing service - as such,
+       it is functional without extension and will produce test cards frames
+       and PAL sized audio frames.
+
+       PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
+       CONTROL THIS IN EXTENDING CLASSES.
+*/
+
+/** \brief private service definition */
+
+typedef struct
+{
+       int size;
+       int count;
+       mlt_service *in;
+       mlt_service out;
+       int filter_count;
+       int filter_size;
+       mlt_filter *filters;
+       pthread_mutex_t mutex;
+}
+mlt_service_base;
+
+/* Private methods
+ */
+
+static void mlt_service_disconnect( mlt_service this );
+static void mlt_service_connect( mlt_service this, mlt_service that );
+static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service this, void **args );
+static void purge_cache( mlt_service self );
+
+/** Initialize a service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service structure to initialize
+ * \param child pointer to the child object for the subclass
+ * \return true if there was an error
+ */
+
+int mlt_service_init( mlt_service this, void *child )
+{
+       int error = 0;
+
+       // Initialise everything to NULL
+       memset( this, 0, sizeof( struct mlt_service_s ) );
+
+       // Assign the child
+       this->child = child;
+
+       // Generate local space
+       this->local = calloc( sizeof( mlt_service_base ), 1 );
+
+       // Associate the methods
+       this->get_frame = service_get_frame;
+
+       // Initialise the properties
+       error = mlt_properties_init( &this->parent, this );
+       if ( error == 0 )
+       {
+               this->parent.close = ( mlt_destructor )mlt_service_close;
+               this->parent.close_object = this;
+
+               mlt_events_init( &this->parent );
+               mlt_events_register( &this->parent, "service-changed", NULL );
+               mlt_events_register( &this->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed );
+               pthread_mutex_init( &( ( mlt_service_base * )this->local )->mutex, NULL );
+       }
+
+       return error;
+}
+
+/** The transmitter for property changes.
+ *
+ * Invokes the listener.
+ *
+ * \private \memberof mlt_service_s
+ * \param listener a function pointer that will be invoked
+ * \param owner a properties list that will be passed to \p listener
+ * \param this a service that will be passed to \p listener
+ * \param args an array of pointers - the first entry is passed as a string to \p listener
+ */
+
+static void mlt_service_property_changed( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( char * )args[ 0 ] );
+}
+
+/** Acquire a mutual exclusion lock on this service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to lock
+ */
+
+void mlt_service_lock( mlt_service this )
+{
+       if ( this != NULL )
+               pthread_mutex_lock( &( ( mlt_service_base * )this->local )->mutex );
+}
+
+/** Release a mutual exclusion lock on this service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to unlock
+ */
+
+void mlt_service_unlock( mlt_service this )
+{
+       if ( this != NULL )
+               pthread_mutex_unlock( &( ( mlt_service_base * )this->local )->mutex );
+}
+
+/** Identify the subclass of the service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the subclass
+ */
+
+mlt_service_type mlt_service_identify( mlt_service this )
+{
+       mlt_service_type type = invalid_type;
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+               char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+               char *resource = mlt_properties_get( properties, "resource" );
+               if ( mlt_type == NULL )
+                       type = unknown_type;
+               else if (resource != NULL && !strcmp( resource, "<playlist>" ) )
+                       type = playlist_type;
+               else if (resource != NULL && !strcmp( resource, "<tractor>" ) )
+                       type = tractor_type;
+               else if (resource != NULL && !strcmp( resource, "<multitrack>" ) )
+                       type = multitrack_type;
+               else if ( !strcmp( mlt_type, "producer" ) )
+                       type = producer_type;
+               else if ( !strcmp( mlt_type, "filter" ) )
+                       type = filter_type;
+               else if ( !strcmp( mlt_type, "transition" ) )
+                       type = transition_type;
+               else if ( !strcmp( mlt_type, "consumer" ) )
+                       type = consumer_type;
+               else
+                       type = unknown_type;
+       }
+       return type;
+}
+
+/** Connect a producer to the service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param producer a producer
+ * \param index which of potentially multiple producers to this service (0 based)
+ * \return > 0 warning, == 0 success, < 0 serious error,
+ *         1 = this service does not accept input,
+ *         2 = the producer is invalid,
+ *         3 = the producer is already registered with this consumer
+ */
+
+int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index )
+{
+       int i = 0;
+
+       // Get the service base
+       mlt_service_base *base = this->local;
+
+       // Special case 'track' index - only works for last filter(s) in a particular chain
+       // but allows a filter to apply to the output frame regardless of which track it comes from
+       if ( index == -1 )
+               index = 0;
+
+       // Check if the producer is already registered with this service
+       for ( i = 0; i < base->count; i ++ )
+               if ( base->in[ i ] == producer )
+                       return 3;
+
+       // Allocate space
+       if ( index >= base->size )
+       {
+               int new_size = base->size + index + 10;
+               base->in = realloc( base->in, new_size * sizeof( mlt_service ) );
+               if ( base->in != NULL )
+               {
+                       for ( i = base->size; i < new_size; i ++ )
+                               base->in[ i ] = NULL;
+                       base->size = new_size;
+               }
+       }
+
+       // If we have space, assign the input
+       if ( base->in != NULL && index >= 0 && index < base->size )
+       {
+               // Get the current service
+               mlt_service current = base->in[ index ];
+
+               // Increment the reference count on this producer
+               if ( producer != NULL )
+                       mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) );
+
+               // Now we disconnect the producer service from its consumer
+               mlt_service_disconnect( producer );
+
+               // Add the service to index specified
+               base->in[ index ] = producer;
+
+               // Determine the number of active tracks
+               if ( index >= base->count )
+                       base->count = index + 1;
+
+               // Now we connect the producer to its connected consumer
+               mlt_service_connect( producer, this );
+
+               // Close the current service
+               mlt_service_close( current );
+
+               // Inform caller that all went well
+               return 0;
+       }
+       else
+       {
+               return -1;
+       }
+}
+
+/** Disconnect this service from its consumer.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ */
+
+static void mlt_service_disconnect( mlt_service this )
+{
+       if ( this != NULL )
+       {
+               // Get the service base
+               mlt_service_base *base = this->local;
+
+               // Disconnect
+               base->out = NULL;
+       }
+}
+
+/** Obtain the consumer this service is connected to.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the consumer
+ */
+
+mlt_service mlt_service_consumer( mlt_service this )
+{
+       // Get the service base
+       mlt_service_base *base = this->local;
+
+       // Return the connected consumer
+       return base->out;
+}
+
+/** Obtain the producer this service is connected to.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the last-most producer
+ */
+
+mlt_service mlt_service_producer( mlt_service this )
+{
+       // Get the service base
+       mlt_service_base *base = this->local;
+
+       // Return the connected producer
+       return base->count > 0 ? base->in[ base->count - 1 ] : NULL;
+}
+
+/** Associate this service to a consumer.
+ *
+ * Overwrites connection to any existing consumer.
+ * \private \memberof mlt_service_s
+ * \param this a service
+ * \param that a consumer
+ */
+
+static void mlt_service_connect( mlt_service this, mlt_service that )
+{
+       if ( this != NULL )
+       {
+               // Get the service base
+               mlt_service_base *base = this->local;
+
+               // There's a bit more required here...
+               base->out = that;
+       }
+}
+
+/** Get the first connected producer.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the first producer
+ */
+
+mlt_service mlt_service_get_producer( mlt_service this )
+{
+       mlt_service producer = NULL;
+
+       // Get the service base
+       mlt_service_base *base = this->local;
+
+       if ( base->in != NULL )
+               producer = base->in[ 0 ];
+
+       return producer;
+}
+
+/** Default implementation of the get_frame virtual function.
+ *
+ * \private \memberof mlt_service_s
+ * \param this a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return false
+ */
+
+static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
+{
+       mlt_service_base *base = this->local;
+       if ( index < base->count )
+       {
+               mlt_service producer = base->in[ index ];
+               if ( producer != NULL )
+                       return mlt_service_get_frame( producer, frame, index );
+       }
+       *frame = mlt_frame_init( this );
+       return 0;
+}
+
+/** Return the properties object.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the properties
+ */
+
+mlt_properties mlt_service_properties( mlt_service this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Recursively apply attached filters.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param frame a frame
+ * \param index used to track depth of recursion, top caller should supply 0
+ */
+
+void mlt_service_apply_filters( mlt_service this, mlt_frame frame, int index )
+{
+       int i;
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+       mlt_properties service_properties = MLT_SERVICE_PROPERTIES( this );
+       mlt_service_base *base = this->local;
+       mlt_position position = mlt_frame_get_position( frame );
+       mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
+       mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
+
+       if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
+       {
+               // Process the frame with the attached filters
+               for ( i = 0; i < base->filter_count; i ++ )
+               {
+                       if ( base->filters[ i ] != NULL )
+                       {
+                               mlt_position in = mlt_filter_get_in( base->filters[ i ] );
+                               mlt_position out = mlt_filter_get_out( base->filters[ i ] );
+                               int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
+                               if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
+                               {
+                                       mlt_properties_set_position( frame_properties, "in", in == 0 ? this_in : in );
+                                       mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out : out );
+                                       mlt_filter_process( base->filters[ i ], frame );
+                                       mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
+                               }
+                       }
+               }
+       }
+}
+
+/** Obtain a frame.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param[out] frame a frame by reference
+ * \param index as determined by the producer
+ * \return true if there was an error
+ */
+
+int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
+{
+       int result = 0;
+
+       // Lock the service
+       mlt_service_lock( this );
+
+       // Ensure that the frame is NULL
+       *frame = NULL;
+
+       // Only process if we have a valid service
+       if ( this != NULL && this->get_frame != NULL )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+               mlt_position in = mlt_properties_get_position( properties, "in" );
+               mlt_position out = mlt_properties_get_position( properties, "out" );
+
+               result = this->get_frame( this, frame, index );
+
+               if ( result == 0 )
+               {
+                       mlt_properties_inc_ref( properties );
+                       properties = MLT_FRAME_PROPERTIES( *frame );
+                       if ( in >=0 && out > 0 )
+                       {
+                               mlt_properties_set_position( properties, "in", in );
+                               mlt_properties_set_position( properties, "out", out );
+                       }
+                       mlt_service_apply_filters( this, *frame, 1 );
+                       mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), this );
+               }
+       }
+
+       // Make sure we return a frame
+       if ( *frame == NULL )
+               *frame = mlt_frame_init( this );
+
+       // Unlock the service
+       mlt_service_unlock( this );
+
+       return result;
+}
+
+/** The service-changed event handler.
+ *
+ * \private \memberof mlt_service_s
+ * \param owner ignored
+ * \param this the service on which the "service-changed" event is fired
+ */
+
+static void mlt_service_filter_changed( mlt_service owner, mlt_service this )
+{
+       mlt_events_fire( MLT_SERVICE_PROPERTIES( this ), "service-changed", NULL );
+}
+
+/** Attach a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param filter the filter to attach
+ * \return true if there was an error
+ */
+
+int mlt_service_attach( mlt_service this, mlt_filter filter )
+{
+       int error = this == NULL || filter == NULL;
+       if ( error == 0 )
+       {
+               int i = 0;
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+               mlt_service_base *base = this->local;
+
+               for ( i = 0; error == 0 && i < base->filter_count; i ++ )
+                       if ( base->filters[ i ] == filter )
+                               error = 1;
+
+               if ( error == 0 )
+               {
+                       if ( base->filter_count == base->filter_size )
+                       {
+                               base->filter_size += 10;
+                               base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) );
+                       }
+
+                       if ( base->filters != NULL )
+                       {
+                               mlt_properties props = MLT_FILTER_PROPERTIES( filter );
+                               mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) );
+                               base->filters[ base->filter_count ++ ] = filter;
+                               mlt_events_fire( properties, "service-changed", NULL );
+                               mlt_events_listen( props, this, "service-changed", ( mlt_listener )mlt_service_filter_changed );
+                               mlt_events_listen( props, this, "property-changed", ( mlt_listener )mlt_service_filter_changed );
+                       }
+                       else
+                       {
+                               error = 2;
+                       }
+               }
+       }
+       return error;
+}
+
+/** Detach a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param filter the filter to detach
+ * \return true if there was an error
+ */
+
+int mlt_service_detach( mlt_service this, mlt_filter filter )
+{
+       int error = this == NULL || filter == NULL;
+       if ( error == 0 )
+       {
+               int i = 0;
+               mlt_service_base *base = this->local;
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( this );
+
+               for ( i = 0; i < base->filter_count; i ++ )
+                       if ( base->filters[ i ] == filter )
+                               break;
+
+               if ( i < base->filter_count )
+               {
+                       base->filters[ i ] = NULL;
+                       for ( i ++ ; i < base->filter_count; i ++ )
+                               base->filters[ i - 1 ] = base->filters[ i ];
+                       base->filter_count --;
+                       mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), this );
+                       mlt_filter_close( filter );
+                       mlt_events_fire( properties, "service-changed", NULL );
+               }
+       }
+       return error;
+}
+
+/** Retrieve a filter.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \param index which one of potentially multiple filters
+ * \return the filter or null if there was an error
+ */
+
+mlt_filter mlt_service_filter( mlt_service this, int index )
+{
+       mlt_filter filter = NULL;
+       if ( this != NULL )
+       {
+               mlt_service_base *base = this->local;
+               if ( index >= 0 && index < base->filter_count )
+                       filter = base->filters[ index ];
+       }
+       return filter;
+}
+
+/** Retrieve the profile.
+ *
+ * \public \memberof mlt_service_s
+ * \param this a service
+ * \return the profile
+ */
+
+mlt_profile mlt_service_profile( mlt_service this )
+{
+       return mlt_properties_get_data( MLT_SERVICE_PROPERTIES( this ), "_profile", NULL );
+}
+
+/** Destroy a service.
+ *
+ * \public \memberof mlt_service_s
+ * \param this the service to destroy
+ */
+
+void mlt_service_close( mlt_service this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( this ) ) <= 0 )
+       {
+               if ( this->close != NULL )
+               {
+                       this->close( this->close_object );
+               }
+               else
+               {
+                       mlt_service_base *base = this->local;
+                       int i = 0;
+                       int count = base->filter_count;
+                       mlt_events_block( MLT_SERVICE_PROPERTIES( this ), this );
+                       while( count -- )
+                               mlt_service_detach( this, base->filters[ 0 ] );
+                       free( base->filters );
+                       for ( i = 0; i < base->count; i ++ )
+                               if ( base->in[ i ] != NULL )
+                                       mlt_service_close( base->in[ i ] );
+                       this->parent.close = NULL;
+                       free( base->in );
+                       pthread_mutex_destroy( &base->mutex );
+                       free( base );
+                       purge_cache( this );
+                       mlt_properties_close( &this->parent );
+               }
+       }
+       else
+       {
+               mlt_service_unlock( this );
+       }
+}
+
+/** Release a service's cache items.
+ *
+ * \private \memberof mlt_service_s
+ * \param self a service
+ */
+
+static void purge_cache( mlt_service self )
+{
+       mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
+
+       if ( caches )
+       {
+               int i = mlt_properties_count( caches );
+               while ( i-- )
+               {
+                       mlt_cache_purge( mlt_properties_get_data_at( caches, i, NULL ), self );
+                       mlt_properties_set_data( mlt_global_properties(), mlt_properties_get_name( caches, i ), NULL, 0, NULL, NULL );
+               }
+       }
+}
+
+/** Lookup the cache object for a service.
+ *
+ * \private \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object
+ * \return a cache
+ */
+
+static mlt_cache get_cache( mlt_service self, const char *name )
+{
+       mlt_cache result = NULL;
+       mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL );
+
+       if ( !caches )
+       {
+               caches = mlt_properties_new();
+               mlt_properties_set_data( mlt_global_properties(), "caches", caches, 0, ( mlt_destructor )mlt_properties_close, NULL );
+       }
+       if ( caches )
+       {
+               result = mlt_properties_get_data( caches, name, NULL );
+               if ( !result )
+               {
+                       result = mlt_cache_init();
+                       mlt_properties_set_data( caches, name, result, 0, ( mlt_destructor )mlt_cache_close, NULL );
+               }
+       }
+       
+       return result;
+}
+
+/** Put an object into a service's cache.
+ *
+ * \public \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object that is unique to the service class, but not to the instance
+ * \param data an opaque pointer to the object to put into the cache
+ * \param size the number of bytes pointed to by data
+ * \param destructor a function that releases the data
+ */
+
+void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor )
+{
+       mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data );
+       mlt_cache cache = get_cache( self, name );
+
+       if ( cache )
+               mlt_cache_put( cache, self, data, size, destructor );
+}
+
+/** Get an object from a service's cache.
+ *
+ * \public \memberof mlt_service_s
+ * \param self a service
+ * \param name a name for the object that is unique to the service class, but not to the instance
+ * \return a cache item or NULL if an object is not found
+ * \see mlt_cache_item_data
+ */
+
+mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name )
+{
+       mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self );
+       mlt_cache_item result = NULL;
+       mlt_cache cache = get_cache( self, name );
+
+       if ( cache )
+               result = mlt_cache_get( cache, self );
+
+       return result;
+}
diff --git a/src/framework/mlt_service.h b/src/framework/mlt_service.h
new file mode 100644 (file)
index 0000000..ea451bf
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * \file mlt_service.h
+ * \brief interface declaration for all service classes
+ * \see mlt_service_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_SERVICE_H_
+#define _MLT_SERVICE_H_
+
+#include "mlt_properties.h"
+#include "mlt_types.h"
+
+/** \brief Service abstract base class
+ *
+ * \extends mlt_properties
+ * The service is the base class for all of the interesting classes and
+ * plugins for MLT. A service can have multiple inputs connections to
+ * other services called its "producers" but only a single output to another
+ * service called its "consumer." A service that has both producer and
+ * consumer connections is called a filter. Any service can have zero or more
+ * filters "attached" to it. We call any collection of services and their
+ * connections a "service network," which is similar to what DirectShow calls
+ * a filter graph or what gstreamer calls an element pipeline.
+ *
+ * \event \em service-changed
+ * \event \em property-changed
+ * \properties \em mlt_type identifies the subclass
+ * \properties \em _mlt_service_hidden a flag that indicates whether to hide the mlt_service
+ * \properties \em mlt_service is the name of the implementation of the service
+ * \properties \em resource is either the stream identifier or grandchild-class
+ * \properties \em in when to start, what is started is service-specific
+ * \properties \em out when to stop
+ * \properties \em _filter_private Set this on a service to ensure that attached filters are handled privately.
+ * See modules/core/filter_region.c and modules/core/filter_watermark.c for examples.
+ * \properties \em disable Set this on a filter to disable it while keeping it in the object model.
+ * \properties \em _profile stores the mlt_profile for a service
+ * \properties \em _unique_id is a unique identifier
+ */
+
+struct mlt_service_s
+{
+       struct mlt_properties_s parent; /**< \private */
+
+       /** Get a frame of data (virtual function).
+        *
+        * \param mlt_producer a producer
+        * \param mlt_frame_ptr a frame pointer by reference
+        * \param int an index
+        * \return true if there was an error
+        */
+       int ( *get_frame )( mlt_service self, mlt_frame_ptr frame, int index );
+
+       /** the destructor virtual function */
+       mlt_destructor close;
+       void *close_object; /**< the object supplied to the close virtual function */
+
+       void *local; /**< \private instance object */
+       void *child; /**< \private the object of a subclass */
+};
+
+#define MLT_SERVICE_PROPERTIES( service )      ( &( service )->parent )
+
+extern int mlt_service_init( mlt_service self, void *child );
+extern void mlt_service_lock( mlt_service self );
+extern void mlt_service_unlock( mlt_service self );
+extern mlt_service_type mlt_service_identify( mlt_service self );
+extern int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index );
+extern mlt_service mlt_service_get_producer( mlt_service self );
+extern int mlt_service_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
+extern mlt_properties mlt_service_properties( mlt_service self );
+extern mlt_service mlt_service_consumer( mlt_service self );
+extern mlt_service mlt_service_producer( mlt_service self );
+extern int mlt_service_attach( mlt_service self, mlt_filter filter );
+extern int mlt_service_detach( mlt_service self, mlt_filter filter );
+extern void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index );
+extern mlt_filter mlt_service_filter( mlt_service self, int index );
+extern mlt_profile mlt_service_profile( mlt_service self );
+extern void mlt_service_close( mlt_service self );
+extern void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor );
+extern mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name );
+
+#endif
+
diff --git a/src/framework/mlt_tokeniser.c b/src/framework/mlt_tokeniser.c
new file mode 100644 (file)
index 0000000..1fa28b0
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+ * \file mlt_tokeniser.c
+ * \brief string tokeniser
+ * \see mlt_tokeniser_s
+ *
+ * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "mlt_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+mlt_tokeniser mlt_tokeniser_init( )
+{
+       return calloc( 1, sizeof( mlt_tokeniser_t ) );
+}
+
+/** Clear the tokeniser.
+*/
+
+static void mlt_tokeniser_clear( mlt_tokeniser tokeniser )
+{
+       int index = 0;
+       for ( index = 0; index < tokeniser->count; index ++ )
+               free( tokeniser->tokens[ index ] );
+       tokeniser->count = 0;
+       free( tokeniser->input );
+       tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int mlt_tokeniser_append( mlt_tokeniser tokeniser, char *token )
+{
+       int error = 0;
+
+       if ( tokeniser->count == tokeniser->size )
+       {
+               tokeniser->size += 20;
+               tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+       }
+
+       if ( tokeniser->tokens != NULL )
+       {
+               tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+       }
+       else
+       {
+               tokeniser->count = 0;
+               error = -1;
+       }
+       return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int mlt_tokeniser_parse_new( mlt_tokeniser tokeniser, char *string, const char *delimiter )
+{
+       int count = 0;
+       int length = strlen( string );
+       int delimiter_size = strlen( delimiter );
+       int index = 0;
+       char *token = strdup( string );
+
+       mlt_tokeniser_clear( tokeniser );
+       tokeniser->input = strdup( string );
+       strcpy( token, "" );
+
+       for ( index = 0; index < length; )
+       {
+               char *start = string + index;
+               char *end = strstr( start, delimiter );
+
+               if ( end == NULL )
+               {
+                       strcat( token, start );
+                       mlt_tokeniser_append( tokeniser, token );
+                       index = length;
+                       count ++;
+               }
+               else if ( start != end )
+               {
+                       strncat( token, start, end - start );
+                       index += end - start;
+                       if ( strchr( token, '\"' ) == NULL || token[ strlen( token ) - 1 ] == '\"' )
+                       {
+                               mlt_tokeniser_append( tokeniser, token );
+                               strcpy( token, "" );
+                               count ++;
+                       }
+                       else while ( strncmp( string + index, delimiter, delimiter_size ) == 0 )
+                       {
+                               strncat( token, delimiter, delimiter_size );
+                               index += delimiter_size;
+                       }
+               }
+               else
+               {
+                       index += strlen( delimiter );
+               }
+       }
+
+       /* Special case - malformed string condition */
+       if ( !strcmp( token, "" ) )
+       {
+               count = 0 - ( count - 1 );
+               mlt_tokeniser_append( tokeniser, token );
+       }
+
+       free( token );
+       return count;
+}
+
+/** Get the original input.
+*/
+
+char *mlt_tokeniser_get_input( mlt_tokeniser tokeniser )
+{
+       return tokeniser->input;
+}
+
+/** Get the number of tokens.
+*/
+
+int mlt_tokeniser_count( mlt_tokeniser tokeniser )
+{
+       return tokeniser->count;
+}
+
+/** Get a token as a string.
+*/
+
+char *mlt_tokeniser_get_string( mlt_tokeniser tokeniser, int index )
+{
+       if ( index < tokeniser->count )
+               return tokeniser->tokens[ index ];
+       else
+               return NULL;
+}
+
+/** Close the tokeniser.
+*/
+
+void mlt_tokeniser_close( mlt_tokeniser tokeniser )
+{
+       mlt_tokeniser_clear( tokeniser );
+       free( tokeniser->tokens );
+       free( tokeniser );
+}
diff --git a/src/framework/mlt_tokeniser.h b/src/framework/mlt_tokeniser.h
new file mode 100644 (file)
index 0000000..d4715af
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * \file mlt_tokeniser.h
+ * \brief string tokeniser
+ * \see mlt_tokeniser_s
+ *
+ * Copyright (C) 2002-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_TOKENISER_H_
+#define _MLT_TOKENISER_H_
+
+/** \brief Tokeniser class
+ *
+ */
+
+typedef struct
+{
+       char *input;
+       char **tokens;
+       int count;
+       int size;
+}
+*mlt_tokeniser, mlt_tokeniser_t;
+
+/* Remote parser API.
+*/
+
+extern mlt_tokeniser mlt_tokeniser_init( );
+extern int mlt_tokeniser_parse_new( mlt_tokeniser self, char *text, const char *delimiter );
+extern char *mlt_tokeniser_get_input( mlt_tokeniser self );
+extern int mlt_tokeniser_count( mlt_tokeniser self );
+extern char *mlt_tokeniser_get_string( mlt_tokeniser self, int index );
+extern void mlt_tokeniser_close( mlt_tokeniser self );
+
+#endif
diff --git a/src/framework/mlt_tractor.c b/src/framework/mlt_tractor.c
new file mode 100644 (file)
index 0000000..40c7821
--- /dev/null
@@ -0,0 +1,540 @@
+/**
+ * \file mlt_tractor.c
+ * \brief tractor service class
+ * \see mlt_tractor_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_tractor.h"
+#include "mlt_frame.h"
+#include "mlt_multitrack.h"
+#include "mlt_field.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Forward references to static methods.
+*/
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track );
+static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this );
+
+/** Construct a tractor without a field or multitrack.
+ *
+ * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
+ * and mlt_service to "tractor".
+ *
+ * \public \memberof mlt_tractor_s
+ * \return the new tractor
+ */
+
+mlt_tractor mlt_tractor_init( )
+{
+       mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
+       if ( this != NULL )
+       {
+               mlt_producer producer = &this->parent;
+               if ( mlt_producer_init( producer, this ) == 0 )
+               {
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+                       mlt_properties_set( properties, "resource", "<tractor>" );
+                       mlt_properties_set( properties, "mlt_type", "mlt_producer" );
+                       mlt_properties_set( properties, "mlt_service", "tractor" );
+                       mlt_properties_set_int( properties, "in", 0 );
+                       mlt_properties_set_int( properties, "out", -1 );
+                       mlt_properties_set_int( properties, "length", 0 );
+
+                       producer->get_frame = producer_get_frame;
+                       producer->close = ( mlt_destructor )mlt_tractor_close;
+                       producer->close_object = this;
+               }
+               else
+               {
+                       free( this );
+                       this = NULL;
+               }
+       }
+       return this;
+}
+
+/** Construct a tractor as well as a field and multitrack.
+ *
+ * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
+ * and mlt_service to "tractor".
+ *
+ * \public \memberof mlt_tractor_s
+ * \return the new tractor
+ */
+
+mlt_tractor mlt_tractor_new( )
+{
+       mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
+       if ( this != NULL )
+       {
+               mlt_producer producer = &this->parent;
+               if ( mlt_producer_init( producer, this ) == 0 )
+               {
+                       mlt_multitrack multitrack = mlt_multitrack_init( );
+                       mlt_field field = mlt_field_new( multitrack, this );
+                       mlt_properties props = MLT_PRODUCER_PROPERTIES( producer );
+
+                       mlt_properties_set( props, "resource", "<tractor>" );
+                       mlt_properties_set( props, "mlt_type", "mlt_producer" );
+                       mlt_properties_set( props, "mlt_service", "tractor" );
+                       mlt_properties_set_position( props, "in", 0 );
+                       mlt_properties_set_position( props, "out", 0 );
+                       mlt_properties_set_position( props, "length", 0 );
+                       mlt_properties_set_data( props, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
+                       mlt_properties_set_data( props, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
+
+                       mlt_events_listen( MLT_MULTITRACK_PROPERTIES( multitrack ), this, "producer-changed", ( mlt_listener )mlt_tractor_listener );
+
+                       producer->get_frame = producer_get_frame;
+                       producer->close = ( mlt_destructor )mlt_tractor_close;
+                       producer->close_object = this;
+               }
+               else
+               {
+                       free( this );
+                       this = NULL;
+               }
+       }
+       return this;
+}
+
+/** Get the service object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the parent service object
+ * \see MLT_TRACTOR_SERVICE
+ */
+
+mlt_service mlt_tractor_service( mlt_tractor this )
+{
+       return MLT_PRODUCER_SERVICE( &this->parent );
+}
+
+/** Get the producer object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the parent producer object
+ * \see MLT_TRACTOR_PRODUCER
+ */
+
+mlt_producer mlt_tractor_producer( mlt_tractor this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the properties object associated to the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return the tractor's property list
+ * \see MLT_TRACTOR_PROPERTIES
+ */
+
+mlt_properties mlt_tractor_properties( mlt_tractor this )
+{
+       return MLT_PRODUCER_PROPERTIES( &this->parent );
+}
+
+/** Get the field this tractor is harvesting.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return a field or NULL if there is no field for this tractor
+ */
+
+mlt_field mlt_tractor_field( mlt_tractor this )
+{
+       return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "field", NULL );
+}
+
+/** Get the multitrack this tractor is pulling.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \return a multitrack or NULL if there is none
+ */
+
+mlt_multitrack mlt_tractor_multitrack( mlt_tractor this )
+{
+       return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "multitrack", NULL );
+}
+
+/** Ensure the tractors in/out points match the multitrack.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ */
+
+void mlt_tractor_refresh( mlt_tractor this )
+{
+       mlt_multitrack multitrack = mlt_tractor_multitrack( this );
+       mlt_properties properties = MLT_MULTITRACK_PROPERTIES( multitrack );
+       mlt_properties self = MLT_TRACTOR_PROPERTIES( this );
+       mlt_events_block( properties, self );
+       mlt_events_block( self, self );
+       mlt_multitrack_refresh( multitrack );
+       mlt_properties_set_position( self, "in", 0 );
+       mlt_properties_set_position( self, "out", mlt_properties_get_position( properties, "out" ) );
+       mlt_events_unblock( self, self );
+       mlt_events_unblock( properties, self );
+       mlt_properties_set_position( self, "length", mlt_properties_get_position( properties, "length" ) );
+}
+
+static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this )
+{
+       mlt_tractor_refresh( this );
+}
+
+/** Connect the tractor.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param producer a producer
+ * \return true on error
+ */
+
+int mlt_tractor_connect( mlt_tractor this, mlt_service producer )
+{
+       int ret = mlt_service_connect_producer( MLT_TRACTOR_SERVICE( this ), producer, 0 );
+
+       // This is the producer we're going to connect to
+       if ( ret == 0 )
+               this->producer = producer;
+
+       return ret;
+}
+
+/** Set the producer for a specific track.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param producer a producer
+ * \param index the 0-based track index
+ * \return true on error
+ */
+
+int mlt_tractor_set_track( mlt_tractor this, mlt_producer producer, int index )
+{
+       return mlt_multitrack_connect( mlt_tractor_multitrack( this ), producer, index );
+}
+
+/** Get the producer for a specific track.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ * \param index the 0-based track index
+ * \return the producer for track \p index
+ */
+
+mlt_producer mlt_tractor_get_track( mlt_tractor this, int index )
+{
+       return mlt_multitrack_track( mlt_tractor_multitrack( this ), index );
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       uint8_t *data = NULL;
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       mlt_frame frame = mlt_frame_pop_service( this );
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+       mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) );
+       mlt_properties_set_int( frame_properties, "resize_alpha", mlt_properties_get_int( properties, "resize_alpha" ) );
+       mlt_properties_set_int( frame_properties, "distort", mlt_properties_get_int( properties, "distort" ) );
+       mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+       mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "consumer_deinterlace" ) );
+       mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
+       mlt_properties_set_int( frame_properties, "normalised_width", mlt_properties_get_int( properties, "normalised_width" ) );
+       mlt_properties_set_int( frame_properties, "normalised_height", mlt_properties_get_int( properties, "normalised_height" ) );
+       mlt_frame_get_image( frame, buffer, format, width, height, writable );
+       mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, NULL, NULL );
+       mlt_properties_set_int( properties, "width", *width );
+       mlt_properties_set_int( properties, "height", *height );
+       mlt_properties_set_int( properties, "format", *format );
+       mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( frame ) );
+       mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( frame_properties, "progressive" ) );
+       mlt_properties_set_int( properties, "distort", mlt_properties_get_int( frame_properties, "distort" ) );
+       data = mlt_frame_get_alpha_mask( frame );
+       mlt_properties_set_data( properties, "alpha", data, 0, NULL, NULL );
+       return 0;
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       mlt_frame frame = mlt_frame_pop_audio( this );
+       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+       mlt_properties_set_data( properties, "audio", *buffer, 0, NULL, NULL );
+       mlt_properties_set_int( properties, "frequency", *frequency );
+       mlt_properties_set_int( properties, "channels", *channels );
+       return 0;
+}
+
+static void destroy_data_queue( void *arg )
+{
+       if ( arg != NULL )
+       {
+               // Assign the correct type
+               mlt_deque queue = arg;
+
+               // Iterate through each item and destroy them
+               while ( mlt_deque_peek_front( queue ) != NULL )
+                       mlt_properties_close( mlt_deque_pop_back( queue ) );
+
+               // Close the deque
+               mlt_deque_close( queue );
+       }
+}
+
+/** Get the next frame.
+ *
+ * \private \memberof mlt_tractor_s
+ * \param parent the producer interface to the tractor
+ * \param[out] frame a frame by reference
+ * \param track the 0-based track index
+ * \return true on error
+ */
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track )
+{
+       mlt_tractor this = parent->child;
+
+       // We only respond to the first track requests
+       if ( track == 0 && this->producer != NULL )
+       {
+               int i = 0;
+               int done = 0;
+               mlt_frame temp = NULL;
+               int count = 0;
+               int image_count = 0;
+
+               // Get the properties of the parent producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent );
+
+               // Try to obtain the multitrack associated to the tractor
+               mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
+
+               // Or a specific producer
+               mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+               // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame)
+               mlt_deque data_queue = mlt_deque_init( );
+
+               // Determine whether this tractor feeds to the consumer or stops here
+               int global_feed = mlt_properties_get_int( properties, "global_feed" );
+
+               // If we don't have one, we're in trouble...
+               if ( multitrack != NULL )
+               {
+                       // Used to garbage collect all frames
+                       char label[ 30 ];
+
+                       // Get the id of the tractor
+                       char *id = mlt_properties_get( properties, "_unique_id" );
+
+                       // Will be used to store the frame properties object
+                       mlt_properties frame_properties = NULL;
+
+                       // We'll store audio and video frames to use here
+                       mlt_frame audio = NULL;
+                       mlt_frame video = NULL;
+                       mlt_frame first_video = NULL;
+
+                       // Temporary properties
+                       mlt_properties temp_properties = NULL;
+
+                       // Get the multitrack's producer
+                       mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack );
+                       mlt_producer_seek( target, mlt_producer_frame( parent ) );
+                       mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) );
+
+                       // We will create one frame and attach everything to it
+                       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+
+                       // Get the properties of the frame
+                       frame_properties = MLT_FRAME_PROPERTIES( *frame );
+
+                       // Loop through each of the tracks we're harvesting
+                       for ( i = 0; !done; i ++ )
+                       {
+                               // Get a frame from the producer
+                               mlt_service_get_frame( this->producer, &temp, i );
+
+                               // Get the temporary properties
+                               temp_properties = MLT_FRAME_PROPERTIES( temp );
+
+                               // Check for last track
+                               done = mlt_properties_get_int( temp_properties, "last_track" );
+
+                               // Handle fx only tracks
+                               if ( mlt_properties_get_int( temp_properties, "fx_cut" ) )
+                               {
+                                       int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 );
+                                       mlt_properties_set_int( temp_properties, "hide", hide );
+                               }
+
+                               // We store all frames with a destructor on the output frame
+                               sprintf( label, "_%s_%d", id, count ++ );
+                               mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL );
+
+                               // We want to append all 'final' feeds to the global queue
+                               if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL )
+                               {
+                                       // Move the contents of this queue on to the output frames data queue
+                                       mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL );
+                                       mlt_deque temp = mlt_deque_init( );
+                                       while ( global_feed && mlt_deque_count( sub_queue ) )
+                                       {
+                                               mlt_properties p = mlt_deque_pop_back( sub_queue );
+                                               if ( mlt_properties_get_int( p, "final" ) )
+                                                       mlt_deque_push_back( data_queue, p );
+                                               else
+                                                       mlt_deque_push_back( temp, p );
+                                       }
+                                       while( mlt_deque_count( temp ) )
+                                               mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) );
+                                       mlt_deque_close( temp );
+                               }
+
+                               // Now do the same with the global queue but without the conditional behaviour
+                               if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL )
+                               {
+                                       mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL );
+                                       while ( mlt_deque_count( sub_queue ) )
+                                       {
+                                               mlt_properties p = mlt_deque_pop_back( sub_queue );
+                                               mlt_deque_push_back( data_queue, p );
+                                       }
+                               }
+
+                               // Pick up first video and audio frames
+                               if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) )
+                               {
+                                       // Order of frame creation is starting to get problematic
+                                       if ( audio != NULL )
+                                       {
+                                               mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio );
+                                               mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio );
+                                       }
+                                       audio = temp;
+                               }
+                               if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) )
+                               {
+                                       if ( video != NULL )
+                                       {
+                                               mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image );
+                                               mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video );
+                                       }
+                                       video = temp;
+                                       if ( first_video == NULL )
+                                               first_video = temp;
+
+                                       // Ensure that all frames know the aspect ratio of the background
+                                       mlt_properties_set_double( temp_properties, "output_ratio",
+                                                                                          mlt_properties_get_double( MLT_FRAME_PROPERTIES( first_video ), "aspect_ratio" ) );
+
+                                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count );
+                                       image_count = 1;
+                               }
+                       }
+
+                       // Now stack callbacks
+                       if ( audio != NULL )
+                       {
+                               mlt_frame_push_audio( *frame, audio );
+                               mlt_frame_push_audio( *frame, producer_get_audio );
+                       }
+
+                       if ( video != NULL )
+                       {
+                               mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video );
+                               mlt_frame_push_service( *frame, video );
+                               mlt_frame_push_service( *frame, producer_get_image );
+                               if ( global_feed )
+                                       mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL );
+                               mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL );
+                               mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) );
+                               mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) );
+                               mlt_properties_set_int( frame_properties, "real_width", mlt_properties_get_int( video_properties, "real_width" ) );
+                               mlt_properties_set_int( frame_properties, "real_height", mlt_properties_get_int( video_properties, "real_height" ) );
+                               mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) );
+                               mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) );
+                               mlt_properties_set_int( frame_properties, "image_count", image_count );
+                       }
+                       else
+                       {
+                               destroy_data_queue( data_queue );
+                       }
+
+                       mlt_frame_set_position( *frame, mlt_producer_frame( parent ) );
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL );
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL );
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "consumer_lock_service", this, 0, NULL, NULL );
+               }
+               else if ( producer != NULL )
+               {
+                       mlt_producer_seek( producer, mlt_producer_frame( parent ) );
+                       mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) );
+                       mlt_service_get_frame( this->producer, frame, track );
+               }
+               else
+               {
+                       mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" );
+                       mlt_service_get_frame( this->producer, frame, track );
+               }
+
+               // Prepare the next frame
+               mlt_producer_prepare_next( parent );
+
+               // Indicate our found status
+               return 0;
+       }
+       else
+       {
+               // Generate a test card
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
+               return 0;
+       }
+}
+
+/** Close the tractor and free its resources.
+ *
+ * \public \memberof mlt_tractor_s
+ * \param this a tractor
+ */
+
+void mlt_tractor_close( mlt_tractor this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_TRACTOR_PROPERTIES( this ) ) <= 0 )
+       {
+               this->parent.close = NULL;
+               mlt_producer_close( &this->parent );
+               free( this );
+       }
+}
+
diff --git a/src/framework/mlt_tractor.h b/src/framework/mlt_tractor.h
new file mode 100644 (file)
index 0000000..73e0715
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * \file mlt_tractor.h
+ * \brief tractor service class
+ * \see mlt_tractor_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_TRACTOR_H_
+#define _MLT_TRACTOR_H_
+
+#include "mlt_producer.h"
+
+/** \brief Tractor class
+ *
+ * The tractor is a convenience class that works with the field class
+ * to manage a multitrack, track filters, and transitions.
+ *
+ * \extends mlt_producer_s
+ * \properties \em multitrack holds a reference to the mulitrack object that a tractor manages
+ * \properties \em field holds a reference to the field object that a tractor manages
+ * \properties \em producer holds a reference to an encapsulated producer
+ * \properties \em global_feed a flag to indicate whether this tractor feeds to the consumer or stops here
+ * \properties \em global_queue is something for the data_feed functionality in the core module
+ * \properties \em data_queue is something for the data_feed functionality in the core module
+ */
+
+struct mlt_tractor_s
+{
+       struct mlt_producer_s parent;
+       mlt_service producer;
+};
+
+#define MLT_TRACTOR_PRODUCER( tractor )                ( &( tractor )->parent )
+#define MLT_TRACTOR_SERVICE( tractor )         MLT_PRODUCER_SERVICE( MLT_TRACTOR_PRODUCER( tractor ) )
+#define MLT_TRACTOR_PROPERTIES( tractor )      MLT_SERVICE_PROPERTIES( MLT_TRACTOR_SERVICE( tractor ) )
+
+extern mlt_tractor mlt_tractor_init( );
+extern mlt_tractor mlt_tractor_new( );
+extern mlt_service mlt_tractor_service( mlt_tractor self );
+extern mlt_producer mlt_tractor_producer( mlt_tractor self );
+extern mlt_properties mlt_tractor_properties( mlt_tractor self );
+extern mlt_field mlt_tractor_field( mlt_tractor self );
+extern mlt_multitrack mlt_tractor_multitrack( mlt_tractor self );
+extern int mlt_tractor_connect( mlt_tractor self, mlt_service service );
+extern void mlt_tractor_refresh( mlt_tractor self );
+extern int mlt_tractor_set_track( mlt_tractor self, mlt_producer producer, int index );
+extern mlt_producer mlt_tractor_get_track( mlt_tractor self, int index );
+extern void mlt_tractor_close( mlt_tractor self );
+
+#endif
diff --git a/src/framework/mlt_transition.c b/src/framework/mlt_transition.c
new file mode 100644 (file)
index 0000000..5077795
--- /dev/null
@@ -0,0 +1,388 @@
+/**
+ * \file mlt_transition.c
+ * \brief abstraction for all transition services
+ * \see mlt_transition_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "mlt_transition.h"
+#include "mlt_frame.h"
+#include "mlt_log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Forward references */
+
+static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** Initialize a new transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param child the object of a subclass
+ * \return true on error
+ */
+
+int mlt_transition_init( mlt_transition this, void *child )
+{
+       mlt_service service = &this->parent;
+       memset( this, 0, sizeof( struct mlt_transition_s ) );
+       this->child = child;
+       if ( mlt_service_init( service, this ) == 0 )
+       {
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+               service->get_frame = transition_get_frame;
+               service->close = ( mlt_destructor )mlt_transition_close;
+               service->close_object = this;
+
+               mlt_properties_set_position( properties, "in", 0 );
+               mlt_properties_set_position( properties, "out", 0 );
+               mlt_properties_set_int( properties, "a_track", 0 );
+               mlt_properties_set_int( properties, "b_track", 1 );
+
+               return 0;
+       }
+       return 1;
+}
+
+/** Create and initialize a new transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \return a new transition
+ */
+
+mlt_transition mlt_transition_new( )
+{
+       mlt_transition this = calloc( 1, sizeof( struct mlt_transition_s ) );
+       if ( this != NULL )
+               mlt_transition_init( this, NULL );
+       return this;
+}
+
+/** Get the service class interface.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the service class
+ * \see MLT_TRANSITION_SERVICE
+ */
+
+mlt_service mlt_transition_service( mlt_transition this )
+{
+       return this != NULL ? &this->parent : NULL;
+}
+
+/** Get the properties interface.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the transition's properties
+ * \see MLT_TRANSITION_PROPERTIES
+ */
+
+mlt_properties mlt_transition_properties( mlt_transition this )
+{
+       return MLT_TRANSITION_PROPERTIES( this );
+}
+
+/** Connect this transition with a producers a and b tracks.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param producer a producer
+ * \param a_track the track index of the first input
+ * \param b_track the track index of the second index
+ * \return true on error
+ */
+
+int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track )
+{
+       int ret = mlt_service_connect_producer( &this->parent, producer, a_track );
+       if ( ret == 0 )
+       {
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+               this->producer = producer;
+               mlt_properties_set_int( properties, "a_track", a_track );
+               mlt_properties_set_int( properties, "b_track", b_track );
+       }
+       return ret;
+}
+
+/** Set the starting and ending time for when the transition is active.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param in the starting time
+ * \param out the ending time
+ */
+
+void mlt_transition_set_in_and_out( mlt_transition this, mlt_position in, mlt_position out )
+{
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       mlt_properties_set_position( properties, "in", in );
+       mlt_properties_set_position( properties, "out", out );
+}
+
+/** Get the index of the a track.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the 0-based index of the track of the first producer
+ */
+
+int mlt_transition_get_a_track( mlt_transition this )
+{
+       return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "a_track" );
+}
+
+/** Get the index of the b track.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the 0-based index of the track of the second producer
+ */
+
+int mlt_transition_get_b_track( mlt_transition this )
+{
+       return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "b_track" );
+}
+
+/** Get the in point.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the starting time
+ */
+
+mlt_position mlt_transition_get_in( mlt_transition this )
+{
+       return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( this ), "in" );
+}
+
+/** Get the out point.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \return the ending time
+ */
+
+mlt_position mlt_transition_get_out( mlt_transition this )
+{
+       return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( this ), "out" );
+}
+
+/** Process the frame.
+ *
+ * If we have no process method (unlikely), we simply return the a_frame unmolested.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ * \param a_frame a frame from the first producer
+ * \param b_frame a frame from the second producer
+ * \return a frame
+ */
+
+mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+       if ( this->process == NULL )
+               return a_frame;
+       else
+               return this->process( this, a_frame, b_frame );
+}
+
+/** Get a frame from this transition.
+
+       The logic is complex here. A transition is typically applied to frames on the a and
+       b tracks specified in the connect method above and only if both contain valid info
+       for the transition type (this is either audio or image).
+
+       However, the fixed a_track may not always contain data of the correct type, eg:
+<pre>
+       +---------+                               +-------+
+       |c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
+       +---------+                     +---------+-+-----+        |          |
+                                       |c4         |       <------+          |
+                +----------+-----------+-+---------+                         |
+                |c2        |c3           |                 <-----------------+
+                +----------+-------------+
+</pre>
+       During the overlap of c1 and c2, there is nothing for the A transition to do, so this
+       results in a no operation, but B is triggered. During the overlap of c2 and c3, again,
+       the A transition is inactive and because the B transition is pointing at track 0,
+       it too would be inactive. This isn't an ideal situation - it's better if the B
+       transition simply treats the frames from c3 as though they're the a track.
+
+       For this to work, we cache all frames coming from all tracks between the a and b
+       tracks.  Before we process, we determine that the b frame contains someting of the
+       right type and then we determine which frame to use as the a frame (selecting a
+       matching frame from a_track to b_track - 1). If both frames contain data of the
+       correct type, we process the transition.
+
+       This method is invoked for each track and we return the cached frames as needed.
+       We clear the cache only when the requested frame is flagged as a 'last_track' frame.
+
+ * \private \memberof mlt_transition_s
+ * \param service a service
+ * \param[out] frame a frame by reference
+ * \param index 0-based track index
+ * \return true on error
+ */
+
+static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
+{
+       int error = 0;
+       mlt_transition this = service->child;
+
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" );
+       int a_track = mlt_properties_get_int( properties, "a_track" );
+       int b_track = mlt_properties_get_int( properties, "b_track" );
+       mlt_position in = mlt_properties_get_position( properties, "in" );
+       mlt_position out = mlt_properties_get_position( properties, "out" );
+       int always_active = mlt_properties_get_int( properties, "always_active" );
+       int type = mlt_properties_get_int( properties, "_transition_type" );
+       int reverse_order = 0;
+
+       // Ensure that we have the correct order
+       if ( a_track > b_track )
+       {
+               reverse_order = 1;
+               a_track = b_track;
+               b_track = mlt_properties_get_int( properties, "a_track" );
+       }
+
+       // Only act on this operation once per multitrack iteration from the tractor
+       if ( !this->held )
+       {
+               int active = 0;
+               int i = 0;
+               int a_frame = a_track;
+               int b_frame = b_track;
+               mlt_position position;
+               int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio;
+
+               // Initialise temporary store
+               if ( this->frames == NULL )
+                       this->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
+
+               // Get all frames between a and b
+               for( i = a_track; i <= b_track; i ++ )
+                       mlt_service_get_frame( this->producer, &this->frames[ i ], i );
+
+               // We're holding these frames until the last_track frame property is received
+               this->held = 1;
+
+               // When we need to locate the a_frame
+               switch( type )
+               {
+                       case 1:
+                       case 2:
+                               // Some transitions (esp. audio) may accept blank frames
+                               active = accepts_blanks;
+
+                               // If we're not active then...
+                               if ( !active )
+                               {
+                                       // Hunt for the a_frame
+                                       while( a_frame <= b_frame && invalid( this->frames[ a_frame ] ) )
+                                               a_frame ++;
+
+                                       // Determine if we're active now
+                                       active = a_frame != b_frame && !invalid( this->frames[ b_frame ] );
+                               }
+                               break;
+
+                       default:
+                               mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
+                               break;
+               }
+
+               // Now handle the non-always active case
+               if ( active && !always_active )
+               {
+                       // For non-always-active transitions, we need the current position of the a frame
+                       position = mlt_frame_get_position( this->frames[ a_frame ] );
+
+                       // If a is in range, we're active
+                       active = position >= in && position <= out;
+               }
+
+               // Finally, process the a and b frames
+               if ( active )
+               {
+                       mlt_frame a_frame_ptr = this->frames[ !reverse_order ? a_frame : b_frame ];
+                       mlt_frame b_frame_ptr = this->frames[ !reverse_order ? b_frame : a_frame ];
+                       int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" );
+                       int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" );
+                       if ( !( a_hide & type ) && !( b_hide & type ) )
+                       {
+                               // Process the transition
+                               *frame = mlt_transition_process( this, a_frame_ptr, b_frame_ptr );
+
+                               // We need to ensure that the tractor doesn't consider this frame for output
+                               if ( *frame == a_frame_ptr )
+                                       b_hide |= type;
+                               else
+                                       a_hide |= type;
+
+                               mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide );
+                               mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide );
+                       }
+               }
+       }
+
+       // Obtain the frame from the cache or the producer we're attached to
+       if ( index >= a_track && index <= b_track )
+               *frame = this->frames[ index ];
+       else
+               error = mlt_service_get_frame( this->producer, frame, index );
+
+       // Determine if that was the last track
+       this->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
+
+       return error;
+}
+
+/** Close and destroy the transition.
+ *
+ * \public \memberof mlt_transition_s
+ * \param this a transition
+ */
+
+void mlt_transition_close( mlt_transition this )
+{
+       if ( this != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( this ) ) <= 0 )
+       {
+               this->parent.close = NULL;
+               if ( this->close != NULL )
+               {
+                       this->close( this );
+               }
+               else
+               {
+                       mlt_service_close( &this->parent );
+                       free( this->frames );
+                       free( this );
+               }
+       }
+}
diff --git a/src/framework/mlt_transition.h b/src/framework/mlt_transition.h
new file mode 100644 (file)
index 0000000..0f9879a
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * \file mlt_transition.h
+ * \brief abstraction for all transition services
+ * \see mlt_transition_s
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_TRANSITION_H_
+#define _MLT_TRANSITION_H_
+
+#include "mlt_service.h"
+
+/** \brief Transition abstract service class
+ *
+ * A transition may modify the output of a producer based on the output of a second producer.
+ *
+ * \extends mlt_service_s
+ * \properties \em a_track the track index (0-based) of a multitrack of the first producer
+ * \properties \em b_track the track index (0-based) of a multitrack of the second producer
+ * \properties \em accepts_blanks a flag to indicate if the transition should accept blank frames
+ * \properties \em always_active a flag to indicate that the in and out points do not apply
+ * \properties \em _transition_type 1 for video, 2 for audio
+ */
+
+struct mlt_transition_s
+{
+       /** We're implementing service here */
+       struct mlt_service_s parent;
+
+       /** public virtual */
+       void ( *close )( mlt_transition );
+
+       /** protected transition method */
+       mlt_frame ( *process )( mlt_transition, mlt_frame, mlt_frame );
+
+       /** Protected */
+       void *child;
+
+       /** track and in/out points */
+       mlt_service producer;
+
+       /** Private */
+       mlt_frame *frames;
+       int held;
+};
+
+#define MLT_TRANSITION_SERVICE( transition )           ( &( transition )->parent )
+#define MLT_TRANSITION_PROPERTIES( transition )                MLT_SERVICE_PROPERTIES( MLT_TRANSITION_SERVICE( transition ) )
+
+extern int mlt_transition_init( mlt_transition self, void *child );
+extern mlt_transition mlt_transition_new( );
+extern mlt_service mlt_transition_service( mlt_transition self );
+extern mlt_properties mlt_transition_properties( mlt_transition self );
+extern int mlt_transition_connect( mlt_transition self, mlt_service producer, int a_track, int b_track );
+extern void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out );
+extern int mlt_transition_get_a_track( mlt_transition self );
+extern int mlt_transition_get_b_track( mlt_transition self );
+extern mlt_position mlt_transition_get_in( mlt_transition self );
+extern mlt_position mlt_transition_get_out( mlt_transition self );
+extern mlt_frame mlt_transition_process( mlt_transition self, mlt_frame a_frame, mlt_frame b_frame );
+extern void mlt_transition_close( mlt_transition self );
+
+#endif
diff --git a/src/framework/mlt_types.h b/src/framework/mlt_types.h
new file mode 100644 (file)
index 0000000..921dd47
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * \file mlt_types.h
+ * \brief Provides forward definitions of all public types
+ *
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _MLT_TYPES_H_
+#define _MLT_TYPES_H_
+
+#ifndef GCC_VERSION
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+
+#include <stdint.h>
+
+#include "mlt_pool.h"
+
+/** The set of supported image formats */
+
+typedef enum
+{
+       mlt_image_none = 0,/**< image not available */
+       mlt_image_rgb24,   /**< 8-bit RGB */
+       mlt_image_rgb24a,  /**< 8-bit RGB with alpha channel */
+       mlt_image_yuv422,  /**< 8-bit YUV 4:2:2 packed */
+       mlt_image_yuv420p, /**< 8-bit YUV 4:2:0 planar */
+       mlt_image_opengl   /**< suitable for OpenGL texture */
+}
+mlt_image_format;
+
+/** The set of supported audio formats */
+
+typedef enum
+{
+       mlt_audio_none = 0,/**< audio not available */
+       mlt_audio_pcm      /**< signed 16-bit interleaved PCM */
+}
+mlt_audio_format;
+
+/** The relative time qualifiers */
+
+typedef enum
+{
+       mlt_whence_relative_start,  /**< relative to the beginning */
+       mlt_whence_relative_current,/**< relative to the current position */
+       mlt_whence_relative_end     /**< relative to the end */
+}
+mlt_whence;
+
+/** The recognized subclasses of mlt_service */
+
+typedef enum
+{
+       invalid_type,               /**< invalid service */
+       unknown_type,               /**< unknown class */
+       producer_type,              /**< Producer class */
+       tractor_type,               /**< Tractor class */
+       playlist_type,              /**< Playlist class */
+       multitrack_type,            /**< Multitrack class */
+       filter_type,                /**< Filter class */
+       transition_type,            /**< Transition class */
+       consumer_type,              /**< Consumer class */
+       field_type                  /**< Field class */
+}
+mlt_service_type;
+
+/* I don't want to break anyone's applications without warning. -Zach */
+#undef DOUBLE_MLT_POSITION
+#ifdef DOUBLE_MLT_POSITION
+typedef double mlt_position;
+#else
+typedef int32_t mlt_position;
+#endif
+
+typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; /**< pointer to Frame object */
+typedef struct mlt_property_s *mlt_property;            /**< pointer to Property object */
+typedef struct mlt_properties_s *mlt_properties;        /**< pointer to Properties object */
+typedef struct mlt_event_struct *mlt_event;             /**< pointer to Event object */
+typedef struct mlt_service_s *mlt_service;              /**< pointer to Service object */
+typedef struct mlt_producer_s *mlt_producer;            /**< pointer to Producer object */
+typedef struct mlt_playlist_s *mlt_playlist;            /**< pointer to Playlist object */
+typedef struct mlt_multitrack_s *mlt_multitrack;        /**< pointer to Multitrack object */
+typedef struct mlt_filter_s *mlt_filter;                /**< pointer to Filter object */
+typedef struct mlt_transition_s *mlt_transition;        /**< pointer to Transition object */
+typedef struct mlt_tractor_s *mlt_tractor;              /**< pointer to Tractor object */
+typedef struct mlt_field_s *mlt_field;                  /**< pointer to Field object */
+typedef struct mlt_consumer_s *mlt_consumer;            /**< pointer to Consumer object */
+typedef struct mlt_parser_s *mlt_parser;                /**< pointer to Properties object */
+typedef struct mlt_deque_s *mlt_deque;                  /**< pointer to Deque object */
+typedef struct mlt_geometry_s *mlt_geometry;            /**< pointer to Geometry object */
+typedef struct mlt_geometry_item_s *mlt_geometry_item;  /**< pointer to Geometry Item object */
+typedef struct mlt_profile_s *mlt_profile;              /**< pointer to Profile object */
+typedef struct mlt_repository_s *mlt_repository;        /**< pointer to Repository object */
+typedef struct mlt_cache_s *mlt_cache;                  /**< pointer to Cache object */
+typedef struct mlt_cache_item_s *mlt_cache_item;        /**< pointer to CacheItem object */
+
+typedef void ( *mlt_destructor )( void * );             /**< pointer to destructor function */
+typedef char *( *mlt_serialiser )( void *, int length );/**< pointer to serialization function */
+
+#define MLT_SERVICE(x) ( ( mlt_service )( x ) )         /**< Cast to a Service pointer */
+#define MLT_PRODUCER(x) ( ( mlt_producer )( x ) )       /**< Cast to a Producer pointer */
+#define MLT_MULTITRACK(x) ( ( mlt_multitrack )( x ) )   /**< Cast to a Multitrack pointer */
+#define MLT_PLAYLIST(x) ( ( mlt_playlist )( x ) )       /**< Cast to a Playlist pointer */
+#define MLT_TRACTOR(x) ( ( mlt_tractor )( x ) )         /**< Cast to a Tractor pointer */
+#define MLT_FILTER(x) ( ( mlt_filter )( x ) )           /**< Cast to a Filter pointer */
+#define MLT_TRANSITION(x) ( ( mlt_transition )( x ) )   /**< Cast to a Transition pointer */
+
+#endif
diff --git a/src/humperdink/Makefile b/src/humperdink/Makefile
new file mode 100644 (file)
index 0000000..dd03dc9
--- /dev/null
@@ -0,0 +1,39 @@
+include ../../config.mak
+
+TARGET = humperdink
+
+OBJS = client.o \
+          io.o \
+          remote.o
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install:       all
+       install -d "$(DESTDIR)$(bindir)"
+       install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+       rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/humperdink/client.c b/src/humperdink/client.c
new file mode 100644 (file)
index 0000000..afd2bbd
--- /dev/null
@@ -0,0 +1,1025 @@
+/*
+ * client.c -- Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "client.h"
+#include "io.h"
+
+/** Clip navigation enumeration.
+*/
+
+typedef enum
+{
+       absolute,
+       relative
+}
+dv_demo_whence;
+
+/** Function prototype for menu handling. 
+*/
+
+typedef valerie_error_code (*demo_function)( dv_demo );
+
+/** The menu structure. 
+*/
+
+typedef struct
+{
+       const char *description;
+       struct menu_item
+       {
+               const char *option;
+               demo_function function;
+       }
+       array[ 50 ];
+}
+*dv_demo_menu, dv_demo_menu_t;
+
+/** Forward reference to menu runner.
+*/
+
+extern valerie_error_code dv_demo_run_menu( dv_demo, dv_demo_menu );
+
+/** Foward references. 
+*/
+
+extern valerie_error_code dv_demo_list_nodes( dv_demo );
+extern valerie_error_code dv_demo_add_unit( dv_demo );
+extern valerie_error_code dv_demo_select_unit( dv_demo );
+extern valerie_error_code dv_demo_execute( dv_demo );
+extern valerie_error_code dv_demo_load( dv_demo );
+extern valerie_error_code dv_demo_transport( dv_demo );
+static void *dv_demo_status_thread( void * );
+
+/** Connected menu definition. 
+*/
+
+dv_demo_menu_t connected_menu =
+{
+       "Connected Menu",
+       {
+               { "Add Unit", dv_demo_add_unit },
+               { "Select Unit", dv_demo_select_unit },
+               { "Command Shell", dv_demo_execute },
+               { NULL, NULL }
+       }
+};
+
+/** Initialise the demo structure.
+*/
+
+dv_demo dv_demo_init( valerie_parser parser )
+{
+       dv_demo this = malloc( sizeof( dv_demo_t ) );
+       if ( this != NULL )
+       {
+               int index = 0;
+               memset( this, 0, sizeof( dv_demo_t ) );
+               strcpy( this->last_directory, "/" );
+               for ( index = 0; index < 4; index ++ )
+               {
+                       this->queues[ index ].unit = index;
+                       this->queues[ index ].position = -1;
+               }
+               this->parser = parser;
+       }
+       return this;
+}
+
+/** Display a status record.
+*/
+
+void dv_demo_show_status( dv_demo demo, valerie_status status )
+{
+       if ( status->unit == demo->selected_unit && demo->showing )
+       {
+               char temp[ 1024 ] = "";
+
+               sprintf( temp, "U%d ", demo->selected_unit );
+
+               switch( status->status )
+               {
+                       case unit_offline:
+                               strcat( temp, "offline   " );
+                               break;
+                       case unit_undefined:
+                               strcat( temp, "undefined " );
+                               break;
+                       case unit_not_loaded:
+                               strcat( temp, "unloaded  " );
+                               break;
+                       case unit_stopped:
+                               strcat( temp, "stopped   " );
+                               break;
+                       case unit_playing:
+                               strcat( temp, "playing   " );
+                               break;
+                       case unit_paused:
+                               strcat( temp, "paused    " );
+                               break;
+                       case unit_disconnected:
+                               strcat( temp, "disconnect" );
+                               break;
+                       default:
+                               strcat( temp, "unknown   " );
+                               break;
+               }
+
+               sprintf( temp + strlen( temp ), " %9d %9d %9d ", status->in, status->position, status->out );
+               strcat( temp, status->clip );
+
+               printf( "%-80.80s\r", temp );
+               fflush( stdout );
+       }
+}
+
+/** Determine action to carry out as dictated by the client unit queue.
+*/
+
+void dv_demo_queue_action( dv_demo demo, valerie_status status )
+{
+       dv_demo_queue queue = &demo->queues[ status->unit ];
+
+       /* SPECIAL CASE STATUS NOTIFICATIONS TO IGNORE */
+
+       /* When we've issued a LOAD on the previous notification, then ignore this one. */
+       if ( queue->ignore )
+       {
+               queue->ignore --;
+               return;
+       }
+
+       if ( queue->mode && status->status != unit_offline && queue->head != queue->tail )
+       {
+               if ( ( status->position >= status->out && status->speed > 0 ) || status->status == unit_not_loaded )
+               {
+                       queue->position = ( queue->position + 1 ) % 50;
+                       if ( queue->position == queue->tail )
+                               queue->position = queue->head;
+                       valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
+                       if ( status->status == unit_not_loaded )
+                               valerie_unit_play( demo->dv, queue->unit );
+                       queue->ignore = 1;
+               }
+               else if ( ( status->position <= status->in && status->speed < 0 ) || status->status == unit_not_loaded )
+               {
+                       if ( queue->position == -1 )
+                               queue->position = queue->head;
+                       valerie_unit_load( demo->dv_status, status->unit, queue->list[ queue->position ] );
+                       if ( status->status == unit_not_loaded )
+                               valerie_unit_play( demo->dv, queue->unit );
+                       queue->position = ( queue->position - 1 ) % 50;
+                       queue->ignore = 1;
+               }
+       }
+}
+
+/** Status thread.
+*/
+
+static void *dv_demo_status_thread( void *arg )
+{
+       dv_demo demo = arg;
+       valerie_status_t status;
+       valerie_notifier notifier = valerie_get_notifier( demo->dv_status );
+
+       while ( !demo->terminated )
+       {
+               if ( valerie_notifier_wait( notifier, &status ) != -1 )
+               {
+                       dv_demo_queue_action( demo, &status );
+                       dv_demo_show_status( demo, &status );
+                       if ( status.status == unit_disconnected )
+                               demo->disconnected = 1;
+               }
+       }
+
+       return NULL;
+}
+
+/** Turn on/off status display.
+*/
+
+void dv_demo_change_status( dv_demo demo, int flag )
+{
+       if ( demo->disconnected && flag )
+       {
+               valerie_error_code error = valerie_connect( demo->dv );
+               if ( error == valerie_ok )
+                       demo->disconnected = 0;
+               else
+                       beep();
+       }
+
+       if ( flag )
+       {
+               valerie_status_t status;
+               valerie_notifier notifier = valerie_get_notifier( demo->dv );
+               valerie_notifier_get( notifier, &status, demo->selected_unit );
+               demo->showing = 1;
+               dv_demo_show_status( demo, &status );
+       }
+       else
+       {
+               demo->showing = 0;
+               printf( "%-80.80s\r", " " );
+               fflush( stdout );
+       }
+}
+
+/** Add a unit.
+*/
+
+valerie_error_code dv_demo_add_unit( dv_demo demo )
+{
+       valerie_error_code error = valerie_ok;
+       valerie_nodes nodes = valerie_nodes_init( demo->dv );
+       valerie_units units = valerie_units_init( demo->dv );
+
+       if ( valerie_nodes_count( nodes ) != -1 && valerie_units_count( units ) != -1 )
+       {
+               char pressed;
+               valerie_node_entry_t node;
+               valerie_unit_entry_t unit;
+               int node_index = 0;
+               int unit_index = 0;
+
+               printf( "Select a Node\n\n" );
+
+               for ( node_index = 0; node_index < valerie_nodes_count( nodes ); node_index ++ )
+               {
+                       valerie_nodes_get( nodes, node_index, &node );
+                       printf( "%d: %s - %s ", node_index + 1, node.guid, node.name );
+                       for ( unit_index = 0; unit_index < valerie_units_count( units ); unit_index ++ )
+                       {
+                               valerie_units_get( units, unit_index, &unit );
+                               if ( !strcmp( unit.guid, node.guid ) )
+                                       printf( "[U%d] ", unit.unit );
+                       }
+                       printf( "\n" );
+               }
+
+               printf( "0. Exit\n\n" );
+
+               printf( "Node: " );
+
+               while ( ( pressed = get_keypress( ) ) != '0' )
+               {
+                       node_index = pressed - '1';
+                       if ( node_index >= 0 && node_index < valerie_nodes_count( nodes ) )
+                       {
+                               int unit;
+                               printf( "%c\n\n", pressed );
+                               valerie_nodes_get( nodes, node_index, &node );
+                               if ( valerie_unit_add( demo->dv, node.guid, &unit ) == valerie_ok )
+                               {
+                                       printf( "Unit added as U%d\n", unit );
+                                       demo->selected_unit = unit;
+                               }
+                               else
+                               {
+                                       int index = 0;
+                                       valerie_response response = valerie_get_last_response( demo->dv );
+                                       printf( "Failed to add unit:\n\n" );
+                                       for( index = 1; index < valerie_response_count( response ) - 1; index ++ )
+                                               printf( "%s\n", valerie_response_get_line( response, index ) );
+                               }
+                               printf( "\n" );
+                               wait_for_any_key( NULL );
+                               break;
+                       }
+                       else
+                       {
+                               beep( );
+                       }
+               }
+       }
+       else
+       {
+               printf( "Invalid response from the server.\n\n" );
+               wait_for_any_key( NULL );
+       }
+
+       valerie_nodes_close( nodes );
+       valerie_units_close( units );
+
+       return error;
+}
+
+/** Select a unit.
+*/
+
+valerie_error_code dv_demo_select_unit( dv_demo demo )
+{
+       int terminated = 0;
+       int refresh = 1;
+
+       while ( !terminated )
+       {
+               valerie_units units = valerie_units_init( demo->dv );
+
+               if ( valerie_units_count( units ) > 0 )
+               {
+                       valerie_unit_entry_t unit;
+                       int index = 0;
+                       char key = '\0';
+
+                       if ( refresh )
+                       {
+                               printf( "Select a Unit\n\n" );
+
+                               for ( index = 0; index < valerie_units_count( units ); index ++ )
+                               {
+                                       valerie_units_get( units, index, &unit );
+                                       printf( "%d: U%d - %s [%s]\n", index + 1, 
+                                                                                                  unit.unit, 
+                                                                                                  unit.guid, 
+                                                                                                  unit.online ? "online" : "offline" );
+                               }
+                               printf( "0: Exit\n\n" );
+
+                               printf( "Unit [%d]: ", demo->selected_unit + 1 );
+                               refresh = 0;
+                       }
+
+                       key = get_keypress( );
+
+                       if ( key == '\r' )
+                               key = demo->selected_unit + '1';
+
+                       if ( key != '0' )
+                       {
+                               if ( key >= '1' && key < '1' + valerie_units_count( units ) )
+                               {
+                                       demo->selected_unit = key - '1';
+                                       printf( "%c\n\n", key );
+                                       dv_demo_load( demo );
+                                       refresh = 1;
+                               }
+                               else
+                               {
+                                       beep( );
+                               }                                       
+                       }
+                       else
+                       {
+                               printf( "0\n\n" );
+                               terminated = 1;
+                       }
+               }
+               else if ( valerie_units_count( units ) == 0 )
+               {
+                       printf( "No units added - add a unit first\n\n" );
+                       dv_demo_add_unit( demo );
+               }
+               else
+               {
+                       printf( "Unable to obtain Unit List.\n" );
+                       terminated = 1;
+               }
+
+               valerie_units_close( units );
+       }
+
+       return valerie_ok;
+}
+
+/** Execute an arbitrary command.
+*/
+
+valerie_error_code dv_demo_execute( dv_demo demo )
+{
+       valerie_error_code error = valerie_ok;
+       char command[ 10240 ];
+       int terminated = 0;
+
+       printf( "Miracle Shell\n" );
+       printf( "Enter an empty command to exit.\n\n" );
+
+       while ( !terminated )
+       {
+               terminated = 1;
+               printf( "Command> " );
+
+               if ( chomp( io_get_string( command, 10240, "" ) ) != NULL )
+               {
+                       if ( strcmp( command, "" ) )
+                       {
+                               int index = 0;
+                               valerie_response response = NULL;
+                               error = valerie_execute( demo->dv, 10240, command );
+                               printf( "\n" );
+                               response = valerie_get_last_response( demo->dv );
+                               for ( index = 0; index < valerie_response_count( response ); index ++ )
+                               {
+                                       char *line = valerie_response_get_line( response, index );
+                                       printf( "%4d: %s\n", index, line );
+                               }
+                               printf( "\n" );
+                               terminated = 0;
+                       }
+               }
+       }
+
+       printf( "\n" );
+
+       return error;
+}
+
+/** Add a file to the queue.
+*/
+
+valerie_error_code dv_demo_queue_add( dv_demo demo, dv_demo_queue queue, char *file )
+{
+       valerie_status_t status;
+       valerie_notifier notifier = valerie_get_notifier( demo->dv );
+
+       if ( ( queue->tail + 1 ) % 50 == queue->head )
+               queue->head = ( queue->head + 1 ) % 50;
+       strcpy( queue->list[ queue->tail ], file );
+       queue->tail = ( queue->tail + 1 ) % 50;
+
+       valerie_notifier_get( notifier, &status, queue->unit );
+       valerie_notifier_put( notifier, &status );
+
+       return valerie_ok;
+}
+
+/** Basic queue maintenance and status reports.
+*/
+
+valerie_error_code dv_demo_queue_maintenance( dv_demo demo, dv_demo_queue queue )
+{
+       printf( "Queue Maintenance for Unit %d\n\n", queue->unit );
+
+       if ( !queue->mode )
+       {
+               char ch;
+               printf( "Activate queueing? [Y] " );
+               ch = get_keypress( );
+               if ( ch == 'y' || ch == 'Y' || ch == '\r' )
+                       queue->mode = 1;
+               printf( "\n\n" );
+       }
+
+       if ( queue->mode )
+       {
+               int terminated = 0;
+               int last_position = -2;
+
+               term_init( );
+
+               while ( !terminated )
+               {
+                       int first = ( queue->position + 1 ) % 50;
+                       int index = first;
+
+                       if ( first == queue->tail )
+                               index = first = queue->head;
+
+                       if ( queue->head == queue->tail )
+                       {
+                               if ( last_position == -2 )
+                               {
+                                       printf( "Queue is empty\n" );
+                                       printf( "\n" );
+                                       printf( "0 = exit, t = turn off queueing\n\n" );
+                                       last_position = -1;
+                               }
+                       }
+                       else if ( last_position != queue->position )
+                       {
+                               printf( "Order of play\n\n" );
+
+                               do 
+                               {
+                                       printf( "%c%02d: %s\n", index == first ? '*' : ' ', index, queue->list[ index ] + 1 );
+                                       index = ( index + 1 ) % 50;
+                                       if ( index == queue->tail )
+                                               index = queue->head;
+                               }
+                               while( index != first );
+       
+                               printf( "\n" );
+                               printf( "0 = exit, t = turn off queueing, c = clear queue\n\n" );
+                               last_position = queue->position;
+                       }
+
+                       dv_demo_change_status( demo, 1 );
+                       
+                       switch( term_read( ) )
+                       {
+                               case -1:
+                                       break;
+                               case '0':
+                                       terminated = 1;
+                                       break;
+                               case 't':
+                                       terminated = 1;
+                                       queue->mode = 0;
+                                       break;
+                               case 'c':
+                                       queue->head = queue->tail = 0;
+                                       queue->position = -1;
+                                       last_position = -2;
+                                       break;
+                       }
+
+                       dv_demo_change_status( demo, 0 );
+               }
+
+               term_exit( );
+       }
+
+       return valerie_ok;
+}
+
+/** Load a file to the selected unit. Horrible function - sorry :-/. Not a good
+       demo....
+*/
+
+valerie_error_code dv_demo_load( dv_demo demo )
+{
+       valerie_error_code error = valerie_ok;
+       int terminated = 0;
+       int refresh = 1;
+       int start = 0;
+
+       strcpy( demo->current_directory, demo->last_directory );
+
+       term_init( );
+
+       while ( !terminated )
+       {
+               valerie_dir dir = valerie_dir_init( demo->dv, demo->current_directory );
+
+               if ( valerie_dir_count( dir ) == -1 )
+               {
+                       printf( "Invalid directory - retrying %s\n", demo->last_directory );
+                       valerie_dir_close( dir );
+                       dir = valerie_dir_init( demo->dv, demo->last_directory );
+                       if ( valerie_dir_count( dir ) == -1 )
+                       {
+                               printf( "Invalid directory - going back to /\n" );
+                               valerie_dir_close( dir );
+                               dir = valerie_dir_init( demo->dv, "/" );
+                               strcpy( demo->current_directory, "/" );
+                       }
+                       else
+                       {
+                               strcpy( demo->current_directory, demo->last_directory );
+                       }
+               }
+
+               terminated = valerie_dir_count( dir ) == -1;
+
+               if ( !terminated )
+               {
+                       int index = 0;
+                       int selected = 0;
+                       int max = 9;
+                       int end = 0;
+
+                       end = valerie_dir_count( dir );
+
+                       strcpy( demo->last_directory, demo->current_directory );
+
+                       while ( !selected && !terminated )
+                       {
+                               valerie_dir_entry_t entry;
+                               int pressed;
+
+                               if ( refresh )
+                               {
+                                       const char *action = "Load & Play";
+                                       if ( demo->queues[ demo->selected_unit ].mode )
+                                               action = "Queue";
+                                       printf( "%s from %s\n\n", action, demo->current_directory );
+                                       if ( strcmp( demo->current_directory, "/" ) )
+                                               printf( "-: Parent directory\n" );
+                                       for ( index = start; index < end && ( index - start ) < max; index ++ )
+                                       {
+                                               valerie_dir_get( dir, index, &entry );
+                                               printf( "%d: %s\n", index - start + 1, entry.name );
+                                       }
+                                       while ( ( index ++ % 9 ) != 0 )
+                                               printf( "\n" );
+                                       printf( "\n" );
+                                       if ( start + max < end )
+                                               printf( "space = more files" );
+                                       else if ( end > max )
+                                               printf( "space = return to start of list" );
+                                       if ( start > 0 )
+                                               printf( ", b = previous files" );
+                                       printf( "\n" );
+                                       printf( "0 = abort, t = transport, x = execute command, q = queue maintenance\n\n" );
+                                       refresh = 0;
+                               }
+
+                               dv_demo_change_status( demo, 1 );
+
+                               pressed = term_read( );
+                               switch( pressed )
+                               {
+                                       case -1:
+                                               break;
+                                       case '0':
+                                               terminated = 1;
+                                               break;
+                                       case 'b':
+                                               refresh = start - max >= 0;
+                                               if ( refresh )
+                                                       start = start - max;
+                                               break;
+                                       case ' ':
+                                               refresh = start + max < end;
+                                               if ( refresh )
+                                               {
+                                                       start = start + max;
+                                               }
+                                               else if ( end > max )
+                                               {
+                                                       start = 0;
+                                                       refresh = 1;
+                                               }
+                                               break;
+                                       case '-':
+                                               if ( strcmp( demo->current_directory, "/" ) )
+                                               {
+                                                       selected = 1;
+                                                       ( *strrchr( demo->current_directory, '/' ) ) = '\0';
+                                                       ( *( strrchr( demo->current_directory, '/' ) + 1 ) ) = '\0';
+                                               }
+                                               break;
+                                       case 't':
+                                               dv_demo_change_status( demo, 0 );
+                                               term_exit( );
+                                               dv_demo_transport( demo );
+                                               term_init( );
+                                               selected = 1;
+                                               break;
+                                       case 'x':
+                                               dv_demo_change_status( demo, 0 );
+                                               term_exit( );
+                                               dv_demo_execute( demo );
+                                               term_init( );
+                                               selected = 1;
+                                               break;
+                                       case 'q':
+                                               dv_demo_change_status( demo, 0 );
+                                               term_exit( );
+                                               dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
+                                               term_init( );
+                                               selected = 1;
+                                               break;
+                                       default:
+                                               if ( pressed >= '1' && pressed <= '9' )
+                                               {
+                                                       if ( ( start + pressed - '1' ) < end )
+                                                       {
+                                                               valerie_dir_get( dir, start + pressed - '1', &entry );
+                                                               selected = 1;
+                                                               strcat( demo->current_directory, entry.name );
+                                                       }
+                                               }
+                                               break;
+                               }
+
+                               dv_demo_change_status( demo, 0 );
+                       }
+
+                       valerie_dir_close( dir );
+               }
+
+               if ( !terminated && demo->current_directory[ strlen( demo->current_directory ) - 1 ] != '/' )
+               {
+                       if ( demo->queues[ demo->selected_unit ].mode == 0 )
+                       {
+                               error = valerie_unit_load( demo->dv, demo->selected_unit, demo->current_directory );
+                               valerie_unit_play( demo->dv, demo->selected_unit );
+                       }
+                       else
+                       {
+                               dv_demo_queue_add( demo, &demo->queues[ demo->selected_unit ], demo->current_directory );
+                               printf( "File %s added to queue.\n", demo->current_directory );
+                       }
+                       strcpy( demo->current_directory, demo->last_directory );
+                       refresh = 0;
+               }
+               else
+               {
+                       refresh = 1;
+                       start = 0;
+               }
+       }
+
+       term_exit( );
+
+       return error;
+}
+
+/** Set the in point of the clip on the select unit.
+*/
+
+valerie_error_code dv_demo_set_in( dv_demo demo )
+{
+       int position = 0;
+       valerie_status_t status;
+       valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
+       valerie_notifier_get( notifier, &status, demo->selected_unit );
+       position = status.position;
+       return valerie_unit_set_in( demo->dv, demo->selected_unit, position );
+}
+
+/** Set the out point of the clip on the selected unit.
+*/
+
+valerie_error_code dv_demo_set_out( dv_demo demo )
+{
+       int position = 0;
+       valerie_status_t status;
+       valerie_notifier notifier = valerie_parser_get_notifier( demo->parser );
+       valerie_notifier_get( notifier, &status, demo->selected_unit );
+       position = status.position;
+       return valerie_unit_set_out( demo->dv, demo->selected_unit, position );
+}
+
+/** Clear the in and out points on the selected unit.
+*/
+
+valerie_error_code dv_demo_clear_in_out( dv_demo demo )
+{
+       return valerie_unit_clear_in_out( demo->dv, demo->selected_unit );
+}
+
+/** Goto a user specified frame on the selected unit.
+*/
+
+valerie_error_code dv_demo_goto( dv_demo demo )
+{
+       int frame = 0;
+       printf( "Frame: " );
+       if ( get_int( &frame, 0 ) )
+               return valerie_unit_goto( demo->dv, demo->selected_unit, frame );
+       return valerie_ok;
+}
+
+/** Manipulate playback on the selected unit.
+*/
+
+valerie_error_code dv_demo_transport( dv_demo demo )
+{
+       valerie_error_code error = valerie_ok;
+       int refresh = 1;
+       int terminated = 0;
+       valerie_status_t status;
+       valerie_notifier notifier = valerie_get_notifier( demo->dv );
+
+       while ( !terminated )
+       {
+               if ( refresh )
+               {
+                       printf( "  +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+  \n" );
+                       printf( "  |1=-5| |2=-2.5| |3=-1| |4=-0.5| |5=1| |6=0.5| |7=1.25| |8=2.5| |9=5|  \n" );
+                       printf( "  +----+ +------+ +----+ +------+ +---+ +-----+ +------+ +-----+ +---+  \n" );
+                       printf( "\n" );
+                       printf( "+----------------------------------------------------------------------+\n" );
+                       printf( "|              0 = quit, x = eXecute, 'space' = pause                  |\n" );
+                       printf( "|              g = goto a frame, q = queue maintenance                 |\n" );
+                       printf( "|     h = step -1, j = end of clip, k = start of clip, l = step 1      |\n" );
+                       printf( "|        eof handling: p = pause, r = repeat, t = terminate            |\n" );
+                       printf( "|       i = set in point, o = set out point, c = clear in/out          |\n" );
+                       printf( "|       u = use point settings, d = don't use point settings           |\n" );
+                       printf( "+----------------------------------------------------------------------+\n" );
+                       printf( "\n" );
+                       term_init( );
+                       refresh = 0;
+               }
+
+               dv_demo_change_status( demo, 1 );
+
+               switch( term_read( ) )
+               {
+                       case '0':
+                               terminated = 1;
+                               break;
+                       case -1:
+                               break;
+                       case ' ':
+                               error = valerie_unit_pause( demo->dv, demo->selected_unit );
+                               break;
+                       case '1':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -5000 );
+                               break;
+                       case '2':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -2500 );
+                               break;
+                       case '3':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -1000 );
+                               break;
+                       case '4':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, -500 );
+                               break;
+                       case '5':
+                               error = valerie_unit_play( demo->dv, demo->selected_unit );
+                               break;
+                       case '6':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 500 );
+                               break;
+                       case '7':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 1250 );
+                               break;
+                       case '8':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 2500 );
+                               break;
+                       case '9':
+                               error = valerie_unit_play_at_speed( demo->dv, demo->selected_unit, 5000 );
+                               break;
+                       case 's':
+                               error = valerie_unit_goto( demo->dv, demo->selected_unit, 0 );
+                               break;
+                       case 'h':
+                               error = valerie_unit_step( demo->dv, demo->selected_unit, -1 );
+                               break;
+                       case 'j':
+                               valerie_notifier_get( notifier, &status, demo->selected_unit );
+                               error = valerie_unit_goto( demo->dv, demo->selected_unit, status.tail_out );
+                               break;
+                       case 'k':
+                               valerie_notifier_get( notifier, &status, demo->selected_unit );
+                               error = valerie_unit_goto( demo->dv, demo->selected_unit, status.in );
+                               break;
+                       case 'l':
+                               error = valerie_unit_step( demo->dv, demo->selected_unit, 1 );
+                               break;
+                       case 'p':
+                               error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "pause" );
+                               break;
+                       case 'r':
+                               error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "loop" );
+                               break;
+                       case 't':
+                               error = valerie_unit_set( demo->dv, demo->selected_unit, "eof", "stop" );
+                               break;
+                       case 'i':
+                               error = dv_demo_set_in( demo );
+                               break;
+                       case 'o':
+                               error = dv_demo_set_out( demo );
+                               break;
+                       case 'g':
+                               dv_demo_change_status( demo, 0 );
+                               term_exit( );
+                               error = dv_demo_goto( demo );
+                               refresh = 1;
+                               break;
+                       case 'c':
+                               error = dv_demo_clear_in_out( demo );
+                               break;
+                       case 'u':
+                               error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "use" );
+                               break;
+                       case 'd':
+                               error = valerie_unit_set( demo->dv, demo->selected_unit, "points", "ignore" );
+                               break;
+                       case 'x':
+                               dv_demo_change_status( demo, 0 );
+                               term_exit( );
+                               dv_demo_execute( demo );
+                               refresh = 1;
+                               break;
+                       case 'q':
+                               dv_demo_change_status( demo, 0 );
+                               term_exit( );
+                               dv_demo_queue_maintenance( demo, &demo->queues[ demo->selected_unit ] );
+                               refresh = 1;
+                               break;
+               }
+
+               dv_demo_change_status( demo, 0 );
+       }
+
+       term_exit( );
+
+       return error;
+}
+
+/** Recursive menu execution.
+*/
+
+valerie_error_code dv_demo_run_menu( dv_demo demo, dv_demo_menu menu )
+{
+       const char *items = "123456789abcdefghijklmnopqrstuvwxyz";
+       int refresh_menu = 1;
+       int terminated = 0;
+       int item_count = 0;
+       int item_selected = 0;
+       int index = 0;
+       char key;
+
+       while( !terminated )
+       {
+
+               if ( refresh_menu )
+               {
+                       printf( "%s\n\n", menu->description );
+                       for ( index = 0; menu->array[ index ].option != NULL; index ++ )
+                               printf( "%c: %s\n", items[ index ], menu->array[ index ].option );
+                       printf( "0: Exit\n\n" );
+                       printf( "Select Option: " );
+                       refresh_menu = 0;
+                       item_count = index;
+               }
+
+               key = get_keypress( );
+
+               if ( demo->disconnected && key != '0' )
+               {
+                       valerie_error_code error = valerie_connect( demo->dv );
+                       if ( error == valerie_ok )
+                               demo->disconnected = 0;
+                       else
+                               beep();
+               }
+
+               if ( !demo->disconnected || key == '0' )
+               {
+                       item_selected = strchr( items, key ) - items;
+
+                       if ( key == '0' )
+                       {
+                               printf( "%c\n\n", key );
+                               terminated = 1;
+                       }
+                       else if ( item_selected >= 0 && item_selected < item_count )
+                       {
+                               printf( "%c\n\n", key );
+                               menu->array[ item_selected ].function( demo );
+                               refresh_menu = 1;
+                       }
+                       else
+                       {
+                               beep( );
+                       }
+               }
+       }
+
+       return valerie_ok;
+}
+
+/** Entry point for main menu.
+*/
+
+void dv_demo_run( dv_demo this )
+{
+       this->dv = valerie_init( this->parser );
+       this->dv_status = valerie_init( this->parser );
+       if ( valerie_connect( this->dv ) == valerie_ok )
+       {
+               pthread_create( &this->thread, NULL, dv_demo_status_thread, this );
+               dv_demo_run_menu( this, &connected_menu );
+               this->terminated = 1;
+               pthread_join( this->thread, NULL );
+               this->terminated = 0;
+       }
+       else
+       {
+               printf( "Unable to connect." );
+               wait_for_any_key( "" );
+       }
+
+       valerie_close( this->dv_status );
+       valerie_close( this->dv );
+
+       printf( "Demo Exit.\n" );
+}
+
+/** Close the demo structure.
+*/
+
+void dv_demo_close( dv_demo demo )
+{
+       free( demo );
+}
diff --git a/src/humperdink/client.h b/src/humperdink/client.h
new file mode 100644 (file)
index 0000000..59d97f9
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * client.h -- Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_CLIENT_H_
+#define _DEMO_CLIENT_H_
+
+#include <stdio.h>
+#include <pthread.h>
+#include <valerie/valerie.h>
+
+/** Queue for unit playback
+*/
+
+typedef struct 
+{
+       int mode;
+       int unit;
+       int position;
+       int head;
+       int tail;
+       char list[ 50 ][ PATH_MAX + NAME_MAX ];
+       int ignore;
+}
+*dv_demo_queue, dv_demo_queue_t;
+
+/** Structure for storing app state. 
+*/
+
+typedef struct
+{
+       int disconnected;
+       valerie_parser parser;
+       valerie dv;
+       valerie dv_status;
+       int selected_unit;
+       char current_directory[ 512 ];
+       char last_directory[ 512 ];
+       int showing;
+       int terminated;
+       pthread_t thread;
+       dv_demo_queue_t queues[ MAX_UNITS ];
+}
+*dv_demo, dv_demo_t;
+
+extern dv_demo dv_demo_init( valerie_parser );
+extern void dv_demo_run( dv_demo );
+extern void dv_demo_close( dv_demo );
+
+#endif
diff --git a/src/humperdink/io.c b/src/humperdink/io.c
new file mode 100644 (file)
index 0000000..42718df
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * io.c -- Valerie client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               if ( length && input[ length - 1 ] == '\n' )
+                       input[ length - 1 ] = '\0';
+               if ( length > 1 && input[ length - 2 ] == '\r' )
+                       input[ length - 2 ] = '\0';
+       }
+       return input;
+}
+
+char *trim( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               int first = 0;
+               while( first < length && isspace( input[ first ] ) )
+                       first ++;
+               memmove( input, input + first, length - first + 1 );
+               length = length - first;
+               while ( length > 0 && isspace( input[ length - 1 ] ) )
+                       input[ -- length ] = '\0';
+       }
+       return input;
+}
+
+char *strip_quotes( char *input )
+{
+       if ( input != NULL )
+       {
+               char *ptr = strrchr( input, '\"' );
+               if ( ptr != NULL )
+                       *ptr = '\0';
+               if ( input[ 0 ] == '\"' )
+                       strcpy( input, input + 1 );
+       }
+       return input;
+}
+
+char *io_get_string( char *output, int maxlength, const char *use )
+{
+       char *value = NULL;
+       strcpy( output, use );
+       if ( trim( chomp( fgets( output, maxlength, stdin ) ) ) != NULL )
+       {
+               if ( !strcmp( output, "" ) )
+                       strcpy( output, use );
+               value = output;
+       }
+       return value;
+}
+
+int *get_int( int *output, int use )
+{
+       int *value = NULL;
+       char temp[ 132 ];
+       *output = use;
+       if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+       {
+               if ( strcmp( temp, "" ) )
+                       *output = atoi( temp );
+               value = output;
+       }
+       return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the 
+       previous tty settings.
+*/
+
+void term_exit(void)
+{
+       if ( mode == 1 )
+       {
+               tcsetattr( 0, TCSANOW, &oldtty );
+               mode = 0;
+       }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+       struct termios tty;
+
+       tcgetattr( 0, &tty );
+       oldtty = tty;
+
+       tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+       tty.c_oflag |= OPOST;
+       tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+       tty.c_cflag &= ~(CSIZE|PARENB);
+       tty.c_cflag |= CS8;
+       tty.c_cc[ VMIN ] = 1;
+       tty.c_cc[ VTIME ] = 0;
+    
+       tcsetattr( 0, TCSANOW, &tty );
+
+       mode = 1;
+
+       atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+       Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+    int n = 1;
+    unsigned char ch;
+    struct timeval tv;
+    fd_set rfds;
+
+    FD_ZERO( &rfds );
+    FD_SET( 0, &rfds );
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+    n = select( 1, &rfds, NULL, NULL, &tv );
+    if (n > 0) 
+       {
+        n = read( 0, &ch, 1 );
+               tcflush( 0, TCIFLUSH );
+        if (n == 1)
+            return ch;
+        return n;
+    }
+    return -1;
+}
+
+char get_keypress( )
+{
+       char value = '\0';
+       int pressed = 0;
+
+       fflush( stdout );
+
+       term_init( );
+       while ( ( pressed = term_read( ) ) == -1 ) ;
+       term_exit( );
+
+       value = (char)pressed;
+
+       return value;
+}
+
+void wait_for_any_key( const char *message )
+{
+       if ( message == NULL )
+               printf( "Press any key to continue: " );
+       else
+               printf( "%s", message );
+
+       get_keypress( );
+
+       printf( "\n\n" );
+}
+
+void beep( )
+{
+       printf( "%c", 7 );
+       fflush( stdout );
+}
diff --git a/src/humperdink/io.h b/src/humperdink/io.h
new file mode 100644 (file)
index 0000000..f4ac23a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * io.h -- Valerie client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *io_get_string( char *, int, const char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( const char * );
+extern void beep( );
+
+#endif
diff --git a/src/humperdink/remote.c b/src/humperdink/remote.c
new file mode 100644 (file)
index 0000000..8171179
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * remote.c -- Remote Valerie client demo
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdint.h>
+
+#include <valerie/valerie_remote.h>
+
+/* Application header files */
+#include "client.h"
+#include "io.h"
+
+/** Connect to a remote server.
+*/
+
+static valerie_parser create_parser( )
+{
+       char server[ 132 ];
+       int port;
+       valerie_parser parser = NULL;
+
+       printf( "Connecting to a Server\n\n" );
+
+       printf( "Server [localhost]: " );
+
+       if ( io_get_string( server, sizeof( server ), "localhost" ) != NULL )
+       {
+               printf( "Port        [5250]: " );
+
+               if ( get_int( &port, 5250 ) != NULL )
+                       parser = valerie_parser_init_remote( server, port );
+       }
+
+       printf( "\n" );
+
+       return parser;
+}
+
+/** Main function.
+*/
+
+int main( int argc, char **argv )
+{
+       valerie_parser parser = create_parser( );
+
+       if ( parser != NULL )
+       {
+               dv_demo demo = dv_demo_init( parser );
+               dv_demo_run( demo );
+               dv_demo_close( demo );
+               valerie_parser_close( parser );
+       }
+
+       return 0;
+}
diff --git a/src/inigo/Makefile b/src/inigo/Makefile
new file mode 100644 (file)
index 0000000..c206a81
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../config.mak
+
+TARGET = inigo
+
+OBJS = inigo.o \
+          io.o
+
+CFLAGS += -I.. $(RDYNAMIC) -DVERSION=\"$(version)\"
+
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install:       all
+       install -d "$(DESTDIR)$(bindir)"
+       install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+
+uninstall:
+       rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/inigo/configure b/src/inigo/configure
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/src/inigo/inigo.c b/src/inigo/inigo.c
new file mode 100644 (file)
index 0000000..698908e
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * inigo.c -- MLT command line utility
+ * Copyright (C) 2002-2008 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+
+#include <framework/mlt.h>
+
+#ifdef __DARWIN__
+#include <SDL.h>
+#endif
+
+#include "io.h"
+
+static void transport_action( mlt_producer producer, char *value )
+{
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+       mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
+       mlt_consumer consumer = mlt_properties_get_data( properties, "transport_consumer", NULL );
+
+       mlt_properties_set_int( properties, "stats_off", 1 );
+
+       if ( strlen( value ) == 1 )
+       {
+               switch( value[ 0 ] )
+               {
+                       case 'q':
+                               mlt_properties_set_int( properties, "done", 1 );
+                               break;
+                       case '0':
+                               mlt_producer_set_speed( producer, 1 );
+                               mlt_producer_seek( producer, 0 );
+                               break;
+                       case '1':
+                               mlt_producer_set_speed( producer, -10 );
+                               break;
+                       case '2':
+                               mlt_producer_set_speed( producer, -5 );
+                               break;
+                       case '3':
+                               mlt_producer_set_speed( producer, -2 );
+                               break;
+                       case '4':
+                               mlt_producer_set_speed( producer, -1 );
+                               break;
+                       case '5':
+                               mlt_producer_set_speed( producer, 0 );
+                               break;
+                       case '6':
+                       case ' ':
+                               mlt_producer_set_speed( producer, 1 );
+                               break;
+                       case '7':
+                               mlt_producer_set_speed( producer, 2 );
+                               break;
+                       case '8':
+                               mlt_producer_set_speed( producer, 5 );
+                               break;
+                       case '9':
+                               mlt_producer_set_speed( producer, 10 );
+                               break;
+                       case 'd':
+                               if ( multitrack != NULL )
+                               {
+                                       int i = 0;
+                                       mlt_position last = -1;
+                                       fprintf( stderr, "\n" );
+                                       for ( i = 0; 1; i ++ )
+                                       {
+                                               mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i );
+                                               if ( time == last )
+                                                       break;
+                                               last = time;
+                                               fprintf( stderr, "%d: %d\n", i, (int)time );
+                                       }
+                               }
+                               break;
+
+                       case 'g':
+                               if ( multitrack != NULL )
+                               {
+                                       mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
+                                       mlt_producer_seek( producer, time );
+                               }
+                               break;
+                       case 'H':
+                               if ( producer != NULL )
+                               {
+                                       mlt_position position = mlt_producer_position( producer );
+                                       mlt_producer_seek( producer, position - ( mlt_producer_get_fps( producer ) * 60 ) );
+                               }
+                               break;
+                       case 'h':
+                               if ( producer != NULL )
+                               {
+                                       mlt_position position = mlt_producer_position( producer );
+                                       mlt_producer_set_speed( producer, 0 );
+                                       mlt_producer_seek( producer, position - 1 );
+                               }
+                               break;
+                       case 'j':
+                               if ( multitrack != NULL )
+                               {
+                                       mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
+                                       mlt_producer_seek( producer, time );
+                               }
+                               break;
+                       case 'k':
+                               if ( multitrack != NULL )
+                               {
+                                       mlt_position time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
+                                       mlt_producer_seek( producer, time );
+                               }
+                               break;
+                       case 'l':
+                               if ( producer != NULL )
+                               {
+                                       mlt_position position = mlt_producer_position( producer );
+                                       if ( mlt_producer_get_speed( producer ) != 0 )
+                                               mlt_producer_set_speed( producer, 0 );
+                                       else
+                                               mlt_producer_seek( producer, position + 1 );
+                               }
+                               break;
+                       case 'L':
+                               if ( producer != NULL )
+                               {
+                                       mlt_position position = mlt_producer_position( producer );
+                                       mlt_producer_seek( producer, position + ( mlt_producer_get_fps( producer ) * 60 ) );
+                               }
+                               break;
+               }
+
+               mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 );
+       }
+
+       mlt_properties_set_int( properties, "stats_off", 0 );
+}
+
+static mlt_consumer create_consumer( mlt_profile profile, char *id )
+{
+       char *arg = id != NULL ? strchr( id, ':' ) : NULL;
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_consumer consumer = mlt_factory_consumer( profile, id, arg );
+       if ( consumer != NULL )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+               mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
+       }
+       return consumer;
+}
+
+#ifdef __DARWIN__
+
+static void event_handling( mlt_producer producer, mlt_consumer consumer )
+{
+       SDL_Event event;
+
+       while ( SDL_PollEvent( &event ) )
+       {
+               switch( event.type )
+               {
+                       case SDL_QUIT:
+                               mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( consumer ), "done", 1 );
+                               break;
+
+                       case SDL_KEYDOWN:
+                               if ( event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+                               {
+                                       char keyboard[ 2 ] = { event.key.keysym.unicode, 0 };
+                                       transport_action( producer, keyboard );
+                               }
+                               break;
+               }
+       }
+}
+
+#endif
+
+static void transport( mlt_producer producer, mlt_consumer consumer )
+{
+       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" );
+       struct timespec tm = { 0, 40000 };
+       int total_length = mlt_producer_get_length( producer );
+       int last_position = 0;
+
+       if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
+       {
+               if ( !silent && !progress )
+               {
+                       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" );
+                       fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+
+                       fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+                       fprintf( stderr, "|               H = back 1 minute,  L = forward 1 minute              |\n" );
+                       fprintf( stderr, "|                 h = previous frame,  l = next frame                 |\n" );
+                       fprintf( stderr, "|           g = start of clip, j = next clip, k = previous clip       |\n" );
+                       fprintf( stderr, "|                0 = restart, q = quit, space = play                  |\n" );
+                       fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+               }
+
+               while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) )
+               {
+                       int value = ( silent || progress )? -1 : term_read( );
+
+                       if ( value != -1 )
+                       {
+                               char string[ 2 ] = { value, 0 };
+                               transport_action( producer, string );
+                       }
+
+#ifdef __DARWIN__
+                       event_handling( producer, consumer );
+#endif
+
+                       if ( !silent && mlt_properties_get_int( properties, "stats_off" ) == 0 )
+                       {
+                               if ( progress )
+                               {
+                                       int current_position = mlt_producer_position( producer );
+                                       if ( current_position > last_position )
+                                       {
+                                               fprintf( stderr, "Current Frame: %10d, percentage: %10d\r",
+                                                       current_position, 100 * current_position / total_length );
+                                               last_position = current_position;
+                                       }
+                               }
+                               else
+                               {
+                                       fprintf( stderr, "Current Position: %10d\r", (int)mlt_producer_position( producer ) );
+                               }
+                       }
+
+                       if ( silent )
+                               nanosleep( &tm, NULL );
+               }
+
+               if ( !silent )
+                       fprintf( stderr, "\n" );
+       }
+}
+
+static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id )
+{
+       mlt_properties metadata = mlt_repository_metadata( repo, type, id );
+       if ( metadata )
+       {
+               char *s = mlt_properties_serialise_yaml( metadata );
+               fprintf( stderr, "%s", s );
+               free( s );
+       }
+       else
+       {
+               fprintf( stderr, "# No metadata for %s \"%s\"\n", typestr, id );
+       }
+}
+
+static void query_services( mlt_repository repo, mlt_service_type type )
+{
+       mlt_properties services = NULL;
+       const char *typestr = NULL;
+       switch ( type )
+       {
+               case consumer_type:
+                       services = mlt_repository_consumers( repo );
+                       typestr = "consumers";
+                       break;
+               case filter_type:
+                       services = mlt_repository_filters( repo );
+                       typestr = "filters";
+                       break;
+               case producer_type:
+                       services = mlt_repository_producers( repo );
+                       typestr = "producers";
+                       break;
+               case transition_type:
+                       services = mlt_repository_transitions( repo );
+                       typestr = "transitions";
+                       break;
+               default:
+                       return;
+       }
+       fprintf( stderr, "---\n%s:\n", typestr );
+       if ( services )
+       {
+               int j;
+               for ( j = 0; j < mlt_properties_count( services ); j++ )
+                       fprintf( stderr, "  - %s\n", mlt_properties_get_name( services, j ) );
+       }
+       fprintf( stderr, "...\n" );
+}
+
+int main( int argc, char **argv )
+{
+       int i;
+       mlt_consumer consumer = NULL;
+       mlt_producer inigo = NULL;
+       FILE *store = NULL;
+       char *name = NULL;
+       mlt_profile profile = NULL;
+       int is_progress = 0;
+       int is_silent = 0;
+
+       // Construct the factory
+       mlt_repository repo = mlt_factory_init( NULL );
+
+       for ( i = 1; i < argc; i ++ )
+       {
+               // Check for serialisation switch
+               if ( !strcmp( argv[ i ], "-serialise" ) )
+               {
+                       name = argv[ ++ i ];
+                       if ( name != NULL && strstr( name, ".inigo" ) )
+                               store = fopen( name, "w" );
+                       else
+                       {
+                               if ( name == NULL || name[0] == '-' )
+                                       store = stdout;
+                               name = NULL;
+                       }
+               }
+               // Look for the profile option
+               else if ( !strcmp( argv[ i ], "-profile" ) )
+               {
+                       const char *pname = argv[ ++ i ];
+                       if ( pname && pname[0] != '-' )
+                               profile = mlt_profile_init( pname );
+               }
+               else if ( !strcmp( argv[ i ], "-progress" ) )
+               {
+                       is_progress = 1;
+               }
+               // Look for the query option
+               else if ( !strcmp( argv[ i ], "-query" ) )
+               {
+                       const char *pname = argv[ ++ i ];
+                       if ( pname && pname[0] != '-' )
+                       {
+                               if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) )
+                                       query_services( repo, consumer_type );
+                               else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) )
+                                       query_services( repo, filter_type );
+                               else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) )
+                                       query_services( repo, producer_type );
+                               else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) )
+                                       query_services( repo, transition_type );
+                               
+                               else if ( !strncmp( pname, "consumer=", 9 ) )
+                                       query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 );
+                               else if ( !strncmp( pname, "filter=", 7 ) )
+                                       query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 );
+                               else if ( !strncmp( pname, "producer=", 9 ) )
+                                       query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 );
+                               else if ( !strncmp( pname, "transition=", 11 ) )
+                                       query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 );
+                               else
+                                       goto query_all;
+                       }
+                       else
+                       {
+query_all:
+                               query_services( repo, consumer_type );
+                               query_services( repo, filter_type );
+                               query_services( repo, producer_type );
+                               query_services( repo, transition_type );
+                               fprintf( stderr, "# You can query the metadata for a specific service using:\n"
+                                       "# -query <type>=<identifer>\n"
+                                       "# where <type> is one of: consumer, filter, producer, or transition.\n" );
+                       }
+                       goto exit_factory;
+               }
+               else if ( !strcmp( argv[ i ], "-silent" ) )
+               {
+                       is_silent = 1;
+               }
+               else if ( !strcmp( argv[ i ], "-verbose" ) )
+               {
+                       mlt_log_set_level( MLT_LOG_VERBOSE );
+               }
+               else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) )
+               {
+                       fprintf( stderr, "MLT inigo " VERSION "\n"
+                               "Copyright (C) 2002-2008 Ushodaya Enterprises Limited\n"
+                               "<http://www.mltframework.org/>\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"
+                       );
+                       goto exit_factory;
+               }
+               else if ( !strcmp( argv[ i ], "-debug" ) )
+               {
+                       mlt_log_set_level( MLT_LOG_DEBUG );
+               }
+       }
+
+       // Create profile if not set explicitly
+       if ( profile == NULL )
+               profile = mlt_profile_init( NULL );
+
+       // Look for the consumer option
+       for ( i = 1; i < argc; i ++ )
+       {
+               if ( !strcmp( argv[ i ], "-consumer" ) )
+               {
+                       consumer = create_consumer( profile, argv[ ++ i ] );
+                       if ( consumer )
+                       {
+                               mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+                               while ( argv[ i + 1 ] != NULL && strstr( argv[ i + 1 ], "=" ) )
+                                       mlt_properties_parse( properties, argv[ ++ i ] );
+                       }
+               }
+       }
+
+       // If we have no consumer, default to sdl
+       if ( store == NULL && consumer == NULL )
+               consumer = create_consumer( profile, NULL );
+
+       // Get inigo producer
+       if ( argc > 1 )
+               inigo = mlt_factory_producer( profile, "inigo", &argv[ 1 ] );
+
+       // Set transport properties on consumer and produder
+       if ( consumer != NULL && inigo != NULL )
+       {
+               mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", inigo, 0, NULL, NULL );
+               mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( inigo ), "transport_consumer", consumer, 0, NULL, NULL );
+               if ( is_progress )
+                       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 ( argc > 1 && inigo != NULL && mlt_producer_get_length( inigo ) > 0 )
+       {
+               // Parse the arguments
+               for ( i = 1; i < argc; i ++ )
+               {
+                       if ( !strcmp( argv[ i ], "-serialise" ) )
+                       {
+                               if ( store != stdout )
+                                       i ++;
+                       }
+                       else
+                       {
+                               if ( store != NULL )
+                                       fprintf( store, "%s\n", argv[ i ] );
+
+                               i ++;
+
+                               while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' )
+                               {
+                                       if ( store != NULL )
+                                               fprintf( store, "%s\n", argv[ i ] );
+                                       i += 1;
+                               }
+
+                               i --;
+                       }
+               }
+
+               if ( consumer != NULL && store == NULL )
+               {
+                       // Get inigo's properties
+                       mlt_properties inigo_props = MLT_PRODUCER_PROPERTIES( inigo );
+       
+                       // Get the last group
+                       mlt_properties group = mlt_properties_get_data( inigo_props, "group", 0 );
+       
+                       // Apply group settings
+                       mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+                       mlt_properties_inherit( properties, group );
+
+                       // Connect consumer to inigo
+                       mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( inigo ) );
+
+                       // Start the consumer
+                       mlt_consumer_start( consumer );
+
+                       // Transport functionality
+                       transport( inigo, consumer );
+
+                       // Stop the consumer
+                       mlt_consumer_stop( consumer );
+               }
+               else if ( store != NULL && store != stdout && name != NULL )
+               {
+                       fprintf( stderr, "Project saved as %s.\n", name );
+                       fclose( store );
+               }
+       }
+       else
+       {
+               fprintf( stderr,
+"Usage: inigo [options] [producer [name=value]* ]+\n"
+"Options:\n"
+"  -attach filter[:arg] [name=value]*       Attach a filter to the output\n"
+"  -attach-cut filter[:arg] [name=value]*   Attach a filter to a cut\n"
+"  -attach-track filter[:arg] [name=value]* Attach a filter to a track\n"
+"  -attach-clip filter[:arg] [name=value]*  Attach a filter to a producer\n"
+"  -audio-track | -hide-video               Add an audio-only track\n"
+"  -blank frames                            Add blank silence to a track\n"
+"  -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"
+"  -group [name=value]*                     Apply properties repeatedly\n"
+"  -help                                    Show this message\n"
+"  -join clips                              Join multiple clips into one cut\n"
+"  -mix length                              Add a mix between the last two cuts\n"
+"  -mixer transition                        Add a transition to the mix\n"
+"  -null-track | -hide-track                Add a hidden track\n"
+"  -profile name                            Set the processing settings\n"
+"  -progress                                Display progress along with position\n"
+"  -remove                                  Remove the most recent cut\n"
+"  -repeat times                            Repeat the last cut\n"
+"  -query                                   List all of the registered services\n"
+"  -query \"consumers\" | \"consumer\"=id       List consumers or show info about one\n"
+"  -query \"filters\" | \"filter\"=id           List filters or show info about one\n"
+"  -query \"producers\" | \"producer\"=id       List producers or show info about one\n"
+"  -query \"transitions\" | \"transition\"=id   List transitions, show info about one\n"
+"  -serialise [filename]                    Write the commands to a text file\n"
+"  -silent                                  Do not display position/transport\n"
+"  -split relative-frame                    Split the last cut into two cuts\n"
+"  -swap                                    Rearrange the last two cuts\n"
+"  -track                                   Add a track\n"
+"  -transition id[:arg] [name=value]*       Add a transition\n"
+"  -verbose                                 Set the logging level to verbose\n"
+"  -version                                 Show the version and copyright\n"
+"  -video-track | -hide-audio               Add a video-only track\n"
+"For more help: <http://www.mltframework.org/>\n" );
+       }
+
+       // Close the consumer
+       if ( consumer != NULL )
+               mlt_consumer_close( consumer );
+
+       // Close the producer
+       if ( inigo != NULL )
+               mlt_producer_close( inigo );
+
+       // Close the factory
+       mlt_profile_close( profile );
+
+exit_factory:
+               
+       mlt_factory_close( );
+
+       return 0;
+}
diff --git a/src/inigo/io.c b/src/inigo/io.c
new file mode 100644 (file)
index 0000000..77d13b9
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * io.c -- inigo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               if ( length && input[ length - 1 ] == '\n' )
+                       input[ length - 1 ] = '\0';
+               if ( length > 1 && input[ length - 2 ] == '\r' )
+                       input[ length - 2 ] = '\0';
+       }
+       return input;
+}
+
+char *trim( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               int first = 0;
+               while( first < length && isspace( input[ first ] ) )
+                       first ++;
+               memmove( input, input + first, length - first + 1 );
+               length = length - first;
+               while ( length > 0 && isspace( input[ length - 1 ] ) )
+                       input[ -- length ] = '\0';
+       }
+       return input;
+}
+
+char *strip_quotes( char *input )
+{
+       if ( input != NULL )
+       {
+               char *ptr = strrchr( input, '\"' );
+               if ( ptr != NULL )
+                       *ptr = '\0';
+               if ( input[ 0 ] == '\"' )
+                       strcpy( input, input + 1 );
+       }
+       return input;
+}
+
+int *get_int( int *output, int use )
+{
+       int *value = NULL;
+       char temp[ 132 ];
+       *output = use;
+       if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+       {
+               if ( strcmp( temp, "" ) )
+                       *output = atoi( temp );
+               value = output;
+       }
+       return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the 
+       previous tty settings.
+*/
+
+void term_exit(void)
+{
+       if ( mode == 1 )
+       {
+               tcsetattr( 0, TCSANOW, &oldtty );
+               mode = 0;
+       }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+       struct termios tty;
+
+       tcgetattr( 0, &tty );
+       oldtty = tty;
+
+       tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+       tty.c_oflag |= OPOST;
+       tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+       tty.c_cflag &= ~(CSIZE|PARENB);
+       tty.c_cflag |= CS8;
+       tty.c_cc[ VMIN ] = 1;
+       tty.c_cc[ VTIME ] = 0;
+    
+       tcsetattr( 0, TCSANOW, &tty );
+
+       mode = 1;
+
+       atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+       Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+    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 );
+               tcflush( 0, TCIFLUSH );
+        if (n == 1)
+            return ch;
+        return n;
+    }
+    return -1;
+}
+
+char get_keypress( )
+{
+       char value = '\0';
+       int pressed = 0;
+
+       fflush( stdout );
+
+       term_init( );
+       while ( ( pressed = term_read( ) ) == -1 ) ;
+       term_exit( );
+
+       value = (char)pressed;
+
+       return value;
+}
+
+void wait_for_any_key( char *message )
+{
+       if ( message == NULL )
+               printf( "Press any key to continue: " );
+       else
+               printf( "%s", message );
+
+       get_keypress( );
+
+       printf( "\n\n" );
+}
+
+void beep( )
+{
+       printf( "%c", 7 );
+       fflush( stdout );
+}
diff --git a/src/inigo/io.h b/src/inigo/io.h
new file mode 100644 (file)
index 0000000..70387ba
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * io.h -- inigo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *get_string( char *, int, char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( char * );
+extern void beep( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/Makefile b/src/miracle/Makefile
new file mode 100644 (file)
index 0000000..2d92bfb
--- /dev/null
@@ -0,0 +1,77 @@
+include ../../config.mak
+
+TARGET = miracle
+
+ifneq ($(targetos), Darwin)
+LIBNAME = libmiracle$(LIBSUF)
+LIBTARGET = $(LIBNAME).$(version)
+LIBSONAME = $(LIBNAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(LIBSONAME)
+else
+LIBNAME = libmiracle$(LIBSUF)
+LIBTARGET = libmiracle.$(version)$(LIBSUF)
+LIBSONAME = libmiracle.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(LIBSONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+APP_OBJS = miracle.o
+
+LIB_OBJS = miracle_log.o \
+          miracle_server.o \
+          miracle_connection.o \
+          miracle_local.o \
+          miracle_unit.o \
+          miracle_commands.o \
+          miracle_unit_commands.o
+
+INCS = miracle_server.h \
+          miracle_local.h \
+          miracle_log.h
+
+OBJS = $(APP_OBJS) $(LIB_OBJS)
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../valerie -lvalerie
+LDFLAGS += -L../framework -lmlt -lpthread
+
+SRCS := $(OBJS:.o=.c)
+
+all:           $(TARGET)
+
+$(TARGET):     $(APP_OBJS) $(LIBTARGET)
+                       $(CC) -o $@ $(APP_OBJS) -L. -lmiracle $(LDFLAGS)
+
+$(LIBTARGET):  $(LIB_OBJS)
+                       $(CC) $(SHFLAGS) -o $@ $(LIB_OBJS) $(LDFLAGS)
+                       ln -sf $(LIBTARGET) $(LIBNAME)
+                       ln -sf $(LIBTARGET) $(LIBSONAME)
+
+depend:                $(SRCS)
+                       $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+                       rm -f .depend
+
+clean: 
+                       rm -f $(OBJS) $(TARGET) $(LIBNAME) $(LIBTARGET)
+
+install:       all
+       install -d "$(DESTDIR)$(bindir)"
+       install -c -s -m 755 $(TARGET) "$(DESTDIR)$(bindir)"
+       install -m 755 $(LIBTARGET) $(DESTDIR)$(libdir)
+       ln -sf $(LIBTARGET) $(DESTDIR)$(libdir)/$(LIBSONAME)
+       ln -sf $(LIBTARGET) $(DESTDIR)$(libdir)/$(LIBNAME)
+       mkdir -p "$(DESTDIR)$(prefix)/include/mlt/miracle"
+       install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/miracle"
+
+uninstall:
+       rm -f "$(DESTDIR)$(bindir)/$(TARGET)"
+       rm -f "$(DESTDIR)$(libdir)/$(LIBTARGET)"
+       rm -f "$(DESTDIR)$(libdir)/$(LIBSONAME)"
+       rm -f "$(DESTDIR)$(libdir)/$(LIBNAME)"
+       rm -rf "$(DESTDIR)$(prefix)/include/mlt/miracle"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/miracle/configure b/src/miracle/configure
new file mode 100755 (executable)
index 0000000..85d890d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo "miracle  -I$prefix/include/mlt -D_REENTRANT      -L$libdir -lmiracle" >> ../../packages.dat
diff --git a/src/miracle/miracle.c b/src/miracle/miracle.c
new file mode 100644 (file)
index 0000000..24aac66
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * miracle.c -- MLT Video TCP Server
+ *
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Authors:
+ *     Dan Dennedy <dan@dennedy.org>
+ *     Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <sched.h>
+
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "miracle_server.h"
+#include "miracle_log.h"
+
+/** Our dv server.
+*/
+
+static miracle_server server = NULL;
+
+/** atexit shutdown handler for the server.
+*/
+
+static void main_cleanup( )
+{
+       miracle_server_close( server );
+}
+
+/** Report usage and exit.
+*/
+
+void usage( char *app )
+{
+       fprintf( stderr, "Usage: %s [-test] [-port NNNN]\n", app );
+       exit( 0 );
+}
+
+/** The main function.
+*/
+
+int main( int argc, char **argv )
+{
+       int error = 0;
+       int index = 0;
+       int background = 1;
+       struct timespec tm = { 5, 0 };
+       struct sched_param scp;
+
+       // Use realtime scheduling if possible
+       memset( &scp, '\0', sizeof( scp ) );
+       scp.sched_priority = sched_get_priority_max( SCHED_FIFO ) - 1;
+#ifndef __DARWIN__
+       sched_setscheduler( 0, SCHED_FIFO, &scp );
+#endif
+
+       mlt_factory_init( NULL );
+
+       server = miracle_server_init( argv[ 0 ] );
+
+       for ( index = 1; index < argc; index ++ )
+       {
+               if ( !strcmp( argv[ index ], "-port" ) )
+                       miracle_server_set_port( server, atoi( argv[ ++ index ] ) );
+               else if ( !strcmp( argv[ index ], "-proxy" ) )
+                       miracle_server_set_proxy( server, argv[ ++ index ] );
+               else if ( !strcmp( argv[ index ], "-test" ) )
+                       background = 0;
+               else
+                       usage( argv[ 0 ] );
+       }
+
+       /* Optionally detatch ourselves from the controlling tty */
+
+       if ( background )
+       {
+               if ( fork() )
+                       return 0;
+               setsid();
+               miracle_log_init( log_syslog, LOG_INFO );
+       }
+       else
+       {
+               miracle_log_init( log_stderr, LOG_DEBUG );
+       }
+
+       atexit( main_cleanup );
+
+       /* Set the config script */
+       miracle_server_set_config( server, "/etc/miracle.conf" );
+
+       /* Execute the server */
+       error = miracle_server_execute( server );
+
+       /* We need to wait until we're exited.. */
+       while ( !server->shutdown )
+               nanosleep( &tm, NULL );
+
+       return error;
+}
diff --git a/src/miracle/miracle_commands.c b/src/miracle/miracle_commands.c
new file mode 100644 (file)
index 0000000..c95a4d0
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * global_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <pthread.h>
+
+#include "miracle_unit.h"
+#include "miracle_commands.h"
+#include "miracle_log.h"
+
+static miracle_unit g_units[MAX_UNITS];
+
+
+/** Return the miracle_unit given a numeric index.
+*/
+
+miracle_unit miracle_get_unit( int n )
+{
+       if (n < MAX_UNITS)
+               return g_units[n];
+       else
+               return NULL;
+}
+
+/** Destroy the miracle_unit given its numeric index.
+*/
+
+void miracle_delete_unit( int n )
+{
+       if (n < MAX_UNITS)
+       {
+               miracle_unit unit = miracle_get_unit(n);
+               if (unit != NULL)
+               {
+                       miracle_unit_close( unit );
+                       g_units[ n ] = NULL;
+                       miracle_log( LOG_NOTICE, "Deleted unit U%d.", n ); 
+               }
+       }
+}
+
+/** Destroy all allocated units on the server.
+*/
+
+void miracle_delete_all_units( void )
+{
+       int i;
+       for (i = 0; i < MAX_UNITS; i++)
+       {
+               if ( miracle_get_unit(i) != NULL )
+               {
+                       miracle_unit_close( miracle_get_unit(i) );
+                       miracle_log( LOG_NOTICE, "Deleted unit U%d.", i ); 
+               }
+       }
+}
+
+/** Add a DV virtual vtr to the server.
+*/
+response_codes miracle_add_unit( command_argument cmd_arg )
+{
+       int i = 0;
+       for ( i = 0; i < MAX_UNITS; i ++ )
+               if ( g_units[ i ] == NULL )
+                       break;
+
+       if ( i < MAX_UNITS )
+       {
+               char *arg = cmd_arg->argument;
+               g_units[ i ] = miracle_unit_init( i, arg );
+               if ( g_units[ i ] != NULL )
+               {
+                       miracle_unit_set_notifier( g_units[ i ], valerie_parser_get_notifier( cmd_arg->parser ), cmd_arg->root_dir );
+                       valerie_response_printf( cmd_arg->response, 10, "U%1d\n\n", i );
+               }
+               return g_units[ i ] != NULL ? RESPONSE_SUCCESS_N : RESPONSE_ERROR;
+       }
+       valerie_response_printf( cmd_arg->response, 1024, "no more units can be created\n\n" );
+
+       return RESPONSE_ERROR;
+}
+
+
+/** List all AV/C nodes on the bus.
+*/
+response_codes miracle_list_nodes( command_argument cmd_arg )
+{
+       response_codes error = RESPONSE_SUCCESS_N;
+       return error;
+}
+
+
+/** List units already added to server.
+*/
+response_codes miracle_list_units( command_argument cmd_arg )
+{
+       response_codes error = RESPONSE_SUCCESS_N;
+       int i = 0;
+
+       for ( i = 0; i < MAX_UNITS; i ++ )
+       {
+               miracle_unit unit = miracle_get_unit( i );
+               if ( unit != NULL )
+               {
+                       mlt_properties properties = unit->properties;
+                       char *constructor = mlt_properties_get( properties, "constructor" );
+                       int node = mlt_properties_get_int( properties, "node" );
+                       int online = !mlt_properties_get_int( properties, "offline" );
+                       valerie_response_printf( cmd_arg->response, 1024, "U%d %02d %s %d\n", i, node, constructor, online );
+               }
+       }
+       valerie_response_printf( cmd_arg->response, 1024, "\n" );
+
+       return error;
+}
+
+static int filter_files( const struct dirent *de )
+{
+       return de->d_name[ 0 ] != '.';
+}
+
+/** List clips in a directory.
+*/
+response_codes miracle_list_clips( command_argument cmd_arg )
+{
+       response_codes error = RESPONSE_BAD_FILE;
+       const char *dir_name = (const char*) cmd_arg->argument;
+       DIR *dir;
+       char fullname[1024];
+       struct dirent **de = NULL;
+       int i, n;
+       
+       snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, dir_name );
+       dir = opendir( fullname );
+       if (dir != NULL)
+       {
+               struct stat info;
+               error = RESPONSE_SUCCESS_N;
+               n = scandir( fullname, &de, filter_files, alphasort );
+               for (i = 0; i < n; i++ )
+               {
+                       snprintf( fullname, 1023, "%s%s/%s", cmd_arg->root_dir, dir_name, de[i]->d_name );
+                       if ( stat( fullname, &info ) == 0 && S_ISDIR( info.st_mode ) )
+                               valerie_response_printf( cmd_arg->response, 1024, "\"%s/\"\n", de[i]->d_name );
+               }
+               for (i = 0; i < n; i++ )
+               {
+                       snprintf( fullname, 1023, "%s%s/%s", cmd_arg->root_dir, dir_name, de[i]->d_name );
+                       if ( lstat( fullname, &info ) == 0 && 
+                                ( S_ISREG( info.st_mode ) || S_ISLNK( info.st_mode ) || ( strstr( fullname, ".clip" ) && info.st_mode | S_IXUSR ) ) )
+                               valerie_response_printf( cmd_arg->response, 1024, "\"%s\" %llu\n", de[i]->d_name, (unsigned long long) info.st_size );
+                       free( de[ i ] );
+               }
+               free( de );
+               closedir( dir );
+               valerie_response_write( cmd_arg->response, "\n", 1 );
+       }
+
+       return error;
+}
+
+/** Set a server configuration property.
+*/
+
+response_codes miracle_set_global_property( command_argument cmd_arg )
+{
+       char *key = (char*) cmd_arg->argument;
+       char *value = NULL;
+
+       value = strchr( key, '=' );
+       if (value == NULL)
+               return RESPONSE_OUT_OF_RANGE;
+       *value = 0;
+       value++;
+       miracle_log( LOG_DEBUG, "SET %s = %s", key, value );
+
+       if ( strncasecmp( key, "root", 1024) == 0 )
+       {
+               int len = strlen(value);
+               int i;
+               
+               /* stop all units and unload clips */
+               for (i = 0; i < MAX_UNITS; i++)
+               {
+                       if (g_units[i] != NULL)
+                               miracle_unit_terminate( g_units[i] );
+               }
+
+               /* set the property */
+               strncpy( cmd_arg->root_dir, value, 1023 );
+
+               /* add a trailing slash if needed */
+               if ( len && cmd_arg->root_dir[ len - 1 ] != '/')
+               {
+                       cmd_arg->root_dir[ len ] = '/';
+                       cmd_arg->root_dir[ len + 1 ] = '\0';
+               }
+       }
+       else
+               return RESPONSE_OUT_OF_RANGE;
+       
+       return RESPONSE_SUCCESS;
+}
+
+/** Get a server configuration property.
+*/
+
+response_codes miracle_get_global_property( command_argument cmd_arg )
+{
+       char *key = (char*) cmd_arg->argument;
+
+       if ( strncasecmp( key, "root", 1024) == 0 )
+       {
+               valerie_response_write( cmd_arg->response, cmd_arg->root_dir, strlen(cmd_arg->root_dir) );
+               return RESPONSE_SUCCESS_1;
+       }
+       else
+               return RESPONSE_OUT_OF_RANGE;
+       
+       return RESPONSE_SUCCESS;
+}
+
+
diff --git a/src/miracle/miracle_commands.h b/src/miracle/miracle_commands.h
new file mode 100644 (file)
index 0000000..9d79683
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * global_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _GLOBAL_COMMANDS_H_
+#define _GLOBAL_COMMANDS_H_
+
+#include <valerie/valerie_status.h>
+#include "miracle_unit.h"
+#include "miracle_connection.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern miracle_unit miracle_get_unit( int );
+extern void miracle_delete_unit( int );
+extern void miracle_delete_all_units( void );
+extern int miracle_unit_status( int n, valerie_status status, int root_offset );
+//extern void raw1394_start_service_threads( void );
+//extern void raw1394_stop_service_threads( void );
+
+extern response_codes miracle_add_unit( command_argument );
+extern response_codes miracle_list_nodes( command_argument );
+extern response_codes miracle_list_units( command_argument );
+extern response_codes miracle_list_clips( command_argument );
+extern response_codes miracle_set_global_property( command_argument );
+extern response_codes miracle_get_global_property( command_argument );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_connection.c b/src/miracle/miracle_connection.c
new file mode 100644 (file)
index 0000000..7f287e3
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * miracle_connection.c -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <netdb.h>
+#include <sys/socket.h> 
+#include <arpa/inet.h>
+
+#include <valerie/valerie_socket.h>
+
+/* Application header files */
+#include "miracle_commands.h"
+#include "miracle_connection.h"
+#include "miracle_server.h"
+#include "miracle_log.h"
+
+/** This is a generic replacement for fgets which operates on a file
+   descriptor. Unlike fgets, we can also specify a line terminator. Maximum
+   of (max - 1) chars can be read into buf from fd. If we reach the
+   end-of-file, *eof_chk is set to 1. 
+*/
+
+int fdgetline( int fd, char *buf, int max, char line_terminator, int *eof_chk )
+{
+       int count = 0;
+       char tmp [1];
+       *eof_chk = 0;
+       
+       if (fd)
+               while (count < max - 1) {
+                       if (read (fd, tmp, 1) > 0) {
+                               if (tmp [0] != line_terminator)
+                                       buf [count++] = tmp [0];
+                               else
+                                       break;
+
+/* Is it an EOF character (ctrl-D, i.e. ascii 4)? If so we definitely want
+   to break. */
+
+                               if (tmp [0] == 4) {
+                                       *eof_chk = 1;
+                                       break;
+                               }
+                       } else {
+                               *eof_chk = 1;
+                               break;
+                       }
+               }
+               
+       buf [count] = '\0';
+       
+       return count;
+}
+
+static int connection_initiate( int );
+static int connection_send( int, valerie_response );
+static int connection_read( int, char *, int );
+static void connection_close( int );
+
+static int connection_initiate( int fd )
+{
+       int error = 0;
+       valerie_response response = valerie_response_init( );
+       valerie_response_set_error( response, 100, "VTR Ready" );
+       error = connection_send( fd, response );
+       valerie_response_close( response );
+       return error;
+}
+
+static int connection_send( int fd, valerie_response response )
+{
+       int error = 0;
+       int index = 0;
+       int code = valerie_response_get_error_code( response );
+
+       if ( code != -1 )
+       {
+               int items = valerie_response_count( response );
+
+               if ( items == 0 )
+                       valerie_response_set_error( response, 500, "Unknown error" );
+
+               if ( code == 200 && items > 2 )
+                       valerie_response_set_error( response, 201, "OK" );
+               else if ( code == 200 && items > 1 )
+                       valerie_response_set_error( response, 202, "OK" );
+
+               code = valerie_response_get_error_code( response );
+               items = valerie_response_count( response );
+
+               for ( index = 0; !error && index < items; index ++ )
+               {
+                       char *line = valerie_response_get_line( response, index );
+                       int length = strlen( line );
+                       if ( length == 0 && index != valerie_response_count( response ) - 1 && write( fd, " ", 1 ) != 1 )
+                               error = -1;
+                       else if ( length > 0 && write( fd, line, length ) != length )
+                               error = -1;
+                       if ( write( fd, "\r\n", 2 ) != 2 )
+                               error = -1;                     
+               }
+
+               if ( ( code == 201 || code == 500 ) && strcmp( valerie_response_get_line( response, items - 1 ), "" ) )
+                       if ( write( fd, "\r\n", 2 ) != 2 )
+                               miracle_log( LOG_ERR, "write(\"\\r\\n\") failed!" );
+       }
+       else
+       {
+               const char *message = "500 Empty Response\r\n\r\n";
+               if ( write( fd, message, strlen( message ) ) != strlen( message ))
+                       miracle_log( LOG_ERR, "write(%s) failed!", message );
+       }
+
+       return error;
+}
+
+static int connection_read( int fd, char *command, int length )
+{
+       int eof_chk;
+       int nchars = fdgetline( fd, command, length, '\n', &eof_chk );
+       char *cr = strchr( command, '\r');
+       if ( cr != NULL ) 
+               cr[0] = '\0';
+       if ( eof_chk || strncasecmp( command, "BYE", 3 ) == 0 ) 
+               nchars = 0;
+       return nchars;
+}
+
+int connection_status( int fd, valerie_notifier notifier )
+{
+       int error = 0;
+       int index = 0;
+       valerie_status_t status;
+       char text[ 10240 ];
+       valerie_socket socket = valerie_socket_init_fd( fd );
+       
+       for ( index = 0; !error && index < MAX_UNITS; index ++ )
+       {
+               valerie_notifier_get( notifier, &status, index );
+               valerie_status_serialise( &status, text, sizeof( text ) );
+               error = valerie_socket_write_data( socket, text, strlen( text )  ) != strlen( text );
+       }
+
+       while ( !error )
+       {
+               if ( valerie_notifier_wait( notifier, &status ) == 0 )
+               {
+                       valerie_status_serialise( &status, text, sizeof( text ) );
+                       error = valerie_socket_write_data( socket, text, strlen( text ) ) != strlen( text );
+               }
+               else
+               {
+                       struct timeval tv = { 0, 0 };
+                       fd_set rfds;
+
+                   FD_ZERO( &rfds );
+                   FD_SET( fd, &rfds );
+
+                       if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
+                               error = 1;
+               }
+       }
+
+       valerie_socket_close( socket );
+       
+       return error;
+}
+
+static void connection_close( int fd )
+{
+       close( fd );
+}
+
+void *parser_thread( void *arg )
+{
+       struct hostent *he;
+       connection_t *connection = arg;
+       mlt_properties owner = connection->owner;
+       char address[ 512 ];
+       char command[ 1024 ];
+       int fd = connection->fd;
+       valerie_parser parser = connection->parser;
+       valerie_response response = NULL;
+
+       /* Get the connecting clients ip information */
+       he = gethostbyaddr( (char *) &( connection->sin.sin_addr.s_addr ), sizeof(u_int32_t), AF_INET); 
+       if ( he != NULL )
+               strcpy( address, he->h_name );
+       else
+               inet_ntop( AF_INET, &( connection->sin.sin_addr.s_addr), address, 32 );
+
+       miracle_log( LOG_NOTICE, "Connection established with %s (%d)", address, fd );
+
+       /* Execute the commands received. */
+       if ( connection_initiate( fd ) == 0 )
+       {
+               int error = 0;
+
+               while( !error && connection_read( fd, command, 1024 ) )
+               {
+                       response = NULL;
+
+                       if ( !strncmp( command, "PUSH ", 5 ) )
+                       {
+                               char temp[ 20 ];
+                               int bytes;
+                               char *buffer = NULL;
+                               int total = 0;
+                               mlt_service service = NULL;
+
+                               connection_read( fd, temp, 20 );
+                               bytes = atoi( temp );
+                               buffer = malloc( bytes + 1 );
+                               while ( total < bytes )
+                               {
+                                       int count = read( fd, buffer + total, bytes - total );
+                                       if ( count >= 0 )
+                                               total += count;
+                                       else
+                                               break;
+                               }
+                               buffer[ bytes ] = '\0';
+                               if ( bytes > 0 && total == bytes )
+                               {
+                                       if ( mlt_properties_get( owner, "push-parser-off" ) == 0 )
+                                       {
+                                               service = ( mlt_service )mlt_factory_producer( NULL, "westley-xml", buffer );
+                                               mlt_events_fire( owner, "push-received", &response, command, service, NULL );
+                                               if ( response == NULL )
+                                                       response = valerie_parser_push( parser, command, service );
+                                       }
+                                       else
+                                       {
+                                               response = valerie_parser_received( parser, command, buffer );
+                                       }
+                               }
+                               error = connection_send( fd, response );
+                               valerie_response_close( response );
+                               mlt_service_close( service );
+                               free( buffer );
+                       }
+                       else if ( strncmp( command, "STATUS", 6 ) )
+                       {
+                               mlt_events_fire( owner, "command-received", &response, command, NULL );
+                               if ( response == NULL )
+                                       response = valerie_parser_execute( parser, command );
+                               miracle_log( LOG_INFO, "%s \"%s\" %d", address, command, valerie_response_get_error_code( response ) );
+                               error = connection_send( fd, response );
+                               valerie_response_close( response );
+                       }
+                       else
+                       {
+                               error = connection_status( fd, valerie_parser_get_notifier( parser ) );
+                       }
+               }
+       }
+
+       /* Free the resources associated with this connection. */
+       connection_close( fd );
+
+       miracle_log( LOG_NOTICE, "Connection with %s (%d) closed", address, fd );
+
+       free( connection );
+
+       return NULL;
+}
diff --git a/src/miracle/miracle_connection.h b/src/miracle/miracle_connection.h
new file mode 100644 (file)
index 0000000..8734c6c
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * miracle_connection.h -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_CONNECTION_H_
+#define _DV_CONNECTION_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <valerie/valerie_parser.h>
+#include <valerie/valerie_tokeniser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Connection structure
+*/
+
+typedef struct 
+{
+       mlt_properties owner;
+       int fd;
+       struct sockaddr_in sin;
+       valerie_parser parser;
+} 
+connection_t;
+
+/** Enumeration for responses.
+*/
+
+typedef enum 
+{
+       RESPONSE_SUCCESS = 200,
+       RESPONSE_SUCCESS_N = 201,
+       RESPONSE_SUCCESS_1 = 202,
+       RESPONSE_UNKNOWN_COMMAND = 400,
+       RESPONSE_TIMEOUT = 401,
+       RESPONSE_MISSING_ARG = 402,
+       RESPONSE_INVALID_UNIT = 403,
+       RESPONSE_BAD_FILE = 404,
+       RESPONSE_OUT_OF_RANGE = 405,
+       RESPONSE_TOO_MANY_FILES = 406,
+       RESPONSE_ERROR = 500
+} 
+response_codes;
+
+/* the following struct is passed as the single argument 
+   to all command callback functions */
+
+typedef struct 
+{
+       valerie_parser    parser;
+       valerie_response  response;
+       valerie_tokeniser tokeniser;
+       char         *command;
+       int           unit;
+       void         *argument;
+       char         *root_dir;
+} 
+command_argument_t, *command_argument;
+
+/* A handler is defined as follows. */
+typedef int (*command_handler_t) ( command_argument );
+
+
+extern void *parser_thread( void *arg );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_local.c b/src/miracle/miracle_local.c
new file mode 100644 (file)
index 0000000..c1029a1
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ * miracle_local.c -- Local Miracle Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+/* Needed for backtrace on linux */
+#ifdef linux
+#include <execinfo.h>
+#endif
+
+/* Valerie header files */
+#include <valerie/valerie_util.h>
+
+/* MLT header files. */
+#include <framework/mlt_factory.h>
+
+/* Application header files */
+#include "miracle_local.h"
+#include "miracle_connection.h"
+#include "miracle_commands.h"
+#include "miracle_unit_commands.h"
+#include "miracle_log.h"
+
+/** Private miracle_local structure.
+*/
+
+typedef struct
+{
+       valerie_parser parser;
+       char root_dir[1024];
+}
+*miracle_local, miracle_local_t;
+
+/** Forward declarations.
+*/
+
+static valerie_response miracle_local_connect( miracle_local );
+static valerie_response miracle_local_execute( miracle_local, char * );
+static valerie_response miracle_local_push( miracle_local, char *, mlt_service );
+static valerie_response miracle_local_receive( miracle_local, char *, char * );
+static void miracle_local_close( miracle_local );
+response_codes miracle_help( command_argument arg );
+response_codes miracle_run( command_argument arg );
+response_codes miracle_shutdown( command_argument arg );
+
+/** DV Parser constructor.
+*/
+
+valerie_parser miracle_parser_init_local( )
+{
+       valerie_parser parser = malloc( sizeof( valerie_parser_t ) );
+       miracle_local local = malloc( sizeof( miracle_local_t ) );
+
+       if ( parser != NULL )
+       {
+               memset( parser, 0, sizeof( valerie_parser_t ) );
+
+               parser->connect = (parser_connect)miracle_local_connect;
+               parser->execute = (parser_execute)miracle_local_execute;
+               parser->push = (parser_push)miracle_local_push;
+               parser->received = (parser_received)miracle_local_receive;
+               parser->close = (parser_close)miracle_local_close;
+               parser->real = local;
+
+               if ( local != NULL )
+               {
+                       memset( local, 0, sizeof( miracle_local_t ) );
+                       local->parser = parser;
+                       local->root_dir[0] = '/';
+               }
+
+               // Construct the factory
+               mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
+       }
+       return parser;
+}
+
+/** response status code/message pair 
+*/
+
+typedef struct 
+{
+       int code;
+       const char *message;
+} 
+responses_t;
+
+/** response messages 
+*/
+
+static responses_t responses [] = 
+{
+       {RESPONSE_SUCCESS, "OK"},
+       {RESPONSE_SUCCESS_N, "OK"},
+       {RESPONSE_SUCCESS_1, "OK"},
+       {RESPONSE_UNKNOWN_COMMAND, "Unknown command"},
+       {RESPONSE_TIMEOUT, "Operation timed out"},
+       {RESPONSE_MISSING_ARG, "Argument missing"},
+       {RESPONSE_INVALID_UNIT, "Unit not found"},
+       {RESPONSE_BAD_FILE, "Failed to locate or open clip"},
+       {RESPONSE_OUT_OF_RANGE, "Argument value out of range"},
+       {RESPONSE_TOO_MANY_FILES, "Too many files open"},
+       {RESPONSE_ERROR, "Server Error"}
+};
+
+/** Argument types.
+*/
+
+typedef enum 
+{
+       ATYPE_NONE,
+       ATYPE_FLOAT,
+       ATYPE_STRING,
+       ATYPE_INT,
+       ATYPE_PAIR
+} 
+arguments_types;
+
+/** A command definition.
+*/
+
+typedef struct 
+{
+/* The command string corresponding to this operation (e.g. "play") */
+       const char *command;
+/* The function associated with it */
+       response_codes (*operation) ( command_argument );
+/* a boolean to indicate if this is a unit or global command
+   unit commands require a unit identifier as first argument */
+       int is_unit;
+/* What type is the argument (RTTI :-) ATYPE_whatever */
+       int type;
+/* online help information */
+       const char *help;
+} 
+command_t;
+
+/* The following define the queue of commands available to the user. The
+   first entry is the name of the command (the string which must be typed),
+   the second command is the function associated with it, the third argument
+   is for the type of the argument, and the last argument specifies whether
+   this is something which should be handled immediately or whether it
+   should be queued (only robot motion commands need to be queued). */
+
+static command_t vocabulary[] = 
+{
+       {"BYE", NULL, 0, ATYPE_NONE, "Terminates the session. Units are not removed and task queue is not flushed."},
+       {"HELP", miracle_help, 0, ATYPE_NONE, "Display this information!"},
+       {"NLS", miracle_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."},
+       {"UADD", miracle_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."},
+       {"ULS", miracle_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."},
+       {"CLS", miracle_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."},
+       {"SET", miracle_set_global_property, 0, ATYPE_PAIR, "Set a server configuration property."},
+       {"GET", miracle_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."},
+       {"RUN", miracle_run, 0, ATYPE_STRING, "Run a batch file." },
+       {"LIST", miracle_list, 1, ATYPE_NONE, "List the playlist associated to a unit."},
+       {"LOAD", miracle_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."},
+       {"INSERT", miracle_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."},
+       {"REMOVE", miracle_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."},
+       {"CLEAN", miracle_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."},
+       {"WIPE", miracle_wipe, 1, ATYPE_NONE, "Clean a unit by removing everything before the currently playing clip."},
+       {"CLEAR", miracle_clear, 1, ATYPE_NONE, "Clear a unit by removing all clips."},
+       {"MOVE", miracle_move, 1, ATYPE_INT, "Move a clip to another clip index."},
+       {"APND", miracle_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."},
+       {"PLAY", miracle_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."},
+       {"STOP", miracle_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."},
+       {"PAUSE", miracle_pause, 1, ATYPE_NONE, "Pause a playing clip."},
+       {"REW", miracle_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."},
+       {"FF", miracle_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."},
+       {"STEP", miracle_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."},
+       {"GOTO", miracle_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."},
+       {"SIN", miracle_set_in_point, 1, ATYPE_INT, "Set the IN point of the loaded clip to frame number argument. -1 = reset in point to 0"},
+       {"SOUT", miracle_set_out_point, 1, ATYPE_INT, "Set the OUT point of the loaded clip to frame number argument. -1 = reset out point to maximum."},
+       {"USTA", miracle_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."},
+       {"USET", miracle_set_unit_property, 1, ATYPE_PAIR, "Set a unit configuration property."},
+       {"UGET", miracle_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."},
+       {"XFER", miracle_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."},
+       {"SHUTDOWN", miracle_shutdown, 0, ATYPE_NONE, "Shutdown the server."},
+       {NULL, NULL, 0, ATYPE_NONE, NULL}
+};
+
+/** Usage message 
+*/
+
+static char helpstr [] = 
+       "Miracle -- A Multimedia Playout Server\n" 
+       "       Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n"
+       "       Authors:\n"
+       "               Dan Dennedy <dan@dennedy.org>\n"
+       "               Charles Yates <charles.yates@pandora.be>\n"
+       "Available commands:\n";
+
+/** Lookup the response message for a status code.
+*/
+
+inline const char *get_response_msg( int code )
+{
+       int i = 0;
+       for ( i = 0; responses[ i ].message != NULL && code != responses[ i ].code; i ++ ) ;
+       return responses[ i ].message;
+}
+
+/** Tell the user the miracle command set
+*/
+
+response_codes miracle_help( command_argument cmd_arg )
+{
+       int i = 0;
+       
+       valerie_response_printf( cmd_arg->response, 10240, "%s", helpstr );
+       
+       for ( i = 0; vocabulary[ i ].command != NULL; i ++ )
+               valerie_response_printf( cmd_arg->response, 1024,
+                                                       "%-10.10s%s\n", 
+                                                       vocabulary[ i ].command, 
+                                                       vocabulary[ i ].help );
+
+       valerie_response_printf( cmd_arg->response, 2, "\n" );
+
+       return RESPONSE_SUCCESS_N;
+}
+
+/** Execute a batch file.
+*/
+
+response_codes miracle_run( command_argument cmd_arg )
+{
+       valerie_response temp = valerie_parser_run( cmd_arg->parser, (char *)cmd_arg->argument );
+
+       if ( temp != NULL )
+       {
+               int index = 0;
+
+               valerie_response_set_error( cmd_arg->response, 
+                                                          valerie_response_get_error_code( temp ),
+                                                          valerie_response_get_error_string( temp ) );
+
+               for ( index = 1; index < valerie_response_count( temp ); index ++ )
+                       valerie_response_printf( cmd_arg->response, 10240, "%s\n", valerie_response_get_line( temp, index ) );
+
+               valerie_response_close( temp );
+       }
+
+       return valerie_response_get_error_code( cmd_arg->response );
+}
+
+response_codes miracle_shutdown( command_argument cmd_arg )
+{
+       exit( 0 );
+       return RESPONSE_SUCCESS;
+}
+
+/** Processes 'thread' id
+*/
+
+static pthread_t self;
+
+/* Signal handler to deal with various shutdown signals. Basically this
+   should clean up and power down the motor. Note that the death of any
+   child thread will kill all thrads. */
+
+void signal_handler( int sig )
+{
+       if ( pthread_equal( self, pthread_self( ) ) )
+       {
+
+#ifdef _GNU_SOURCE
+               miracle_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) );
+#else
+               miracle_log( LOG_DEBUG, "Received signal %i - shutting down.", sig );
+#endif
+
+               exit(EXIT_SUCCESS);
+       }
+}
+
+static void sigsegv_handler()
+{
+#ifdef linux
+       void *array[ 10 ];
+       size_t size;
+       char **strings;
+       size_t i;
+
+       miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n"
+               "Dumping stack from the offending thread\n\n" );
+       size = backtrace( array, 10 );
+       strings = backtrace_symbols( array, size );
+
+       miracle_log( LOG_CRIT, "Obtained %zd stack frames.\n", size );
+
+       for ( i = 0; i < size; i++ )
+                miracle_log( LOG_CRIT, "%s", strings[ i ] );
+
+       free( strings );
+
+       miracle_log( LOG_CRIT, "\nDone dumping - exiting.\n" );
+#else
+       miracle_log( LOG_CRIT, "\a\nMiracle experienced a segmentation fault.\n" );
+#endif
+       exit( EXIT_FAILURE );
+}
+
+
+
+/** Local 'connect' function.
+*/
+
+static valerie_response miracle_local_connect( miracle_local local )
+{
+       valerie_response response = valerie_response_init( );
+
+       self = pthread_self( );
+
+       valerie_response_set_error( response, 100, "VTR Ready" );
+
+       signal( SIGHUP, signal_handler );
+       signal( SIGINT, signal_handler );
+       signal( SIGTERM, SIG_DFL );
+       signal( SIGSTOP, signal_handler );
+       signal( SIGPIPE, signal_handler );
+       signal( SIGALRM, signal_handler );
+       signal( SIGCHLD, SIG_IGN );
+       if ( getenv( "MLT_SIGSEGV" ) )
+               signal( SIGSEGV, sigsegv_handler );
+
+       return response;
+}
+
+/** Set the error and determine the message associated to this command.
+*/
+
+void miracle_command_set_error( command_argument cmd, response_codes code )
+{
+       valerie_response_set_error( cmd->response, code, get_response_msg( code ) );
+}
+
+/** Parse the unit argument.
+*/
+
+int miracle_command_parse_unit( command_argument cmd, int argument )
+{
+       int unit = -1;
+       char *string = valerie_tokeniser_get_string( cmd->tokeniser, argument );
+       if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
+               unit = atoi( string + 1 );
+       return unit;
+}
+
+/** Parse a normal argument.
+*/
+
+void *miracle_command_parse_argument( command_argument cmd, int argument, arguments_types type, char *command )
+{
+       void *ret = NULL;
+       char *value = valerie_tokeniser_get_string( cmd->tokeniser, argument );
+
+       if ( value != NULL )
+       {
+               switch( type )
+               {
+                       case ATYPE_NONE:
+                               break;
+
+                       case ATYPE_FLOAT:
+                               ret = malloc( sizeof( float ) );
+                               if ( ret != NULL )
+                                       *( float * )ret = atof( value );
+                               break;
+
+                       case ATYPE_STRING:
+                               ret = strdup( value );
+                               break;
+                                       
+                       case ATYPE_PAIR:
+                               if ( strchr( command, '=' ) )
+                               {
+                                       char *ptr = strchr( command, '=' );
+                                       while ( *( ptr - 1 ) != ' ' ) 
+                                               ptr --;
+                                       ret = strdup( ptr );
+                                       ptr = ret;
+                                       while( ptr[ strlen( ptr ) - 1 ] == ' ' )
+                                               ptr[ strlen( ptr ) - 1 ] = '\0';
+                               }
+                               break;
+
+                       case ATYPE_INT:
+                               ret = malloc( sizeof( int ) );
+                               if ( ret != NULL )
+                                       *( int * )ret = atoi( value );
+                               break;
+               }
+       }
+
+       return ret;
+}
+
+/** Get the error code - note that we simply the success return.
+*/
+
+response_codes miracle_command_get_error( command_argument cmd )
+{
+       response_codes ret = valerie_response_get_error_code( cmd->response );
+       if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 )
+               ret = RESPONSE_SUCCESS;
+       return ret;
+}
+
+/** Execute the command.
+*/
+
+static valerie_response miracle_local_execute( miracle_local local, char *command )
+{
+       command_argument_t cmd;
+       cmd.parser = local->parser;
+       cmd.response = valerie_response_init( );
+       cmd.tokeniser = valerie_tokeniser_init( );
+       cmd.command = command;
+       cmd.unit = -1;
+       cmd.argument = NULL;
+       cmd.root_dir = local->root_dir;
+
+       /* Set the default error */
+       miracle_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND );
+
+       /* Parse the command */
+       if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+       {
+               int index = 0;
+               char *value = valerie_tokeniser_get_string( cmd.tokeniser, 0 );
+               int found = 0;
+
+               /* Strip quotes from all tokens */
+               for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+                       valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+               /* Search the vocabulary array for value */
+               for ( index = 1; !found && vocabulary[ index ].command != NULL; index ++ )
+                       if ( ( found = !strcasecmp( vocabulary[ index ].command, value ) ) )
+                               break;
+
+               /* If we found something, the handle the args and call the handler. */
+               if ( found )
+               {
+                       int position = 1;
+
+                       miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+                       if ( vocabulary[ index ].is_unit )
+                       {
+                               cmd.unit = miracle_command_parse_unit( &cmd, position );
+                               if ( cmd.unit == -1 )
+                                       miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+                               position ++;
+                       }
+
+                       if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
+                       {
+                               cmd.argument = miracle_command_parse_argument( &cmd, position, vocabulary[ index ].type, command );
+                               if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE )
+                                       miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+                               position ++;
+                       }
+
+                       if ( miracle_command_get_error( &cmd ) == RESPONSE_SUCCESS )
+                       {
+                               response_codes error = vocabulary[ index ].operation( &cmd );
+                               miracle_command_set_error( &cmd, error );
+                       }
+
+                       free( cmd.argument );
+               }
+       }
+
+       valerie_tokeniser_close( cmd.tokeniser );
+
+       return cmd.response;
+}
+
+static valerie_response miracle_local_receive( miracle_local local, char *command, char *doc )
+{
+       command_argument_t cmd;
+       cmd.parser = local->parser;
+       cmd.response = valerie_response_init( );
+       cmd.tokeniser = valerie_tokeniser_init( );
+       cmd.command = command;
+       cmd.unit = -1;
+       cmd.argument = NULL;
+       cmd.root_dir = local->root_dir;
+
+       /* Set the default error */
+       miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+       /* Parse the command */
+       if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+       {
+               int index = 0;
+               int position = 1;
+
+               /* Strip quotes from all tokens */
+               for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+                       valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+               cmd.unit = miracle_command_parse_unit( &cmd, position );
+               if ( cmd.unit == -1 )
+                       miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+               position ++;
+
+               miracle_receive( &cmd, doc );
+               miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+               free( cmd.argument );
+       }
+
+       valerie_tokeniser_close( cmd.tokeniser );
+
+       return cmd.response;
+}
+
+static valerie_response miracle_local_push( miracle_local local, char *command, mlt_service service )
+{
+       command_argument_t cmd;
+       cmd.parser = local->parser;
+       cmd.response = valerie_response_init( );
+       cmd.tokeniser = valerie_tokeniser_init( );
+       cmd.command = command;
+       cmd.unit = -1;
+       cmd.argument = NULL;
+       cmd.root_dir = local->root_dir;
+
+       /* Set the default error */
+       miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+       /* Parse the command */
+       if ( valerie_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 )
+       {
+               int index = 0;
+               int position = 1;
+
+               /* Strip quotes from all tokens */
+               for ( index = 0; index < valerie_tokeniser_count( cmd.tokeniser ); index ++ )
+                       valerie_util_strip( valerie_tokeniser_get_string( cmd.tokeniser, index ), '\"' );
+
+               cmd.unit = miracle_command_parse_unit( &cmd, position );
+               if ( cmd.unit == -1 )
+                       miracle_command_set_error( &cmd, RESPONSE_MISSING_ARG );
+               position ++;
+
+               miracle_push( &cmd, service );
+               miracle_command_set_error( &cmd, RESPONSE_SUCCESS );
+
+               free( cmd.argument );
+       }
+
+       valerie_tokeniser_close( cmd.tokeniser );
+
+       return cmd.response;
+}
+
+/** Close the parser.
+*/
+
+static void miracle_local_close( miracle_local local )
+{
+       miracle_delete_all_units();
+#ifdef linux
+       //pthread_kill_other_threads_np();
+       miracle_log( LOG_DEBUG, "Clean shutdown." );
+       //free( local );
+       //mlt_factory_close( );
+#endif
+}
diff --git a/src/miracle/miracle_local.h b/src/miracle/miracle_local.h
new file mode 100644 (file)
index 0000000..ffccb17
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * miracle_local.h -- Local Miracle Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MIRACLE_LOCAL_H_
+#define _MIRACLE_LOCAL_H_
+
+/* Application header files */
+#include <valerie/valerie_parser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Local parser API.
+*/
+
+extern valerie_parser miracle_parser_init_local( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_log.c b/src/miracle/miracle_log.c
new file mode 100644 (file)
index 0000000..62b7f73
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * miracle_log.c -- logging facility implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdio.h>
+
+#include "miracle_log.h"
+
+static int log_output = log_stderr;
+static int threshold = LOG_DEBUG;
+
+void miracle_log_init( enum log_output method, int new_threshold )
+{
+       log_output = method;
+       threshold = new_threshold;
+       if (method == log_syslog)
+               openlog( "miracle", LOG_CONS, LOG_DAEMON );
+
+}
+
+void miracle_log( int priority, const char *format, ... )
+{
+       va_list list;
+       va_start( list, format );
+       if ( LOG_PRI(priority) <= threshold )
+       {
+               if ( log_output == log_syslog )
+               {
+                               vsyslog( priority, format, list );
+               }
+               else
+               {
+                       char line[1024];
+                       if ( snprintf( line, 1024, "(%d) %s\n", priority, format ) != 0 )
+                               vfprintf( stderr, line, list );
+               }
+       }
+       va_end( list );
+}
diff --git a/src/miracle/miracle_log.h b/src/miracle/miracle_log.h
new file mode 100644 (file)
index 0000000..be21893
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * miracle_log.h -- logging facility header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <syslog.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum log_output {
+       log_stderr,
+       log_syslog
+};
+
+void miracle_log_init( enum log_output method, int threshold );
+void miracle_log( int priority, const char *format, ... );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_server.c b/src/miracle/miracle_server.c
new file mode 100644 (file)
index 0000000..629da55
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * miracle_server.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+/* Application header files */
+#include "miracle_server.h"
+#include "miracle_connection.h"
+#include "miracle_local.h"
+#include "miracle_log.h"
+#include "miracle_commands.h"
+#include <valerie/valerie_remote.h>
+#include <valerie/valerie_tokeniser.h>
+
+#define VERSION "0.0.1"
+
+static void miracle_command_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ] );
+}
+
+static void miracle_doc_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ], ( char * )args[ 2 ] );
+}
+
+static void miracle_push_received( mlt_listener listener, mlt_properties owner, miracle_server this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( valerie_response ** )args[ 0 ], ( char * )args[ 1 ], ( mlt_service )args[ 2 ] );
+}
+
+/** Initialise a server structure.
+*/
+
+miracle_server miracle_server_init( char *id )
+{
+       miracle_server server = malloc( sizeof( miracle_server_t ) );
+       if ( server != NULL )
+               memset( server, 0, sizeof( miracle_server_t ) );
+       if ( server != NULL && mlt_properties_init( &server->parent, server ) == 0 )
+       {
+               server->id = id;
+               server->port = DEFAULT_TCP_PORT;
+               server->socket = -1;
+               server->shutdown = 1;
+               mlt_events_init( &server->parent );
+               mlt_events_register( &server->parent, "command-received", ( mlt_transmitter )miracle_command_received );
+               mlt_events_register( &server->parent, "doc-received", ( mlt_transmitter )miracle_doc_received );
+               mlt_events_register( &server->parent, "push-received", ( mlt_transmitter )miracle_push_received );
+       }
+       return server;
+}
+
+const char *miracle_server_id( miracle_server server )
+{
+       return server != NULL && server->id != NULL ? server->id : "miracle";
+}
+
+void miracle_server_set_config( miracle_server server, const char *config )
+{
+       if ( server != NULL )
+       {
+               free( server->config );
+               server->config = config != NULL ? strdup( config ) : NULL;
+       }
+}
+
+/** Set the port of the server.
+*/
+
+void miracle_server_set_port( miracle_server server, int port )
+{
+       server->port = port;
+}
+
+void miracle_server_set_proxy( miracle_server server, char *proxy )
+{
+       valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+       server->proxy = 1;
+       server->remote_port = DEFAULT_TCP_PORT;
+       valerie_tokeniser_parse_new( tokeniser, proxy, ":" );
+       strcpy( server->remote_server, valerie_tokeniser_get_string( tokeniser, 0 ) );
+       if ( valerie_tokeniser_count( tokeniser ) == 2 )
+               server->remote_port = atoi( valerie_tokeniser_get_string( tokeniser, 1 ) );
+       valerie_tokeniser_close( tokeniser );
+}
+
+/** Wait for a connection.
+*/
+
+static int miracle_server_wait_for_connect( miracle_server server )
+{
+    struct timeval tv;
+    fd_set rfds;
+
+    /* Wait for a 1 second. */
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+
+    FD_ZERO( &rfds );
+    FD_SET( server->socket, &rfds );
+
+    return select( server->socket + 1, &rfds, NULL, NULL, &tv);
+}
+
+/** Run the server thread.
+*/
+
+static void *miracle_server_run( void *arg )
+{
+       miracle_server server = arg;
+       pthread_t cmd_parse_info;
+       connection_t *tmp = NULL;
+       pthread_attr_t thread_attributes;
+       socklen_t socksize;
+
+       socksize = sizeof( struct sockaddr );
+
+       miracle_log( LOG_NOTICE, "%s version %s listening on port %i", server->id, VERSION, server->port );
+
+       /* Create the initial thread. We want all threads to be created detached so
+          their resources get freed automatically. (CY: ... hmmph...) */
+       pthread_attr_init( &thread_attributes );
+       pthread_attr_setdetachstate( &thread_attributes, PTHREAD_CREATE_DETACHED );
+
+       while ( !server->shutdown )
+       {
+               /* Wait for a new connection. */
+               if ( miracle_server_wait_for_connect( server ) )
+               {
+                       /* Create a new block of data to hold a copy of the incoming connection for
+                          our server thread. The thread should free this when it terminates. */
+
+                       tmp = (connection_t*) malloc( sizeof(connection_t) );
+                       tmp->owner = &server->parent;
+                       tmp->parser = server->parser;
+                       tmp->fd = accept( server->socket, (struct sockaddr*) &(tmp->sin), &socksize );
+
+                       /* Pass the connection to a parser thread :-/ */
+                       if ( tmp->fd != -1 )
+                               pthread_create( &cmd_parse_info, &thread_attributes, parser_thread, tmp );
+               }
+       }
+
+       miracle_log( LOG_NOTICE, "%s version %s server terminated.", server->id, VERSION );
+
+       return NULL;
+}
+
+/** Execute the server thread.
+*/
+
+int miracle_server_execute( miracle_server server )
+{
+       int error = 0;
+       valerie_response response = NULL;
+       int index = 0;
+       struct sockaddr_in ServerAddr;
+       int flag = 1;
+
+       server->shutdown = 0;
+
+       ServerAddr.sin_family = AF_INET;
+       ServerAddr.sin_port = htons( server->port );
+       ServerAddr.sin_addr.s_addr = INADDR_ANY;
+       
+       /* Create socket, and bind to port. Listen there. Backlog = 5
+          should be sufficient for listen (). */
+       server->socket = socket( AF_INET, SOCK_STREAM, 0 );
+
+       if ( server->socket == -1 )
+       {
+               server->shutdown = 1;
+               perror( "socket" );
+               miracle_log( LOG_ERR, "%s unable to create socket.", server->id );
+               return -1;
+       }
+
+    setsockopt( server->socket, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof( int ) );
+
+       if ( bind( server->socket, (struct sockaddr *) &ServerAddr, sizeof (ServerAddr) ) != 0 )
+       {
+               server->shutdown = 1;
+               perror( "bind" );
+               miracle_log( LOG_ERR, "%s unable to bind to port %d.", server->id, server->port );
+               return -1;
+       }
+
+       if ( listen( server->socket, 5 ) != 0 )
+       {
+               server->shutdown = 1;
+               perror( "listen" );
+               miracle_log( LOG_ERR, "%s unable to listen on port %d.", server->id, server->port );
+               return -1;
+       }
+
+       fcntl( server->socket, F_SETFL, O_NONBLOCK );
+
+       if ( !server->proxy )
+       {
+               miracle_log( LOG_NOTICE, "Starting server on %d.", server->port );
+               server->parser = miracle_parser_init_local( );
+       }
+       else
+       {
+               miracle_log( LOG_NOTICE, "Starting proxy for %s:%d on %d.", server->remote_server, server->remote_port, server->port );
+               server->parser = valerie_parser_init_remote( server->remote_server, server->remote_port );
+       }
+
+       response = valerie_parser_connect( server->parser );
+
+       if ( response != NULL && valerie_response_get_error_code( response ) == 100 )
+       {
+               /* read configuration file */
+               if ( response != NULL && !server->proxy && server->config != NULL )
+               {
+                       valerie_response_close( response );
+                       response = valerie_parser_run( server->parser, server->config );
+
+                       if ( valerie_response_count( response ) > 1 )
+                       {
+                               if ( valerie_response_get_error_code( response ) > 299 )
+                                       miracle_log( LOG_ERR, "Error evaluating server configuration. Processing stopped." );
+                               for ( index = 0; index < valerie_response_count( response ); index ++ )
+                                       miracle_log( LOG_DEBUG, "%4d: %s", index, valerie_response_get_line( response, index ) );
+                       }
+               }
+
+               if ( response != NULL )
+               {
+                       int result;
+                       valerie_response_close( response );
+                       result = pthread_create( &server->thread, NULL, miracle_server_run, server );
+                       if ( result )
+                       {
+                               miracle_log( LOG_CRIT, "Failed to launch TCP listener thread" );
+                               error = -1;
+                       }
+               }
+       }
+       else
+       {
+               miracle_log( LOG_ERR, "Error connecting to parser. Processing stopped." );
+               server->shutdown = 1;
+               error = -1;
+       }
+
+       return error;
+}
+
+/** Fetch a units properties
+*/
+
+mlt_properties miracle_server_fetch_unit( miracle_server server, int index )
+{
+       miracle_unit unit = miracle_get_unit( index );
+       return unit != NULL ? unit->properties : NULL;
+}
+
+/** Shutdown the server.
+*/
+
+void miracle_server_shutdown( miracle_server server )
+{
+       if ( server != NULL && !server->shutdown )
+       {
+               server->shutdown = 1;
+               pthread_join( server->thread, NULL );
+               miracle_server_set_config( server, NULL );
+               valerie_parser_close( server->parser );
+               server->parser = NULL;
+               close( server->socket );
+       }
+}
+
+/** Close the server.
+*/
+
+void miracle_server_close( miracle_server server )
+{
+       if ( server != NULL && mlt_properties_dec_ref( &server->parent ) <= 0 )
+       {
+               mlt_properties_close( &server->parent );
+               miracle_server_shutdown( server );
+               free( server );
+       }
+}
diff --git a/src/miracle/miracle_server.h b/src/miracle/miracle_server.h
new file mode 100644 (file)
index 0000000..592ff3a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * miracle_server.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MIRACLE_SERVER_H_
+#define _MIRACLE_SERVER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include <valerie/valerie_parser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Servers default port
+*/
+
+#define DEFAULT_TCP_PORT 5250
+
+/** Structure for the server
+*/
+
+typedef struct
+{
+       struct mlt_properties_s parent;
+       char *id;
+       int port;
+       int socket;
+       valerie_parser parser;
+       pthread_t thread;
+       int shutdown;
+       int proxy;
+       char remote_server[ 50 ];
+       int remote_port;
+       char *config;
+}
+*miracle_server, miracle_server_t;
+
+/** API for the server
+*/
+
+extern miracle_server miracle_server_init( char * );
+extern const char *miracle_server_id( miracle_server );
+extern void miracle_server_set_config( miracle_server, const char * );
+extern void miracle_server_set_port( miracle_server, int );
+extern void miracle_server_set_proxy( miracle_server, char * );
+extern int miracle_server_execute( miracle_server );
+extern mlt_properties miracle_server_fetch_unit( miracle_server, int );
+extern void miracle_server_shutdown( miracle_server );
+extern void miracle_server_close( miracle_server );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_unit.c b/src/miracle/miracle_unit.c
new file mode 100644 (file)
index 0000000..4974e38
--- /dev/null
@@ -0,0 +1,777 @@
+/*
+ * miracle_unit.c -- Transmission Unit Implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <limits.h>
+
+#include <sys/mman.h>
+
+#include "miracle_unit.h"
+#include "miracle_log.h"
+#include "miracle_local.h"
+
+#include <framework/mlt.h>
+
+/* Forward references */
+static void miracle_unit_status_communicate( miracle_unit );
+
+/** Allocate a new DV transmission unit.
+
+    \return A new miracle_unit handle.
+*/
+
+miracle_unit miracle_unit_init( int index, char *constructor )
+{
+       miracle_unit this = NULL;
+       mlt_consumer consumer = NULL;
+
+       char *id = strdup( constructor );
+       char *arg = strchr( id, ':' );
+
+       if ( arg != NULL )
+               *arg ++ = '\0';
+
+       consumer = mlt_factory_consumer( NULL, id, arg );
+
+       if ( consumer != NULL )
+       {
+               mlt_playlist playlist = mlt_playlist_init( );
+               this = calloc( sizeof( miracle_unit_t ), 1 );
+               this->properties = mlt_properties_new( );
+               mlt_properties_init( this->properties, this );
+               mlt_properties_set_int( this->properties, "unit", index );
+               mlt_properties_set_int( this->properties, "generation", 0 );
+               mlt_properties_set( this->properties, "constructor", constructor );
+               mlt_properties_set( this->properties, "id", id );
+               mlt_properties_set( this->properties, "arg", arg );
+               mlt_properties_set_data( this->properties, "consumer", consumer, 0, ( mlt_destructor )mlt_consumer_close, NULL );
+               mlt_properties_set_data( this->properties, "playlist", playlist, 0, ( mlt_destructor )mlt_playlist_close, NULL );
+               mlt_consumer_connect( consumer, MLT_PLAYLIST_SERVICE( playlist ) );
+       }
+
+       return this;
+}
+
+static char *strip_root( miracle_unit unit, char *file )
+{
+       mlt_properties properties = unit->properties;
+       char *root = mlt_properties_get( properties, "root" );
+       if ( file != NULL && root != NULL )
+       {
+               int length = strlen( root );
+               if ( root[ length - 1 ] == '/' )
+                       length --;
+               if ( !strncmp( file, root, length ) )
+                       file += length;
+       }
+       return file;
+}
+
+/** Communicate the current status to all threads waiting on the notifier.
+*/
+
+static void miracle_unit_status_communicate( miracle_unit unit )
+{
+       if ( unit != NULL )
+       {
+               mlt_properties properties = unit->properties;
+               char *root_dir = mlt_properties_get( properties, "root" );
+               valerie_notifier notifier = mlt_properties_get_data( properties, "notifier", NULL );
+               valerie_status_t status;
+
+               if ( root_dir != NULL && notifier != NULL )
+               {
+                       if ( miracle_unit_get_status( unit, &status ) == 0 )
+                               /* if ( !( ( status.status == unit_playing || status.status == unit_paused ) &&
+                                               strcmp( status.clip, "" ) && 
+                                       !strcmp( status.tail_clip, "" ) && 
+                                               status.position == 0 && 
+                                               status.in == 0 && 
+                                               status.out == 0 ) ) */
+                                       valerie_notifier_put( notifier, &status );
+               }
+       }
+}
+
+/** Set the notifier info
+*/
+
+void miracle_unit_set_notifier( miracle_unit this, valerie_notifier notifier, char *root_dir )
+{
+       mlt_properties properties = this->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_properties playlist_properties = MLT_PLAYLIST_PROPERTIES( playlist );
+
+       mlt_properties_set( properties, "root", root_dir );
+       mlt_properties_set_data( properties, "notifier", notifier, 0, NULL, NULL );
+       mlt_properties_set_data( playlist_properties, "notifier_arg", this, 0, NULL, NULL );
+       mlt_properties_set_data( playlist_properties, "notifier", miracle_unit_status_communicate, 0, NULL, NULL );
+
+       miracle_unit_status_communicate( this );
+}
+
+/** Create or locate a producer for the file specified.
+*/
+
+static mlt_producer locate_producer( miracle_unit unit, char *file )
+{
+       // Try to get the profile from the consumer
+       mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+       mlt_profile profile = NULL;
+
+       if ( consumer != NULL )
+       {
+               profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
+       }
+       return mlt_factory_producer( profile, "fezzik", file );
+}
+
+/** Update the generation count.
+*/
+
+static void update_generation( miracle_unit unit )
+{
+       mlt_properties properties = unit->properties;
+       int generation = mlt_properties_get_int( properties, "generation" );
+       mlt_properties_set_int( properties, "generation", ++ generation );
+}
+
+/** Wipe all clips on the playlist for this unit.
+*/
+
+static void clear_unit( miracle_unit unit )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+
+       mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+       mlt_playlist_clear( playlist );
+       mlt_producer_seek( producer, 0 );
+       mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+
+       update_generation( unit );
+}
+
+/** Wipe all but the playing clip from the unit.
+*/
+
+static void clean_unit( miracle_unit unit )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_playlist_clip_info info;
+       int current = mlt_playlist_current_clip( playlist );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+       mlt_position position = mlt_producer_frame( producer );
+       double speed = mlt_producer_get_speed( producer );
+       mlt_playlist_get_clip_info( playlist, &info, current );
+
+       if ( info.producer != NULL )
+       {
+               mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( info.producer ) );
+               position -= info.start;
+               clear_unit( unit );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_playlist_append_io( playlist, info.producer, info.frame_in, info.frame_out );
+               mlt_producer_seek( producer, position );
+               mlt_producer_set_speed( producer, speed );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_producer_close( info.producer );
+       }
+       
+       update_generation( unit );
+}
+
+/** Remove everything up to the current clip from the unit.
+*/
+
+static void wipe_unit( miracle_unit unit )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_playlist_clip_info info;
+       int current = mlt_playlist_current_clip( playlist );
+       mlt_playlist_get_clip_info( playlist, &info, current );
+
+       if ( info.producer != NULL && info.start > 0 )
+       {
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_playlist_remove_region( playlist, 0, info.start - 1 );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+       }
+       
+       update_generation( unit );
+}
+
+/** Generate a report on all loaded clips.
+*/
+
+void miracle_unit_report_list( miracle_unit unit, valerie_response response )
+{
+       int i;
+       mlt_properties properties = unit->properties;
+       int generation = mlt_properties_get_int( properties, "generation" );
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+       valerie_response_printf( response, 1024, "%d\n", generation );
+               
+       for ( i = 0; i < mlt_playlist_count( playlist ); i ++ )
+       {
+               mlt_playlist_clip_info info;
+               char *title;
+               mlt_playlist_get_clip_info( playlist , &info, i );
+               title = mlt_properties_get( MLT_PRODUCER_PROPERTIES( info.producer ), "title" );
+               if ( title == NULL )
+                       title = strip_root( unit, info.resource );
+               valerie_response_printf( response, 10240, "%d \"%s\" %d %d %d %d %.2f\n", 
+                                                                i, 
+                                                                title,
+                                                                info.frame_in, 
+                                                                info.frame_out,
+                                                                info.frame_count, 
+                                                                info.length, 
+                                                                info.fps );
+       }
+       valerie_response_printf( response, 1024, "\n" );
+}
+
+/** Load a clip into the unit clearing existing play list.
+
+    \todo error handling
+    \param unit A miracle_unit handle.
+    \param clip The absolute file name of the clip to load.
+    \param in   The starting frame (-1 for 0)
+       \param out  The ending frame (-1 for maximum)
+*/
+
+valerie_error_code miracle_unit_load( miracle_unit unit, char *clip, int32_t in, int32_t out, int flush )
+{
+       // Now try to create a producer
+       mlt_producer instance = locate_producer( unit, clip );
+
+       if ( instance != NULL )
+       {
+               mlt_properties properties = unit->properties;
+               mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+               int original = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_playlist_append_io( playlist, instance, in, out );
+               mlt_playlist_remove_region( playlist, 0, original );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               miracle_log( LOG_DEBUG, "loaded clip %s", clip );
+               update_generation( unit );
+               miracle_unit_status_communicate( unit );
+               mlt_producer_close( instance );
+               return valerie_ok;
+       }
+
+       return valerie_invalid_file;
+}
+
+valerie_error_code miracle_unit_insert( miracle_unit unit, char *clip, int index, int32_t in, int32_t out )
+{
+       mlt_producer instance = locate_producer( unit, clip );
+
+       if ( instance != NULL )
+       {
+               mlt_properties properties = unit->properties;
+               mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+               fprintf( stderr, "inserting clip %s before %d\n", clip, index );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_playlist_insert( playlist, instance, index, in, out );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               miracle_log( LOG_DEBUG, "inserted clip %s at %d", clip, index );
+               update_generation( unit );
+               miracle_unit_status_communicate( unit );
+               mlt_producer_close( instance );
+               return valerie_ok;
+       }
+
+       return valerie_invalid_file;
+}
+
+valerie_error_code miracle_unit_remove( miracle_unit unit, int index )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+       mlt_playlist_remove( playlist, index );
+       mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+       miracle_log( LOG_DEBUG, "removed clip at %d", index );
+       update_generation( unit );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+valerie_error_code miracle_unit_clean( miracle_unit unit )
+{
+       clean_unit( unit );
+       miracle_log( LOG_DEBUG, "Cleaned playlist" );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+valerie_error_code miracle_unit_wipe( miracle_unit unit )
+{
+       wipe_unit( unit );
+       miracle_log( LOG_DEBUG, "Wiped playlist" );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+valerie_error_code miracle_unit_clear( miracle_unit unit )
+{
+       mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+       clear_unit( unit );
+       mlt_consumer_purge( consumer );
+       miracle_log( LOG_DEBUG, "Cleared playlist" );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+valerie_error_code miracle_unit_move( miracle_unit unit, int src, int dest )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+       mlt_playlist_move( playlist, src, dest );
+       mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+       miracle_log( LOG_DEBUG, "moved clip %d to %d", src, dest );
+       update_generation( unit );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+/** Add a clip to the unit play list.
+
+    \todo error handling
+    \param unit A miracle_unit handle.
+    \param clip The absolute file name of the clip to load.
+    \param in   The starting frame (-1 for 0)
+       \param out  The ending frame (-1 for maximum)
+*/
+
+valerie_error_code miracle_unit_append( miracle_unit unit, char *clip, int32_t in, int32_t out )
+{
+       mlt_producer instance = locate_producer( unit, clip );
+
+       if ( instance != NULL )
+       {
+               mlt_properties properties = unit->properties;
+               mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               mlt_playlist_append_io( playlist, instance, in, out );
+               miracle_log( LOG_DEBUG, "appended clip %s", clip );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               update_generation( unit );
+               miracle_unit_status_communicate( unit );
+               mlt_producer_close( instance );
+               return valerie_ok;
+       }
+
+       return valerie_invalid_file;
+}
+
+/** Add an mlt_service to the playlist
+
+    \param unit A miracle_unit handle.
+    \param service the service to add
+*/
+
+valerie_error_code miracle_unit_append_service( miracle_unit unit, mlt_service service )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+       mlt_playlist_append( playlist, ( mlt_producer )service );
+       mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+       miracle_log( LOG_DEBUG, "appended clip" );
+       update_generation( unit );
+       miracle_unit_status_communicate( unit );
+       return valerie_ok;
+}
+
+/** Start playing the unit.
+
+    \todo error handling
+    \param unit A miracle_unit handle.
+    \param speed An integer that specifies the playback rate as a
+                 percentage multiplied by 100.
+*/
+
+void miracle_unit_play( miracle_unit_t *unit, int speed )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+       mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+       mlt_producer_set_speed( producer, ( double )speed / 1000 );
+       mlt_consumer_start( consumer );
+       miracle_unit_status_communicate( unit );
+}
+
+/** Stop playback.
+
+    Terminates the dv_pump and halts dv1394 transmission.
+
+    \param unit A miracle_unit handle.
+*/
+
+void miracle_unit_terminate( miracle_unit unit )
+{
+       mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+       mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+       mlt_producer_set_speed( producer, 0 );
+       mlt_consumer_stop( consumer );
+       miracle_unit_status_communicate( unit );
+}
+
+/** Query the status of unit playback.
+
+    \param unit A miracle_unit handle.
+    \return 1 if the unit is not playing, 0 if playing.
+*/
+
+int miracle_unit_has_terminated( miracle_unit unit )
+{
+       mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+       return mlt_consumer_is_stopped( consumer );
+}
+
+/** Transfer the currently loaded clip to another unit
+*/
+
+int miracle_unit_transfer( miracle_unit dest_unit, miracle_unit src_unit )
+{
+       int i;
+       mlt_properties dest_properties = dest_unit->properties;
+       mlt_playlist dest_playlist = mlt_properties_get_data( dest_properties, "playlist", NULL );
+       mlt_properties src_properties = src_unit->properties;
+       mlt_playlist src_playlist = mlt_properties_get_data( src_properties, "playlist", NULL );
+       mlt_playlist tmp_playlist = mlt_playlist_init( );
+
+       for ( i = 0; i < mlt_playlist_count( src_playlist ); i ++ )
+       {
+               mlt_playlist_clip_info info;
+               mlt_playlist_get_clip_info( src_playlist, &info, i );
+               if ( info.producer != NULL )
+                       mlt_playlist_append_io( tmp_playlist, info.producer, info.frame_in, info.frame_out );
+       }
+
+       clear_unit( src_unit );
+
+       mlt_service_lock( MLT_PLAYLIST_SERVICE( dest_playlist ) );
+
+       for ( i = 0; i < mlt_playlist_count( tmp_playlist ); i ++ )
+       {
+               mlt_playlist_clip_info info;
+               mlt_playlist_get_clip_info( tmp_playlist, &info, i );
+               if ( info.producer != NULL )
+                       mlt_playlist_append_io( dest_playlist, info.producer, info.frame_in, info.frame_out );
+       }
+
+       mlt_service_unlock( MLT_PLAYLIST_SERVICE( dest_playlist ) );
+
+       update_generation( dest_unit );
+       miracle_unit_status_communicate( dest_unit );
+
+       mlt_playlist_close( tmp_playlist );
+
+       return 0;
+}
+
+/** Determine if unit is offline.
+*/
+
+int miracle_unit_is_offline( miracle_unit unit )
+{
+       return 0;
+}
+
+/** Obtain the status for a given unit
+*/
+
+int miracle_unit_get_status( miracle_unit unit, valerie_status status )
+{
+       int error = unit == NULL;
+
+       memset( status, 0, sizeof( valerie_status_t ) );
+
+       if ( !error )
+       {
+               mlt_properties properties = unit->properties;
+               mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+               mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+               mlt_producer clip = mlt_playlist_current( playlist );
+
+               mlt_playlist_clip_info info;
+               int clip_index = mlt_playlist_current_clip( playlist );
+               mlt_playlist_get_clip_info( playlist, &info, clip_index );
+
+               if ( info.resource != NULL && strcmp( info.resource, "" ) )
+               {
+                       char *title = mlt_properties_get( MLT_PRODUCER_PROPERTIES( info.producer ), "title" );
+                       if ( title == NULL )
+                               title = strip_root( unit, info.resource );
+                       strncpy( status->clip, title, sizeof( status->clip ) );
+                       status->speed = (int)( mlt_producer_get_speed( producer ) * 1000.0 );
+                       status->fps = mlt_producer_get_fps( producer );
+                       status->in = info.frame_in;
+                       status->out = info.frame_out;
+                       status->position = mlt_producer_frame( clip );
+                       status->length = mlt_producer_get_length( clip );
+                       strncpy( status->tail_clip, title, sizeof( status->tail_clip ) );
+                       status->tail_in = info.frame_in;
+                       status->tail_out = info.frame_out;
+                       status->tail_position = mlt_producer_frame( clip );
+                       status->tail_length = mlt_producer_get_length( clip );
+                       status->clip_index = mlt_playlist_current_clip( playlist );
+                       status->seek_flag = 1;
+               }
+
+               status->generation = mlt_properties_get_int( properties, "generation" );
+
+               if ( miracle_unit_has_terminated( unit ) )
+                       status->status = unit_stopped;
+               else if ( !strcmp( status->clip, "" ) )
+                       status->status = unit_not_loaded;
+               else if ( status->speed == 0 )
+                       status->status = unit_paused;
+               else
+                       status->status = unit_playing;
+       }
+       else
+       {
+               status->status = unit_undefined;
+       }
+
+       status->unit = mlt_properties_get_int( unit->properties, "unit" );
+
+       return error;
+}
+
+/** Change position in the playlist.
+*/
+
+void miracle_unit_change_position( miracle_unit unit, int clip, int32_t position )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+       mlt_playlist_clip_info info;
+
+       if ( clip < 0 )
+       {
+               clip = 0;
+               position = 0;
+       }
+       else if ( clip >= mlt_playlist_count( playlist ) )
+       {
+               clip = mlt_playlist_count( playlist ) - 1;
+               position = INT_MAX;
+       }
+
+       if ( mlt_playlist_get_clip_info( playlist, &info, clip ) == 0 )
+       {
+               int32_t frame_start = info.start;
+               int32_t frame_offset = position;
+
+               if ( frame_offset < 0 )
+                       frame_offset = info.frame_out;
+               if ( frame_offset < info.frame_in )
+                       frame_offset = info.frame_in;
+               if ( frame_offset >= info.frame_out )
+                       frame_offset = info.frame_out;
+               
+               mlt_producer_seek( producer, frame_start + frame_offset - info.frame_in );
+       }
+
+       miracle_unit_status_communicate( unit );
+}
+
+/** Get the index of the current clip.
+*/
+
+int    miracle_unit_get_current_clip( miracle_unit unit )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       int clip_index = mlt_playlist_current_clip( playlist );
+       return clip_index;
+}
+
+/** Set a clip's in point
+*/
+
+int miracle_unit_set_clip_in( miracle_unit unit, int index, int32_t position )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_playlist_clip_info info;
+       int error = mlt_playlist_get_clip_info( playlist, &info, index );
+
+       if ( error == 0 )
+       {
+               miracle_unit_play( unit, 0 );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               error = mlt_playlist_resize_clip( playlist, index, position, info.frame_out );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               update_generation( unit );
+               miracle_unit_change_position( unit, index, 0 );
+       }
+
+       return error;
+}
+
+/** Set a clip's out point.
+*/
+
+int miracle_unit_set_clip_out( miracle_unit unit, int index, int32_t position )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_playlist_clip_info info;
+       int error = mlt_playlist_get_clip_info( playlist, &info, index );
+
+       if ( error == 0 )
+       {
+               miracle_unit_play( unit, 0 );
+               mlt_service_lock( MLT_PLAYLIST_SERVICE( playlist ) );
+               error = mlt_playlist_resize_clip( playlist, index, info.frame_in, position );
+               mlt_service_unlock( MLT_PLAYLIST_SERVICE( playlist ) );
+               update_generation( unit );
+               miracle_unit_status_communicate( unit );
+               miracle_unit_change_position( unit, index, -1 );
+       }
+
+       return error;
+}
+
+/** Step by specified position.
+*/
+
+void miracle_unit_step( miracle_unit unit, int32_t offset )
+{
+       mlt_properties properties = unit->properties;
+       mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+       mlt_producer producer = MLT_PLAYLIST_PRODUCER( playlist );
+       mlt_position position = mlt_producer_frame( producer );
+       mlt_producer_seek( producer, position + offset );
+}
+
+/** Set the unit's clip mode regarding in and out points.
+*/
+
+//void miracle_unit_set_mode( miracle_unit unit, dv_player_clip_mode mode )
+//{
+       //dv_player player = miracle_unit_get_dv_player( unit );
+       //if ( player != NULL )
+               //dv_player_set_clip_mode( player, mode );
+       //miracle_unit_status_communicate( unit );
+//}
+
+/** Get the unit's clip mode regarding in and out points.
+*/
+
+//dv_player_clip_mode miracle_unit_get_mode( miracle_unit unit )
+//{
+       //dv_player player = miracle_unit_get_dv_player( unit );
+       //return dv_player_get_clip_mode( player );
+//}
+
+/** Set the unit's clip mode regarding eof handling.
+*/
+
+//void miracle_unit_set_eof_action( miracle_unit unit, dv_player_eof_action action )
+//{
+       //dv_player player = miracle_unit_get_dv_player( unit );
+       //dv_player_set_eof_action( player, action );
+       //miracle_unit_status_communicate( unit );
+//}
+
+/** Get the unit's clip mode regarding eof handling.
+*/
+
+//dv_player_eof_action miracle_unit_get_eof_action( miracle_unit unit )
+//{
+       //dv_player player = miracle_unit_get_dv_player( unit );
+       //return dv_player_get_eof_action( player );
+//}
+
+int miracle_unit_set( miracle_unit unit, char *name_value )
+{
+       mlt_properties properties = NULL;
+
+       if ( strncmp( name_value, "consumer.", 9 ) )
+       {
+               mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+               properties = MLT_PLAYLIST_PROPERTIES( playlist );
+       }
+       else
+       {
+               mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+               properties = MLT_CONSUMER_PROPERTIES( consumer );
+               name_value += 9;
+       }
+
+       return mlt_properties_parse( properties, name_value );
+}
+
+char *miracle_unit_get( miracle_unit unit, char *name )
+{
+       mlt_playlist playlist = mlt_properties_get_data( unit->properties, "playlist", NULL );
+       mlt_properties properties = MLT_PLAYLIST_PROPERTIES( playlist );
+       return mlt_properties_get( properties, name );
+}
+
+/** Release the unit
+
+    \todo error handling
+    \param unit A miracle_unit handle.
+*/
+
+void miracle_unit_close( miracle_unit unit )
+{
+       if ( unit != NULL )
+       {
+               miracle_log( LOG_DEBUG, "closing unit..." );
+               miracle_unit_terminate( unit );
+               mlt_properties_close( unit->properties );
+               free( unit );
+               miracle_log( LOG_DEBUG, "... unit closed." );
+       }
+}
+
diff --git a/src/miracle/miracle_unit.h b/src/miracle/miracle_unit.h
new file mode 100644 (file)
index 0000000..a78500f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * dvunit.h -- Transmission Unit Header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_UNIT_H_
+#define _DV_UNIT_H_
+
+#include <pthread.h>
+
+#include <framework/mlt_properties.h>
+#include <valerie/valerie.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct
+{
+       mlt_properties properties;
+} 
+miracle_unit_t, *miracle_unit;
+
+extern miracle_unit         miracle_unit_init( int index, char *arg );
+extern void                            miracle_unit_report_list( miracle_unit unit, valerie_response response );
+extern void                 miracle_unit_allow_stdin( miracle_unit unit, int flag );
+extern valerie_error_code   miracle_unit_load( miracle_unit unit, char *clip, int32_t in, int32_t out, int flush );
+extern valerie_error_code      miracle_unit_insert( miracle_unit unit, char *clip, int index, int32_t in, int32_t out );
+extern valerie_error_code   miracle_unit_append( miracle_unit unit, char *clip, int32_t in, int32_t out );
+extern valerie_error_code   miracle_unit_append_service( miracle_unit unit, mlt_service service );
+extern valerie_error_code      miracle_unit_remove( miracle_unit unit, int index );
+extern valerie_error_code      miracle_unit_clean( miracle_unit unit );
+extern valerie_error_code      miracle_unit_wipe( miracle_unit unit );
+extern valerie_error_code      miracle_unit_clear( miracle_unit unit );
+extern valerie_error_code      miracle_unit_move( miracle_unit unit, int src, int dest );
+extern int                  miracle_unit_transfer( miracle_unit dest_unit, miracle_unit src_unit );
+extern void                 miracle_unit_play( miracle_unit_t *unit, int speed );
+extern void                 miracle_unit_terminate( miracle_unit );
+extern int                  miracle_unit_has_terminated( miracle_unit );
+extern int                  miracle_unit_get_nodeid( miracle_unit unit );
+extern int                  miracle_unit_get_channel( miracle_unit unit );
+extern int                  miracle_unit_is_offline( miracle_unit unit );
+extern void                 miracle_unit_set_notifier( miracle_unit, valerie_notifier, char * );
+extern int                  miracle_unit_get_status( miracle_unit, valerie_status );
+extern void                 miracle_unit_change_position( miracle_unit, int, int32_t position );
+extern void                 miracle_unit_change_speed( miracle_unit unit, int speed );
+extern int                  miracle_unit_set_clip_in( miracle_unit unit, int index, int32_t position );
+extern int                  miracle_unit_set_clip_out( miracle_unit unit, int index, int32_t position );
+//extern void                 miracle_unit_set_mode( miracle_unit unit, dv_player_clip_mode mode );
+//extern dv_player_clip_mode  miracle_unit_get_mode( miracle_unit unit );
+//extern void                 miracle_unit_set_eof_action( miracle_unit unit, dv_player_eof_action mode );
+//extern dv_player_eof_action miracle_unit_get_eof_action( miracle_unit unit );
+extern void                 miracle_unit_step( miracle_unit unit, int32_t offset );
+extern void                 miracle_unit_close( miracle_unit unit );
+extern void                 miracle_unit_suspend( miracle_unit );
+extern void                 miracle_unit_restore( miracle_unit );
+extern int                                     miracle_unit_set( miracle_unit, char *name_value );
+extern char *                          miracle_unit_get( miracle_unit, char *name );
+extern int                                     miracle_unit_get_current_clip( miracle_unit );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/miracle/miracle_unit_commands.c b/src/miracle/miracle_unit_commands.c
new file mode 100644 (file)
index 0000000..97fb2dd
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * unit_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "miracle_unit.h"
+#include "miracle_commands.h"
+#include "miracle_log.h"
+
+int miracle_load( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       char *filename = (char*) cmd_arg->argument;
+       char fullname[1024];
+       int flush = 1;
+       char *service;
+
+       if ( filename[0] == '!' )
+       {
+               flush = 0;
+               filename ++;
+       }
+
+       service = strchr( filename, ':' );
+       if ( service != NULL )
+       {
+               service = filename;
+               filename = strchr( service, ':' );
+               *filename ++ = '\0';
+               
+               if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+                       filename++;
+       
+               snprintf( fullname, 1023, "%s:%s%s", service, cmd_arg->root_dir, filename );
+       }
+       else
+       {
+               if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+                       filename++;
+
+               snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+       }
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               int32_t in = -1, out = -1;
+               if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 5 )
+               {
+                       in = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 3 ) );
+                       out = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+               }
+               if ( miracle_unit_load( unit, fullname, in, out, flush ) != valerie_ok )
+                       return RESPONSE_BAD_FILE;
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_list( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit( cmd_arg->unit );
+
+       if ( unit != NULL )
+       {
+               miracle_unit_report_list( unit, cmd_arg->response );
+               return RESPONSE_SUCCESS;
+       }
+
+       return RESPONSE_INVALID_UNIT;
+}
+
+static int parse_clip( command_argument cmd_arg, int arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       int clip = miracle_unit_get_current_clip( unit );
+       
+       if ( valerie_tokeniser_count( cmd_arg->tokeniser ) > arg )
+       {
+               char *token = valerie_tokeniser_get_string( cmd_arg->tokeniser, arg );
+               if ( token[ 0 ] == '+' )
+                       clip += atoi( token + 1 );
+               else if ( token[ 0 ] == '-' )
+                       clip -= atoi( token + 1 );
+               else
+                       clip = atoi( token );
+       }
+       
+       return clip;
+}
+
+int miracle_insert( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       char *filename = (char*) cmd_arg->argument;
+       char fullname[1024];
+
+       if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+               filename++;
+
+       snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               long in = -1, out = -1;
+               int index = parse_clip( cmd_arg, 3 );
+               
+               if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 6 )
+               {
+                       in = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+                       out = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 5 ) );
+               }
+               
+               switch( miracle_unit_insert( unit, fullname, index, in, out ) )
+               {
+                       case valerie_ok:
+                               return RESPONSE_SUCCESS;
+                       default:
+                               return RESPONSE_BAD_FILE;
+               }
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_remove( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               int index = parse_clip( cmd_arg, 2 );
+                       
+               if ( miracle_unit_remove( unit, index ) != valerie_ok )
+                       return RESPONSE_BAD_FILE;
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_clean( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               if ( miracle_unit_clean( unit ) != valerie_ok )
+                       return RESPONSE_BAD_FILE;
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_wipe( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               if ( miracle_unit_wipe( unit ) != valerie_ok )
+                       return RESPONSE_BAD_FILE;
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_clear( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               if ( miracle_unit_clear( unit ) != valerie_ok )
+                       return RESPONSE_BAD_FILE;
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_move( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if ( unit != NULL )
+       {
+               if ( valerie_tokeniser_count( cmd_arg->tokeniser ) > 2 )
+               {
+                       int src = parse_clip( cmd_arg, 2 );
+                       int dest = parse_clip( cmd_arg, 3 );
+                       
+                       if ( miracle_unit_move( unit, src, dest ) != valerie_ok )
+                               return RESPONSE_BAD_FILE;
+               }
+               else
+               {
+                       return RESPONSE_MISSING_ARG;
+               }
+       }
+       else
+       {
+               return RESPONSE_INVALID_UNIT;
+       }
+
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_append( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       char *filename = (char*) cmd_arg->argument;
+       char fullname[1024];
+
+       if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' )
+               filename++;
+
+       snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename );
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               int32_t in = -1, out = -1;
+               if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 5 )
+               {
+                       in = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 3 ) );
+                       out = atol( valerie_tokeniser_get_string( cmd_arg->tokeniser, 4 ) );
+               }
+               switch ( miracle_unit_append( unit, fullname, in, out ) )
+               {
+                       case valerie_ok:
+                               return RESPONSE_SUCCESS;
+                       default:
+                               return RESPONSE_BAD_FILE;
+               }
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_push( command_argument cmd_arg, mlt_service service )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit != NULL && service != NULL )
+               if ( miracle_unit_append_service( unit, service ) == valerie_ok )
+                       return RESPONSE_SUCCESS;
+       return RESPONSE_BAD_FILE;
+}
+
+int miracle_receive( command_argument cmd_arg, char *doc )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit != NULL )
+       {
+               // Get the consumer's profile
+               mlt_consumer consumer = mlt_properties_get_data( unit->properties, "consumer", NULL );
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) );
+               mlt_producer producer = mlt_factory_producer( profile, "westley-xml", doc );
+               if ( producer != NULL )
+               {
+                       if ( miracle_unit_append_service( unit, MLT_PRODUCER_SERVICE( producer ) ) == valerie_ok )
+                       {
+                               mlt_producer_close( producer );
+                               return RESPONSE_SUCCESS;
+                       }
+                       mlt_producer_close( producer );
+               }
+       }
+       return RESPONSE_BAD_FILE;
+}
+
+int miracle_play( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if ( unit == NULL )
+       {
+               return RESPONSE_INVALID_UNIT;
+       }
+       else
+       {
+               int speed = 1000;
+               if ( valerie_tokeniser_count( cmd_arg->tokeniser ) == 3 )
+                       speed = atoi( valerie_tokeniser_get_string( cmd_arg->tokeniser, 2 ) );
+               miracle_unit_play( unit, speed );
+       }
+
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_stop( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else 
+               miracle_unit_terminate( unit );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_pause( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else 
+               miracle_unit_play( unit, 0 );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_rewind( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else 
+               miracle_unit_play( unit, -2000 );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_step( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               miracle_unit_play( unit, 0 );
+               miracle_unit_step( unit, *(int*) cmd_arg->argument );
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_goto( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       int clip = parse_clip( cmd_arg, 3 );
+       
+       if (unit == NULL || miracle_unit_is_offline(unit))
+               return RESPONSE_INVALID_UNIT;
+       else
+               miracle_unit_change_position( unit, clip, *(int*) cmd_arg->argument );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_ff( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else 
+               miracle_unit_play( unit, 2000 );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_set_in_point( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       int clip = parse_clip( cmd_arg, 3 );
+
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               int position = *(int *) cmd_arg->argument;
+
+               switch( miracle_unit_set_clip_in( unit, clip, position ) )
+               {
+                       case -1:
+                               return RESPONSE_BAD_FILE;
+                       case -2:
+                               return RESPONSE_OUT_OF_RANGE;
+               }
+       }
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_set_out_point( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       int clip = parse_clip( cmd_arg, 3 );
+       
+       if ( unit == NULL )
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               int position = *(int *) cmd_arg->argument;
+
+               switch( miracle_unit_set_clip_out( unit, clip, position ) )
+               {
+                       case -1:
+                               return RESPONSE_BAD_FILE;
+                       case -2:
+                               return RESPONSE_OUT_OF_RANGE;
+               }
+       }
+
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_get_unit_status( command_argument cmd_arg )
+{
+       valerie_status_t status;
+       int error = miracle_unit_get_status( miracle_get_unit( cmd_arg->unit ), &status );
+
+       if ( error == -1 )
+               return RESPONSE_INVALID_UNIT;
+       else
+       {
+               char text[ 10240 ];
+               valerie_response_printf( cmd_arg->response, sizeof( text ), valerie_status_serialise( &status, text, sizeof( text ) ) );
+               return RESPONSE_SUCCESS_1;
+       }
+       return 0;
+}
+
+
+int miracle_set_unit_property( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       char *name_value = (char*) cmd_arg->argument;
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else
+               miracle_unit_set( unit, name_value );
+       return RESPONSE_SUCCESS;
+}
+
+int miracle_get_unit_property( command_argument cmd_arg )
+{
+       miracle_unit unit = miracle_get_unit(cmd_arg->unit);
+       char *name = (char*) cmd_arg->argument;
+       char *value = miracle_unit_get( unit, name );
+       if (unit == NULL)
+               return RESPONSE_INVALID_UNIT;
+       else if ( value != NULL )
+               valerie_response_printf( cmd_arg->response, 1024, "%s\n", value );
+       return RESPONSE_SUCCESS;
+}
+
+
+int miracle_transfer( command_argument cmd_arg )
+{
+       miracle_unit src_unit = miracle_get_unit(cmd_arg->unit);
+       int dest_unit_id = -1;
+       char *string = (char*) cmd_arg->argument;
+       if ( string != NULL && ( string[ 0 ] == 'U' || string[ 0 ] == 'u' ) && strlen( string ) > 1 )
+               dest_unit_id = atoi( string + 1 );
+       
+       if ( src_unit != NULL && dest_unit_id != -1 )
+       {
+               miracle_unit dest_unit = miracle_get_unit( dest_unit_id );
+               if ( dest_unit != NULL && !miracle_unit_is_offline(dest_unit) && dest_unit != src_unit )
+               {
+                       miracle_unit_transfer( dest_unit, src_unit );
+                       return RESPONSE_SUCCESS;
+               }
+       }
+       return RESPONSE_INVALID_UNIT;
+}
diff --git a/src/miracle/miracle_unit_commands.h b/src/miracle/miracle_unit_commands.h
new file mode 100644 (file)
index 0000000..cfd51a7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * unit_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _UNIT_COMMANDS_H_
+#define _UNIT_COMMANDS_H_
+
+#include "miracle_connection.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern response_codes miracle_list( command_argument );
+extern response_codes miracle_load( command_argument );
+extern response_codes miracle_insert( command_argument );
+extern response_codes miracle_remove( command_argument );
+extern response_codes miracle_clean( command_argument );
+extern response_codes miracle_wipe( command_argument );
+extern response_codes miracle_clear( command_argument );
+extern response_codes miracle_move( command_argument );
+extern response_codes miracle_append( command_argument );
+extern response_codes miracle_play( command_argument );
+extern response_codes miracle_stop( command_argument );
+extern response_codes miracle_pause( command_argument );
+extern response_codes miracle_rewind( command_argument );
+extern response_codes miracle_step( command_argument );
+extern response_codes miracle_goto( command_argument );
+extern response_codes miracle_ff( command_argument );
+extern response_codes miracle_set_in_point( command_argument );
+extern response_codes miracle_set_out_point( command_argument );
+extern response_codes miracle_get_unit_status( command_argument );
+extern response_codes miracle_set_unit_property( command_argument );
+extern response_codes miracle_get_unit_property( command_argument );
+extern response_codes miracle_transfer( command_argument );
+extern response_codes miracle_push( command_argument, mlt_service );
+extern response_codes miracle_receive( command_argument, char * );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/modules/Makefile b/src/modules/Makefile
new file mode 100644 (file)
index 0000000..fcbf3ea
--- /dev/null
@@ -0,0 +1,31 @@
+include ../../config.mak
+include make.inc
+
+all clean depend:
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+               then $(MAKE) -C $$subdir $@ || exit 1; \
+               fi \
+       done
+
+distclean:
+       rm -f consumers.dat filters.dat producers.dat transitions.dat make.inc; \
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+               then $(MAKE) -C $$subdir $@ || exit 1; \
+               fi \
+       done
+
+install:
+       list='$(SUBDIRS)'; \
+       for subdir in $$list; do \
+               if [ -f $$subdir/Makefile -a ! -f disable-$$subdir ] ; \
+               then $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \
+               fi \
+       done
+
+uninstall:
+       rm -rf "$(DESTDIR)$(libdir)/mlt"
+
diff --git a/src/modules/avformat/Makefile b/src/modules/avformat/Makefile
new file mode 100644 (file)
index 0000000..7d88943
--- /dev/null
@@ -0,0 +1,86 @@
+include ../../../config.mak
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+include config.mak
+
+LDFLAGS += -lavformat$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavcodec$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavutil$(AVFORMAT_SUFFIX)
+LDFLAGS += -lavdevice$(AVFORMAT_SUFFIX) $(EXTRA_LIBS)
+
+ifndef CODECS
+TARGET = ../libmltffmpeg$(LIBSUF)
+else
+TARGET = ../libmltavformat$(LIBSUF)
+endif
+
+OBJS = factory.o
+
+ifdef FILTERS
+OBJS += filter_avcolour_space.o \
+           filter_avresample.o \
+           filter_avdeinterlace.o
+ifdef SWSCALE
+OBJS += filter_swscale.o
+endif
+CFLAGS += -DFILTERS
+endif
+
+ifdef CODECS
+OBJS += producer_avformat.o \
+           consumer_avformat.o
+CFLAGS += -DCODECS
+endif
+
+ifdef SWSCALE
+CFLAGS += -DSWSCALE
+LDFLAGS += -lswscale$(AVFORMAT_SUFFIX)
+endif
+
+ifdef LOCAL_FFMPEG
+LOCAL_FFMPEG_OBJS = ffmpeg/libavformat/libavformat$(AVFORMAT_SUFFIX) \
+                    ffmpeg/libavcodec/libavcodec$(AVFORMAT_SUFFIX) \
+                    ffmpeg/libavutil/libavutil$(AVFORMAT_SUFFIX) \
+                    ffmpeg/libavutil/libavdevice$(AVFORMAT_SUFFIX)
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(LOCAL_FFMPEG_OBJS):
+       if [ $(LOCAL_FFMPEG) ] ; then \
+               $(MAKE) -C ffmpeg ffmpeg ; \
+       fi
+
+$(TARGET): $(OBJS) $(LOCAL_FFMPEG_OBJS)
+       $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+       if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg dep ; fi
+       $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+       if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg distclean ; fi
+       rm -f .depend
+
+clean: 
+       #if [ $(LOCAL_FFMPEG) ] ; then $(MAKE) -C ffmpeg clean ; fi
+       rm -f $(OBJS) ../libmltffmpeg$(LIBSUF) ../libmltavformat$(LIBSUF)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+       install -d "$(DESTDIR)$(prefix)/share/mlt/avformat"
+       install -m 644 producer_avformat.yml "$(DESTDIR)$(prefix)/share/mlt/avformat"
+
+uninstall:
+       rm "$(DESTDIR)$(libdir)/mlt/libmltavformat$(LIBSUF)" 2> /dev/null || true
+       rm "$(DESTDIR)$(libdir)/mlt/libmltffmpeg$(LIBSUF)" 2> /dev/null || true
+       rm -rf "$(DESTDIR)$(prefix)/share/mlt/avformat"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/avformat/audioconvert.h b/src/modules/avformat/audioconvert.h
new file mode 100644 (file)
index 0000000..4b76710
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * audio conversion
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2008 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_AUDIOCONVERT_H
+#define AVCODEC_AUDIOCONVERT_H
+
+/**
+ * @file audioconvert.h
+ * Audio format conversion routines
+ */
+
+
+#include "avcodec.h"
+
+
+/**
+ * Generate string corresponding to the sample format with
+ * number sample_fmt, or a header if sample_fmt is negative.
+ *
+ * @param[in] buf the buffer where to write the string
+ * @param[in] buf_size the size of buf
+ * @param[in] sample_fmt the number of the sample format to print the corresponding info string, or
+ * a negative value to print the corresponding header.
+ * Meaningful values for obtaining a sample format info vary from 0 to SAMPLE_FMT_NB -1.
+ */
+void avcodec_sample_fmt_string(char *buf, int buf_size, int sample_fmt);
+
+/**
+ * @return NULL on error
+ */
+const char *avcodec_get_sample_fmt_name(int sample_fmt);
+
+/**
+ * @return SAMPLE_FMT_NONE on error
+ */
+enum SampleFormat avcodec_get_sample_fmt(const char* name);
+
+/**
+ * @return NULL on error
+ */
+const char *avcodec_get_channel_name(int channel_id);
+
+/**
+ * Return description of channel layout
+ */
+void avcodec_get_channel_layout_string(char *buf, int buf_size, int nb_channels, int64_t channel_layout);
+
+/**
+ * Guess the channel layout
+ * @param nb_channels
+ * @param codec_id Codec identifier, or CODEC_ID_NONE if unknown
+ * @param fmt_name Format name, or NULL if unknown
+ * @return Channel layout mask
+ */
+int64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name);
+
+
+struct AVAudioConvert;
+typedef struct AVAudioConvert AVAudioConvert;
+
+/**
+ * Create an audio sample format converter context
+ * @param out_fmt Output sample format
+ * @param out_channels Number of output channels
+ * @param in_fmt Input sample format
+ * @param in_channels Number of input channels
+ * @param[in] matrix Channel mixing matrix (of dimension in_channel*out_channels). Set to NULL to ignore.
+ * @param flags See FF_MM_xx
+ * @return NULL on error
+ */
+AVAudioConvert *av_audio_convert_alloc(enum SampleFormat out_fmt, int out_channels,
+                                       enum SampleFormat in_fmt, int in_channels,
+                                       const float *matrix, int flags);
+
+/**
+ * Free audio sample format converter context
+ */
+void av_audio_convert_free(AVAudioConvert *ctx);
+
+/**
+ * Convert between audio sample formats
+ * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel.
+ * @param[in] out_stride distance between consecutive input samples (measured in bytes)
+ * @param[in] in array of input buffers for each channel
+ * @param[in] in_stride distance between consecutive output samples (measured in bytes)
+ * @param len length of audio frame size (measured in samples)
+ */
+int av_audio_convert(AVAudioConvert *ctx,
+                           void * const out[6], const int out_stride[6],
+                     const void * const  in[6], const int  in_stride[6], int len);
+
+#endif /* AVCODEC_AUDIOCONVERT_H */
diff --git a/src/modules/avformat/configure b/src/modules/avformat/configure
new file mode 100755 (executable)
index 0000000..dc371a3
--- /dev/null
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+# Determine whether to recommend/use the HEAD revision of FFmpeg (unreleased)
+# or a specific revision based upon whether the last digit of our version
+# is even or odd. An odd MLT version number always represents unreleased.
+svn_rev="17887"
+micro_version=$(echo $version | cut -d . -f 3)
+odd_version=$(($micro_version % 2))
+[ "$odd_version" -eq "1" ] && svn_rev="HEAD"
+
+if [ "$help" = "1" ]
+then
+       cat << EOF
+FFMPEG/avformat options:
+
+  --avformat-svn          - Obtain ffmpeg from Subversion
+  --avformat-svn-extra    - Add extra configure options for --avformat-svn
+  --avformat-shared=path  - Link against a shared installation of ffmpeg (default)
+  --avformat-static=path  - Link against a static ffmpeg dev tree
+  --avformat-ldextra=libs - Provide additional libs to link with
+  --avformat-suffix=suff  - Specify a custom suffix for an ffmpeg shared build
+  --avformat-swscale      - Use ffmpeg libswcale instead of img_convert
+  --avformat-no-codecs    - Disable the producer and consumer to avoid the FFmpeg codecs
+  --avformat-no-filters   - Disable the filters to make a codecs+muxers-only plugin
+
+  NOTE: The recommended version of FFmpeg is SVN-r$svn_rev.
+
+EOF
+
+else
+       targetos=$(uname -s)
+       case $targetos in
+       Darwin)
+               export LIBSUF=.dylib
+               ;;
+       Linux|FreeBSD)
+               export LIBSUF=.so
+               ;;
+       *)
+               ;;
+       esac
+               
+       bits=$(uname -m)
+       case $bits in
+       x86_64)
+               [ -d /usr/lib/lib64 ] && export LIBDIR=lib64 || export LIBDIR=lib
+               ;;
+       *)
+               export LIBDIR=lib
+               ;;
+       esac
+
+       echo > config.mak
+
+       export static_ffmpeg=
+       export shared_ffmpeg=$(pkg-config --variable=prefix libavformat)
+       export extra_libs=
+       export svn_ffmpeg=
+       export svn_ffmpeg_extra=
+       export avformat_suffix=
+       export swscale=
+       export codecs=true
+       export filters=true
+
+       for i in "$@"
+       do
+               case $i in
+                       --avformat-static=* )   static_ffmpeg="${i#--avformat-static=}" ;;
+                       --avformat-shared=* )   shared_ffmpeg="${i#--avformat-shared=}" ;;
+                       --avformat-ldextra=* )  extra_libs="${i#--avformat-ldextra=}" ;;
+                       --avformat-svn )                svn_ffmpeg=true ;;
+                       --avformat-svn-extra=* ) svn_ffmpeg_extra="${i#--avformat-svn-extra=}" ;;
+                       --avformat-cvs )                svn_ffmpeg=true ;;
+                       --avformat-suffix=* )   avformat_suffix="${i#--avformat-suffix=}" ;;
+                       --avformat-swscale )    swscale=true ;;
+                       --avformat-swscaler )   swscale=true ;;
+                       --avformat-no-codecs )  codecs=false ;;
+                       --avformat-no-filters ) filters=false ;;
+               esac
+       done
+
+       if [ "$svn_ffmpeg" != "" ]
+       then
+               if [ "$gpl" = "true" ]
+               then
+                       enable_gpl="--enable-gpl"
+                       [ "$swscale" != "" ] && [ "$svn_rev" = "17887" ] &&
+                               enable_swscale="--enable-swscale"
+               fi
+               if [ ! -d "ffmpeg" ]
+               then
+                       echo
+                       echo "Checking out ffmpeg/avformat revision $svn_rev - no password required"
+                       echo
+                       if [ "$svn_rev" = "17887" ]; then
+                               svn checkout -r $svn_rev svn://svn.mplayerhq.hu/ffmpeg/branches/0.5 ffmpeg
+                       else
+                               svn checkout -r $svn_rev svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
+                       fi
+               fi
+               [ -d "ffmpeg" ] && ( cd ffmpeg ; ./configure $enable_gpl $enable_swscale $svn_ffmpeg_extra )
+               #[ ! -f "ffmpeg/ffmpeg.patch" ] && ( cd ffmpeg ; cp ../ffmpeg.patch . ; patch -p0 < ffmpeg.patch )
+               echo "CFLAGS+=-I`pwd`/ffmpeg -I`pwd`/ffmpeg/libavformat -I`pwd`/ffmpeg/libavcodec -I`pwd`/ffmpeg/libavutil -I`pwd`/ffmpeg/libavdevice" >> config.mak
+               echo "LDFLAGS+=-L`pwd`/ffmpeg/libavformat -L`pwd`/ffmpeg/libavcodec -L`pwd`/ffmpeg/libavutil -L`pwd`/ffmpeg/libavdevice" >> config.mak
+               if [ "$swscale" != "" ] || [ "$svn_rev" = "HEAD" ]
+               then
+                       echo "CFLAGS+=-I`pwd`/ffmpeg/libswscale" >> config.mak
+                       echo "LDFLAGS+=-L`pwd`/ffmpeg/libswscale" >> config.mak
+                       echo "SWSCALE=1" >> config.mak
+               fi
+               [ $targetos = "Darwin" ] &&
+                       echo "LDFLAGS+=-single_module" >> config.mak
+               echo "LOCAL_FFMPEG=1" >> config.mak
+               echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
+               extra_libs="$extra_libs -lz -lbz2"
+       elif [ "$static_ffmpeg" != "" ]
+       then 
+               if [ -d "$static_ffmpeg" ]
+               then
+                       echo "CFLAGS+=-I$static_ffmpeg/libavformat -I$static_ffmpeg/libavcodec -I$static_ffmpeg/libavutil -I$static_ffmpeg/libavdevice" >> config.mak
+                       echo "LDFLAGS+=-L$static_ffmpeg/libavformat -L$static_ffmpeg/libavcodec -L$static_ffmpeg/libavutil -L$static_ffmpeg/libavdevice" >> config.mak
+                       [ $targetos = "Darwin" ] &&
+                               echo "LDFLAGS+=-single_module" >> config.mak
+                       if [ "$swscale" != "" ]
+                       then
+                               echo "CFLAGS+=-I$static_ffmpeg/libswscale" >> config.mak
+                               echo "LDFLAGS+=-L$static_ffmpeg/libswscale" >> config.mak
+                               echo "SWSCALE=1" >> config.mak
+                       fi
+                       echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
+                       extra_libs="$extra_libs -lz"
+               else
+                       echo "avformat: Invalid path specified: $static_ffmpeg"
+                       touch ../disable-avformat
+                       echo 0
+               fi
+       elif [ "$shared_ffmpeg" != "" ]
+       then
+               echo "PREFIX=$shared_ffmpeg" >> config.mak
+               echo "CFLAGS+=$(pkg-config --cflags libavformat) $TMP_CFLAGS" >> config.mak
+               echo "LDFLAGS+=$(pkg-config --libs libavformat)" >> config.mak
+               [ -d "$shared_ffmpeg/include/ffmpeg/libavformat" ] &&
+                       echo "CFLAGS+=-I$shared_ffmpeg/include/ffmpeg/libavformat -I$shared_ffmpeg/include/ffmpeg/libavcodec" >> config.mak
+               [ -d "$shared_ffmpeg/include/libavformat" ] &&
+                       echo "CFLAGS+=-I$shared_ffmpeg/include/libavformat -I$shared_ffmpeg/include/libavcodec" >> config.mak
+               avcodec_version=$(pkg-config --modversion libavcodec)
+               if [ "$swscale" != "" ] || ( [ $(echo $avcodec_version | cut -d. -f1) -ge 52 ] && [ $(echo $avcodec_version | cut -d. -f2) -ge 21 ] )
+               then
+                       [ -d "$shared_ffmpeg/include/ffmpeg/libswscale" ] &&
+                               echo "CFLAGS+=-I$shared_ffmpeg/include/ffmpeg/libswscale" >> config.mak
+                       [ -d "$shared_ffmpeg/include/libswscale" ] &&
+                               echo "CFLAGS+=-I$shared_ffmpeg/include/libswscale" >> config.mak
+                       echo "SWSCALE=1" >> config.mak
+               fi
+       else
+               echo "avformat: No build environment found. "
+               echo "          Try configuring mlt with --avformat-svn."
+               touch ../disable-avformat
+               exit 0
+       fi
+
+       echo "EXTRA_LIBS=$extra_libs" >> config.mak
+       echo "AVFORMAT_SUFFIX=$avformat_suffix" >> config.mak
+       [ "$codecs" = "true" ] && echo "CODECS=1" >> config.mak
+       [ "$filters" = "true" ] && echo "FILTERS=1" >> config.mak
+       exit 0
+
+fi
diff --git a/src/modules/avformat/consumer_avformat.c b/src/modules/avformat/consumer_avformat.c
new file mode 100644 (file)
index 0000000..bc83b0b
--- /dev/null
@@ -0,0 +1,1371 @@
+/*
+ * consumer_avformat.c -- an encoder based on avformat
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <math.h>
+#include <unistd.h>
+
+// avformat header files
+#include <avformat.h>
+#ifdef SWSCALE
+#include <swscale.h>
+#endif
+#include <opt.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+//
+// This structure should be extended and made globally available in mlt
+//
+
+typedef struct
+{
+       int16_t *buffer;
+       int size;
+       int used;
+       double time;
+       int frequency;
+       int channels;
+}
+*sample_fifo, sample_fifo_s;
+
+sample_fifo sample_fifo_init( int frequency, int channels )
+{
+       sample_fifo this = calloc( 1, sizeof( sample_fifo_s ) );
+       this->frequency = frequency;
+       this->channels = channels;
+       return this;
+}
+
+// sample_fifo_clear and check are temporarily aborted (not working as intended)
+
+void sample_fifo_clear( sample_fifo this, double time )
+{
+       int words = ( float )( time - this->time ) * this->frequency * this->channels;
+       if ( ( int )( ( float )time * 100 ) < ( int )( ( float )this->time * 100 ) && this->used > words && words > 0 )
+       {
+               memmove( this->buffer, &this->buffer[ words ], ( this->used - words ) * sizeof( int16_t ) );
+               this->used -= words;
+               this->time = time;
+       }
+       else if ( ( int )( ( float )time * 100 ) != ( int )( ( float )this->time * 100 ) )
+       {
+               this->used = 0;
+               this->time = time;
+       }
+}
+
+void sample_fifo_check( sample_fifo this, double time )
+{
+       if ( this->used == 0 )
+       {
+               if ( ( int )( ( float )time * 100 ) < ( int )( ( float )this->time * 100 ) )
+                       this->time = time;
+       }
+}
+
+void sample_fifo_append( sample_fifo this, int16_t *samples, int count )
+{
+       if ( ( this->size - this->used ) < count )
+       {
+               this->size += count * 5;
+               this->buffer = realloc( this->buffer, this->size * sizeof( int16_t ) );
+       }
+
+       memcpy( &this->buffer[ this->used ], samples, count * sizeof( int16_t ) );
+       this->used += count;
+}
+
+int sample_fifo_used( sample_fifo this )
+{
+       return this->used;
+}
+
+int sample_fifo_fetch( sample_fifo this, int16_t *samples, int count )
+{
+       if ( count > this->used )
+               count = this->used;
+
+       memcpy( samples, this->buffer, count * sizeof( int16_t ) );
+       this->used -= count;
+       memmove( this->buffer, &this->buffer[ count ], this->used * sizeof( int16_t ) );
+
+       this->time += ( double )count / this->channels / this->frequency;
+
+       return count;
+}
+
+void sample_fifo_close( sample_fifo this )
+{
+       free( this->buffer );
+       free( this );
+}
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_avformat_init( mlt_profile profile, char *arg )
+{
+       // Allocate the consumer
+       mlt_consumer this = mlt_consumer_new( profile );
+
+       // If memory allocated and initialises without error
+       if ( this != NULL )
+       {
+               // Get properties from the consumer
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+               // Assign close callback
+               this->close = consumer_close;
+
+               // Interpret the argument
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "target", arg );
+
+               // sample and frame queue
+               mlt_properties_set_data( properties, "frame_queue", mlt_deque_init( ), 0, ( mlt_destructor )mlt_deque_close, NULL );
+
+               // Audio options not fully handled by AVOptions
+#define QSCALE_NONE (-99999)
+               mlt_properties_set_int( properties, "aq", QSCALE_NONE );
+               
+               // Video options not fully handled by AVOptions
+               mlt_properties_set_int( properties, "dc", 8 );
+               
+               // Muxer options not fully handled by AVOptions
+               mlt_properties_set_double( properties, "muxdelay", 0.7 );
+               mlt_properties_set_double( properties, "muxpreload", 0.5 );
+
+               // Ensure termination at end of the stream
+               mlt_properties_set_int( properties, "terminate_on_pause", 1 );
+               
+               // Default to separate processing threads for producer and consumer with no frame dropping!
+               mlt_properties_set_int( properties, "real_time", -1 );
+               mlt_properties_set_int( properties, "prefill", 1 );
+
+               // Set up start/stop/terminated callbacks
+               this->start = consumer_start;
+               this->stop = consumer_stop;
+               this->is_stopped = consumer_is_stopped;
+       }
+
+       // Return this
+       return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+       int error = 0;
+
+       // Report information about available muxers and codecs as YAML Tiny
+       char *s = mlt_properties_get( properties, "f" );
+       if ( s && strcmp( s, "list" ) == 0 )
+       {
+               fprintf( stderr, "---\nformats:\n" );
+               AVOutputFormat *format = NULL;
+               while ( ( format = av_oformat_next( format ) ) )
+                       fprintf( stderr, "  - %s\n", format->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+       s = mlt_properties_get( properties, "acodec" );
+       if ( s && strcmp( s, "list" ) == 0 )
+       {
+               fprintf( stderr, "---\naudio_codecs:\n" );
+               AVCodec *codec = NULL;
+               while ( ( codec = av_codec_next( codec ) ) )
+                       if ( codec->encode && codec->type == CODEC_TYPE_AUDIO )
+                               fprintf( stderr, "  - %s\n", codec->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+       s = mlt_properties_get( properties, "vcodec" );
+       if ( s && strcmp( s, "list" ) == 0 )
+       {
+               fprintf( stderr, "---\nvideo_codecs:\n" );
+               AVCodec *codec = NULL;
+               while ( ( codec = av_codec_next( codec ) ) )
+                       if ( codec->encode && codec->type == CODEC_TYPE_VIDEO )
+                               fprintf( stderr, "  - %s\n", codec->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+
+       // Check that we're not already running
+       if ( !error && !mlt_properties_get_int( properties, "running" ) )
+       {
+               // Allocate a thread
+               pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+               // Get the width and height
+               int width = mlt_properties_get_int( properties, "width" );
+               int height = mlt_properties_get_int( properties, "height" );
+
+               // Obtain the size property
+               char *size = mlt_properties_get( properties, "s" );
+
+               // Interpret it
+               if ( size != NULL )
+               {
+                       int tw, th;
+                       if ( sscanf( size, "%dx%d", &tw, &th ) == 2 && tw > 0 && th > 0 )
+                       {
+                               width = tw;
+                               height = th;
+                       }
+                       else
+                       {
+                               fprintf( stderr, "%s: Invalid size property %s - ignoring.\n", __FILE__, size );
+                       }
+               }
+               
+               // Now ensure we honour the multiple of two requested by libavformat
+               width = ( width / 2 ) * 2;
+               height = ( height / 2 ) * 2;
+               mlt_properties_set_int( properties, "width", width );
+               mlt_properties_set_int( properties, "height", height );
+
+               // We need to set these on the profile as well because the s property is
+               // an alias to mlt properties that correspond to profile settings.
+               mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+               if ( profile )
+               {
+                       profile->width = width;
+                       profile->height = height;
+               }
+
+               // Handle the ffmpeg command line "-r" property for frame rate
+               if ( mlt_properties_get( properties, "r" ) )
+               {
+                       double frame_rate = mlt_properties_get_double( properties, "r" );
+                       AVRational rational = av_d2q( frame_rate, 255 );
+                       mlt_properties_set_int( properties, "frame_rate_num", rational.num );
+                       mlt_properties_set_int( properties, "frame_rate_den", rational.den );
+                       if ( profile )
+                       {
+                               profile->frame_rate_num = rational.num;
+                               profile->frame_rate_den = rational.den;
+                               mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
+                       }
+               }
+               
+               // Apply AVOptions that are synonyms for standard mlt_consumer options
+               if ( mlt_properties_get( properties, "ac" ) )
+                       mlt_properties_set_int( properties, "channels", mlt_properties_get_int( properties, "ac" ) );
+               if ( mlt_properties_get( properties, "ar" ) )
+                       mlt_properties_set_int( properties, "frequency", mlt_properties_get_int( properties, "ar" ) );
+
+               // Assign the thread to properties
+               mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+               // Set the running state
+               mlt_properties_set_int( properties, "running", 1 );
+
+               // Create the thread
+               pthread_create( thread, NULL, consumer_thread, this );
+       }
+       return error;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check that we're running
+       if ( mlt_properties_get_int( properties, "running" ) )
+       {
+               // Get the thread
+               pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+               // Stop the thread
+               mlt_properties_set_int( properties, "running", 0 );
+
+               // Wait for termination
+               pthread_join( *thread, NULL );
+       }
+
+       return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+       return !mlt_properties_get_int( properties, "running" );
+}
+
+/** Process properties as AVOptions and apply to AV context obj
+*/
+
+static void apply_properties( void *obj, mlt_properties properties, int flags )
+{
+       int i;
+       int count = mlt_properties_count( properties ); 
+       for ( i = 0; i < count; i++ )
+       {
+               const char *opt_name = mlt_properties_get_name( properties, i );
+               const AVOption *opt = av_find_opt( obj, opt_name, NULL, flags, flags );
+               if ( opt != NULL )
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(7<<8)+0)
+                       av_set_string3( obj, opt_name, mlt_properties_get( properties, opt_name), 0, NULL );
+#elif LIBAVCODEC_VERSION_INT >= ((51<<16)+(59<<8)+0)
+                       av_set_string2( obj, opt_name, mlt_properties_get( properties, opt_name), 0 );
+#else
+                       av_set_string( obj, opt_name, mlt_properties_get( properties, opt_name) );
+#endif
+       }
+}
+
+/** Add an audio output stream
+*/
+
+static AVStream *add_audio_stream( mlt_consumer this, AVFormatContext *oc, int codec_id )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Create a new stream
+       AVStream *st = av_new_stream( oc, 1 );
+
+       // If created, then initialise from properties
+       if ( st != NULL ) 
+       {
+               AVCodecContext *c = st->codec;
+
+               // Establish defaults from AVOptions
+               avcodec_get_context_defaults2( c, CODEC_TYPE_AUDIO );
+
+               c->codec_id = codec_id;
+               c->codec_type = CODEC_TYPE_AUDIO;
+
+               // Setup multi-threading
+               int thread_count = mlt_properties_get_int( properties, "threads" );
+               if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+                       thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+               if ( thread_count > 1 )
+                       avcodec_thread_init( c, thread_count );         
+       
+               if (oc->oformat->flags & AVFMT_GLOBALHEADER) 
+                       c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+               
+               // Allow the user to override the audio fourcc
+               if ( mlt_properties_get( properties, "atag" ) )
+               {
+                       char *tail = NULL;
+                       char *arg = mlt_properties_get( properties, "atag" );
+                       int tag = strtol( arg, &tail, 0);
+                       if( !tail || *tail )
+                               tag = arg[ 0 ] + ( arg[ 1 ] << 8 ) + ( arg[ 2 ] << 16 ) + ( arg[ 3 ] << 24 );
+                       c->codec_tag = tag;
+               }
+
+               // Process properties as AVOptions
+               apply_properties( c, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM );
+
+               int audio_qscale = mlt_properties_get_int( properties, "aq" );
+        if ( audio_qscale > QSCALE_NONE )
+               {
+                       c->flags |= CODEC_FLAG_QSCALE;
+                       c->global_quality = st->quality = FF_QP2LAMBDA * audio_qscale;
+               }
+
+               // Set parameters controlled by MLT
+               c->sample_rate = mlt_properties_get_int( properties, "frequency" );
+               c->channels = mlt_properties_get_int( properties, "channels" );
+
+               if ( mlt_properties_get( properties, "alang" ) != NULL )
+                       strncpy( st->language, mlt_properties_get( properties, "alang" ), sizeof( st->language ) );
+       }
+       else
+       {
+               fprintf( stderr, "%s: Could not allocate a stream for audio\n", __FILE__ );
+       }
+
+       return st;
+}
+
+static int open_audio( AVFormatContext *oc, AVStream *st, int audio_outbuf_size )
+{
+       // We will return the audio input size from here
+       int audio_input_frame_size = 0;
+
+       // Get the context
+       AVCodecContext *c = st->codec;
+
+       // Find the encoder
+       AVCodec *codec = avcodec_find_encoder( c->codec_id );
+
+       // Continue if codec found and we can open it
+       if ( codec != NULL && avcodec_open( c, codec ) >= 0 )
+       {
+               // ugly hack for PCM codecs (will be removed ASAP with new PCM
+               // support to compute the input frame size in samples
+               if ( c->frame_size <= 1 ) 
+               {
+                       audio_input_frame_size = audio_outbuf_size / c->channels;
+                       switch(st->codec->codec_id) 
+                       {
+                               case CODEC_ID_PCM_S16LE:
+                               case CODEC_ID_PCM_S16BE:
+                               case CODEC_ID_PCM_U16LE:
+                               case CODEC_ID_PCM_U16BE:
+                                       audio_input_frame_size >>= 1;
+                                       break;
+                               default:
+                                       break;
+                       }
+               } 
+               else 
+               {
+                       audio_input_frame_size = c->frame_size;
+               }
+
+               // Some formats want stream headers to be seperate (hmm)
+               if( !strcmp( oc->oformat->name, "mp4" ) || 
+                       !strcmp( oc->oformat->name, "mov" ) || 
+                       !strcmp( oc->oformat->name, "3gp" ) )
+                       c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+       }
+       else
+       {
+               fprintf( stderr, "%s: Unable to encode audio - disabling audio output.\n", __FILE__ );
+       }
+
+       return audio_input_frame_size;
+}
+
+static void close_audio( AVFormatContext *oc, AVStream *st )
+{
+       avcodec_close( st->codec );
+}
+
+/** Add a video output stream 
+*/
+
+static AVStream *add_video_stream( mlt_consumer this, AVFormatContext *oc, int codec_id )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Create a new stream
+       AVStream *st = av_new_stream( oc, 0 );
+
+       if ( st != NULL ) 
+       {
+               char *pix_fmt = mlt_properties_get( properties, "pix_fmt" );
+               AVCodecContext *c = st->codec;
+
+               // Establish defaults from AVOptions
+               avcodec_get_context_defaults2( c, CODEC_TYPE_VIDEO );
+
+               c->codec_id = codec_id;
+               c->codec_type = CODEC_TYPE_VIDEO;
+               
+               // Setup multi-threading
+               int thread_count = mlt_properties_get_int( properties, "threads" );
+               if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+                       thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+               if ( thread_count > 1 )
+                       avcodec_thread_init( c, thread_count );         
+       
+               // Process properties as AVOptions
+               apply_properties( c, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM );
+
+               // Set options controlled by MLT
+               c->width = mlt_properties_get_int( properties, "width" );
+               c->height = mlt_properties_get_int( properties, "height" );
+               c->time_base.num = mlt_properties_get_int( properties, "frame_rate_den" );
+               c->time_base.den = mlt_properties_get_int( properties, "frame_rate_num" );
+               if ( st->time_base.den == 0 )
+                       st->time_base = c->time_base;
+               c->pix_fmt = pix_fmt ? avcodec_get_pix_fmt( pix_fmt ) : PIX_FMT_YUV420P;
+
+               if ( mlt_properties_get( properties, "aspect" ) )
+               {
+                       // "-aspect" on ffmpeg command line is display aspect ratio
+                       double ar = mlt_properties_get_double( properties, "aspect" );
+                       AVRational rational = av_d2q( ar, 255 );
+
+                       // Update the profile and properties as well since this is an alias 
+                       // for mlt properties that correspond to profile settings
+                       mlt_properties_set_int( properties, "display_aspect_num", rational.num );
+                       mlt_properties_set_int( properties, "display_aspect_den", rational.den );
+                       mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
+                       if ( profile )
+                       {
+                               profile->display_aspect_num = rational.num;
+                               profile->display_aspect_den = rational.den;
+                               mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) );
+                       }
+
+                       // Now compute the sample aspect ratio
+                       rational = av_d2q( ar * c->height / c->width, 255 );
+                       c->sample_aspect_ratio = rational;
+                       // Update the profile and properties as well since this is an alias 
+                       // for mlt properties that correspond to profile settings
+                       mlt_properties_set_int( properties, "sample_aspect_num", rational.num );
+                       mlt_properties_set_int( properties, "sample_aspect_den", rational.den );
+                       if ( profile )
+                       {
+                               profile->sample_aspect_num = rational.num;
+                               profile->sample_aspect_den = rational.den;
+                               mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+                       }
+               }
+               else
+               {
+                       c->sample_aspect_ratio.num = mlt_properties_get_int( properties, "sample_aspect_num" );
+                       c->sample_aspect_ratio.den = mlt_properties_get_int( properties, "sample_aspect_den" );
+               }
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+               st->sample_aspect_ratio = c->sample_aspect_ratio;
+#endif
+
+               if ( mlt_properties_get_double( properties, "qscale" ) > 0 )
+               {
+                       c->flags |= CODEC_FLAG_QSCALE;
+                       st->quality = FF_QP2LAMBDA * mlt_properties_get_double( properties, "qscale" );
+               }
+
+               // Allow the user to override the video fourcc
+               if ( mlt_properties_get( properties, "vtag" ) )
+               {
+                       char *tail = NULL;
+                       const char *arg = mlt_properties_get( properties, "vtag" );
+                       int tag = strtol( arg, &tail, 0);
+                       if( !tail || *tail )
+                               tag = arg[ 0 ] + ( arg[ 1 ] << 8 ) + ( arg[ 2 ] << 16 ) + ( arg[ 3 ] << 24 );
+                       c->codec_tag = tag;
+               }
+
+               // Some formats want stream headers to be seperate
+               if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) 
+                       c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+               // Translate these standard mlt consumer properties to ffmpeg
+               if ( mlt_properties_get_int( properties, "progressive" ) == 0 &&
+                    mlt_properties_get_int( properties, "deinterlace" ) == 0 )
+               {
+                       if ( ! mlt_properties_get( properties, "ildct" ) || mlt_properties_get_int( properties, "ildct" ) )
+                               c->flags |= CODEC_FLAG_INTERLACED_DCT;
+                       if ( ! mlt_properties_get( properties, "ilme" ) || mlt_properties_get_int( properties, "ilme" ) )
+                               c->flags |= CODEC_FLAG_INTERLACED_ME;
+               }
+               
+               // parse the ratecontrol override string
+               int i;
+               char *rc_override = mlt_properties_get( properties, "rc_override" );
+               for ( i = 0; rc_override; i++ )
+               {
+                       int start, end, q;
+                       int e = sscanf( rc_override, "%d,%d,%d", &start, &end, &q );
+                       if ( e != 3 )
+                               fprintf( stderr, "%s: Error parsing rc_override\n", __FILE__ );
+                       c->rc_override = av_realloc( c->rc_override, sizeof( RcOverride ) * ( i + 1 ) );
+                       c->rc_override[i].start_frame = start;
+                       c->rc_override[i].end_frame = end;
+                       if ( q > 0 )
+                       {
+                               c->rc_override[i].qscale = q;
+                               c->rc_override[i].quality_factor = 1.0;
+                       }
+                       else
+                       {
+                               c->rc_override[i].qscale = 0;
+                               c->rc_override[i].quality_factor = -q / 100.0;
+                       }
+                       rc_override = strchr( rc_override, '/' );
+                       if ( rc_override )
+                               rc_override++;
+               }
+               c->rc_override_count = i;
+               if ( !c->rc_initial_buffer_occupancy )
+                       c->rc_initial_buffer_occupancy = c->rc_buffer_size * 3/4;
+               c->intra_dc_precision = mlt_properties_get_int( properties, "dc" ) - 8;
+
+               // Setup dual-pass
+               i = mlt_properties_get_int( properties, "pass" );
+               if ( i == 1 )
+                       c->flags |= CODEC_FLAG_PASS1;
+               else if ( i == 2 )
+                       c->flags |= CODEC_FLAG_PASS2;
+               if ( codec_id != CODEC_ID_H264 && ( c->flags & ( CODEC_FLAG_PASS1 | CODEC_FLAG_PASS2 ) ) )
+               {
+                       char logfilename[1024];
+                       FILE *f;
+                       int size;
+                       char *logbuffer;
+
+                       snprintf( logfilename, sizeof(logfilename), "%s_2pass.log",
+                               mlt_properties_get( properties, "passlogfile" ) ? mlt_properties_get( properties, "passlogfile" ) : mlt_properties_get( properties, "target" ) );
+                       if ( c->flags & CODEC_FLAG_PASS1 )
+                       {
+                               f = fopen( logfilename, "w" );
+                               if ( !f )
+                                       perror( logfilename );
+                               else
+                                       mlt_properties_set_data( properties, "_logfile", f, 0, ( mlt_destructor )fclose, NULL );
+                       }
+                       else
+                       {
+                               /* read the log file */
+                               f = fopen( logfilename, "r" );
+                               if ( !f )
+                               {
+                                       perror(logfilename);
+                               }
+                               else
+                               {
+                                       mlt_properties_set( properties, "_logfilename", logfilename );
+                                       fseek( f, 0, SEEK_END );
+                                       size = ftell( f );
+                                       fseek( f, 0, SEEK_SET );
+                                       logbuffer = av_malloc( size + 1 );
+                                       if ( !logbuffer )
+                                               fprintf( stderr, "%s: Could not allocate log buffer\n", __FILE__ );
+                                       else
+                                       {
+                                               size = fread( logbuffer, 1, size, f );
+                                               fclose( f );
+                                               logbuffer[size] = '\0';
+                                               c->stats_in = logbuffer;
+                                               mlt_properties_set_data( properties, "_logbuffer", logbuffer, 0, ( mlt_destructor )av_free, NULL );
+                                       }
+                               }
+                       }
+               }
+       }
+       else
+       {
+               fprintf( stderr, "%s: Could not allocate a stream for video\n", __FILE__ );
+       }
+       return st;
+}
+
+static AVFrame *alloc_picture( int pix_fmt, int width, int height )
+{
+       // Allocate a frame
+       AVFrame *picture = avcodec_alloc_frame();
+
+       // Determine size of the 
+       int size = avpicture_get_size(pix_fmt, width, height);
+
+       // Allocate the picture buf
+       uint8_t *picture_buf = av_malloc(size);
+
+       // If we have both, then fill the image
+       if ( picture != NULL && picture_buf != NULL )
+       {
+               // Fill the frame with the allocated buffer
+               avpicture_fill( (AVPicture *)picture, picture_buf, pix_fmt, width, height);
+       }
+       else
+       {
+               // Something failed - clean up what we can
+               av_free( picture );
+               av_free( picture_buf );
+               picture = NULL;
+       }
+
+       return picture;
+}
+       
+static int open_video(AVFormatContext *oc, AVStream *st)
+{
+       // Get the codec
+       AVCodecContext *video_enc = st->codec;
+
+       // find the video encoder
+       AVCodec *codec = avcodec_find_encoder( video_enc->codec_id );
+
+       if( codec && codec->pix_fmts )
+       {
+               const enum PixelFormat *p = codec->pix_fmts;
+               for( ; *p!=-1; p++ )
+               {
+                       if( *p == video_enc->pix_fmt )
+                               break;
+               }
+               if( *p == -1 )
+                       video_enc->pix_fmt = codec->pix_fmts[ 0 ];
+       }
+
+       // Open the codec safely
+       return codec != NULL && avcodec_open( video_enc, codec ) >= 0;
+}
+
+void close_video(AVFormatContext *oc, AVStream *st)
+{
+       avcodec_close(st->codec);
+}
+
+static inline long time_difference( struct timeval *time1 )
+{
+       struct timeval time2;
+       gettimeofday( &time2, NULL );
+       return time2.tv_sec * 1000000 + time2.tv_usec - time1->tv_sec * 1000000 - time1->tv_usec;
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+       // Map the argument to the object
+       mlt_consumer this = arg;
+
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get the terminate on pause property
+       int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
+       int terminated = 0;
+
+       // Determine if feed is slow (for realtime stuff)
+       int real_time_output = mlt_properties_get_int( properties, "real_time" );
+
+       // Time structures
+       struct timeval ante;
+
+       // Get the frame rate
+       double fps = mlt_properties_get_double( properties, "fps" );
+
+       // Get width and height
+       int width = mlt_properties_get_int( properties, "width" );
+       int height = mlt_properties_get_int( properties, "height" );
+       int img_width = width;
+       int img_height = height;
+
+       // Get default audio properties
+       mlt_audio_format aud_fmt = mlt_audio_pcm;
+       int channels = mlt_properties_get_int( properties, "channels" );
+       int frequency = mlt_properties_get_int( properties, "frequency" );
+       int16_t *pcm = NULL;
+       int samples = 0;
+
+       // AVFormat audio buffer and frame size
+       int audio_outbuf_size = 10000;
+       uint8_t *audio_outbuf = av_malloc( audio_outbuf_size );
+       int audio_input_frame_size = 0;
+
+       // AVFormat video buffer and frame count
+       int frame_count = 0;
+       int video_outbuf_size = ( 1024 * 1024 );
+       uint8_t *video_outbuf = av_malloc( video_outbuf_size );
+
+       // Used for the frame properties
+       mlt_frame frame = NULL;
+       mlt_properties frame_properties = NULL;
+
+       // Get the queues
+       mlt_deque queue = mlt_properties_get_data( properties, "frame_queue", NULL );
+       sample_fifo fifo = mlt_properties_get_data( properties, "sample_fifo", NULL );
+
+       // Need two av pictures for converting
+       AVFrame *output = NULL;
+       AVFrame *input = alloc_picture( PIX_FMT_YUYV422, width, height );
+
+       // For receiving images from an mlt_frame
+       uint8_t *image;
+       mlt_image_format img_fmt = mlt_image_yuv422;
+
+       // For receiving audio samples back from the fifo
+       int16_t *buffer = av_malloc( 48000 * 2 );
+       int count = 0;
+
+       // Allocate the context
+       AVFormatContext *oc = av_alloc_format_context( );
+
+       // Streams
+       AVStream *audio_st = NULL;
+       AVStream *video_st = NULL;
+
+       // Time stamps
+       double audio_pts = 0;
+       double video_pts = 0;
+
+       // Loop variable
+       int i;
+
+       // Frames despatched
+       long int frames = 0;
+       long int total_time = 0;
+
+       // Determine the format
+       AVOutputFormat *fmt = NULL;
+       const char *filename = mlt_properties_get( properties, "target" );
+       char *format = mlt_properties_get( properties, "f" );
+       char *vcodec = mlt_properties_get( properties, "vcodec" );
+       char *acodec = mlt_properties_get( properties, "acodec" );
+       
+       // Used to store and override codec ids
+       int audio_codec_id;
+       int video_codec_id;
+
+       // Check for user selected format first
+       if ( format != NULL )
+               fmt = guess_format( format, NULL, NULL );
+
+       // Otherwise check on the filename
+       if ( fmt == NULL && filename != NULL )
+               fmt = guess_format( NULL, filename, NULL );
+
+       // Otherwise default to mpeg
+       if ( fmt == NULL )
+               fmt = guess_format( "mpeg", NULL, NULL );
+
+       // We need a filename - default to stdout?
+       if ( filename == NULL || !strcmp( filename, "" ) )
+               filename = "pipe:";
+
+       // Get the codec ids selected
+       audio_codec_id = fmt->audio_codec;
+       video_codec_id = fmt->video_codec;
+
+       // Check for audio codec overides
+       if ( ( acodec && strcmp( "acodec", "none" ) == 0 ) || mlt_properties_get_int( properties, "an" ) )
+               audio_codec_id = CODEC_ID_NONE;
+       else if ( acodec )
+       {
+               AVCodec *p = avcodec_find_encoder_by_name( acodec );
+               if ( p != NULL )
+                       audio_codec_id = p->id;
+               else
+                       fprintf( stderr, "%s: audio codec %s unrecognised - ignoring\n", __FILE__, acodec );
+       }
+
+       // Check for video codec overides
+       if ( ( vcodec && strcmp( "vcodec", "none" ) == 0 ) || mlt_properties_get_int( properties, "vn" ) )
+               video_codec_id = CODEC_ID_NONE;
+       else if ( vcodec )
+       {
+               AVCodec *p = avcodec_find_encoder_by_name( vcodec );
+               if ( p != NULL )
+                       video_codec_id = p->id;
+               else
+                       fprintf( stderr, "%s: video codec %s unrecognised - ignoring\n", __FILE__, vcodec );
+       }
+
+       // Write metadata
+       char *tmp = NULL;
+       int metavalue;
+
+       tmp = mlt_properties_get( properties, "meta.attr.title.markup");
+       if (tmp != NULL) snprintf( oc->title, sizeof(oc->title), "%s", tmp );
+
+       tmp = mlt_properties_get( properties, "meta.attr.comment.markup");
+       if (tmp != NULL) snprintf( oc->comment, sizeof(oc->comment), "%s", tmp );
+
+       tmp = mlt_properties_get( properties, "meta.attr.author.markup");
+       if (tmp != NULL) snprintf( oc->author, sizeof(oc->author), "%s", tmp );
+
+       tmp = mlt_properties_get( properties, "meta.attr.copyright.markup");
+       if (tmp != NULL) snprintf( oc->copyright, sizeof(oc->copyright), "%s", tmp );
+
+       tmp = mlt_properties_get( properties, "meta.attr.album.markup");
+       if (tmp != NULL) snprintf( oc->album, sizeof(oc->album), "%s", tmp );
+
+       metavalue = mlt_properties_get_int( properties, "meta.attr.year.markup");
+       if (metavalue != 0) oc->year = metavalue;
+
+       metavalue = mlt_properties_get_int( properties, "meta.attr.track.markup");
+       if (metavalue != 0) oc->track = metavalue;
+
+       oc->oformat = fmt;
+       snprintf( oc->filename, sizeof(oc->filename), "%s", filename );
+
+       // Add audio and video streams 
+       if ( video_codec_id != CODEC_ID_NONE )
+               video_st = add_video_stream( this, oc, video_codec_id );
+       if ( audio_codec_id != CODEC_ID_NONE )
+               audio_st = add_audio_stream( this, oc, audio_codec_id );
+
+       // Set the parameters (even though we have none...)
+       if ( av_set_parameters(oc, NULL) >= 0 ) 
+       {
+               oc->preload = ( int )( mlt_properties_get_double( properties, "muxpreload" ) * AV_TIME_BASE );
+               oc->max_delay= ( int )( mlt_properties_get_double( properties, "muxdelay" ) * AV_TIME_BASE );
+
+               // Process properties as AVOptions
+               apply_properties( oc, properties, AV_OPT_FLAG_ENCODING_PARAM );
+
+               if ( video_st && !open_video( oc, video_st ) )
+                       video_st = NULL;
+               if ( audio_st )
+                       audio_input_frame_size = open_audio( oc, audio_st, audio_outbuf_size );
+
+               // Open the output file, if needed
+               if ( !( fmt->flags & AVFMT_NOFILE ) ) 
+               {
+                       if ( url_fopen( &oc->pb, filename, URL_WRONLY ) < 0 ) 
+                       {
+                               fprintf( stderr, "%s: Could not open '%s'\n", __FILE__, filename );
+                               mlt_properties_set_int( properties, "running", 0 );
+                       }
+               }
+       
+               // Write the stream header, if any
+               if ( mlt_properties_get_int( properties, "running" ) )
+                       av_write_header( oc );
+       }
+       else
+       {
+               fprintf( stderr, "%s: Invalid output format parameters\n", __FILE__ );
+               mlt_properties_set_int( properties, "running", 0 );
+       }
+
+       // Allocate picture
+       if ( video_st )
+               output = alloc_picture( video_st->codec->pix_fmt, width, height );
+
+       // Last check - need at least one stream
+       if ( audio_st == NULL && video_st == NULL )
+               mlt_properties_set_int( properties, "running", 0 );
+
+       // Get the starting time (can ignore the times above)
+       gettimeofday( &ante, NULL );
+
+       // Loop while running
+       while( mlt_properties_get_int( properties, "running" ) && !terminated )
+       {
+               // Get the frame
+               frame = mlt_consumer_rt_frame( this );
+
+               // Check that we have a frame to work with
+               if ( frame != NULL )
+               {
+                       // Increment frames despatched
+                       frames ++;
+
+                       // Default audio args
+                       frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+                       // Check for the terminated condition
+                       terminated = terminate_on_pause && mlt_properties_get_double( frame_properties, "_speed" ) == 0.0;
+
+                       // Get audio and append to the fifo
+                       if ( !terminated && audio_st )
+                       {
+                               samples = mlt_sample_calculator( fps, frequency, count ++ );
+                               mlt_frame_get_audio( frame, &pcm, &aud_fmt, &frequency, &channels, &samples );
+
+                               // Create the fifo if we don't have one
+                               if ( fifo == NULL )
+                               {
+                                       fifo = sample_fifo_init( frequency, channels );
+                                       mlt_properties_set_data( properties, "sample_fifo", fifo, 0, ( mlt_destructor )sample_fifo_close, NULL );
+                               }
+
+                               if ( mlt_properties_get_double( frame_properties, "_speed" ) != 1.0 )
+                                       memset( pcm, 0, samples * channels * 2 );
+
+                               // Append the samples
+                               sample_fifo_append( fifo, pcm, samples * channels );
+                               total_time += ( samples * 1000000 ) / frequency;
+                       }
+
+                       // Encode the image
+                       if ( !terminated && video_st )
+                               mlt_deque_push_back( queue, frame );
+                       else
+                               mlt_frame_close( frame );
+               }
+
+               // While we have stuff to process, process...
+               while ( 1 )
+               {
+                       if (audio_st)
+                               audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
+                       else
+                               audio_pts = 0.0;
+        
+                       if (video_st)
+                               video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
+                       else
+                               video_pts = 0.0;
+
+                       // Write interleaved audio and video frames
+                       if ( !video_st || ( video_st && audio_st && audio_pts < video_pts ) )
+                       {
+                               if ( channels * audio_input_frame_size < sample_fifo_used( fifo ) )
+                               {
+                                       AVCodecContext *c;
+                                       AVPacket pkt;
+                                       av_init_packet( &pkt );
+
+                                       c = audio_st->codec;
+
+                                       sample_fifo_fetch( fifo, buffer, channels * audio_input_frame_size );
+
+                                       pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, buffer );
+                                       // Write the compressed frame in the media file
+                                       if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+                                               pkt.pts = av_rescale_q( c->coded_frame->pts, c->time_base, audio_st->time_base );
+                                       pkt.flags |= PKT_FLAG_KEY;
+                                       pkt.stream_index= audio_st->index;
+                                       pkt.data= audio_outbuf;
+
+                                       if ( pkt.size )
+                                               if ( av_interleaved_write_frame( oc, &pkt ) != 0) 
+                                                       fprintf( stderr, "%s: Error while writing audio frame\n", __FILE__ );
+
+                                       audio_pts += c->frame_size;
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+                       else if ( video_st )
+                       {
+                               if ( mlt_deque_count( queue ) )
+                               {
+                                       int out_size, ret;
+                                       AVCodecContext *c;
+
+                                       frame = mlt_deque_pop_front( queue );
+                                       frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+                                       c = video_st->codec;
+                                       
+                                       if ( mlt_properties_get_int( frame_properties, "rendered" ) )
+                                       {
+                                               int i = 0;
+                                               int j = 0;
+                                               uint8_t *p;
+                                               uint8_t *q;
+
+                                               mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+
+                                               mlt_frame_get_image( frame, &image, &img_fmt, &img_width, &img_height, 0 );
+
+                                               q = image;
+
+                                               // Convert the mlt frame to an AVPicture
+                                               for ( i = 0; i < height; i ++ )
+                                               {
+                                                       p = input->data[ 0 ] + i * input->linesize[ 0 ];
+                                                       j = width;
+                                                       while( j -- )
+                                                       {
+                                                               *p ++ = *q ++;
+                                                               *p ++ = *q ++;
+                                                       }
+                                               }
+
+                                               // Do the colour space conversion
+#ifdef SWSCALE
+                                               struct SwsContext *context = sws_getContext( width, height, PIX_FMT_YUYV422,
+                                                       width, height, video_st->codec->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+                                               sws_scale( context, input->data, input->linesize, 0, height,
+                                                       output->data, output->linesize);
+                                               sws_freeContext( context );
+#else
+                                               img_convert( ( AVPicture * )output, video_st->codec->pix_fmt, ( AVPicture * )input, PIX_FMT_YUYV422, width, height );
+#endif
+
+                                               // Apply the alpha if applicable
+                                               if ( video_st->codec->pix_fmt == PIX_FMT_RGB32 )
+                                               {
+                                                       uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+                                                       register int n;
+
+                                                       for ( i = 0; i < height; i ++ )
+                                                       {
+                                                               n = ( width + 7 ) / 8;
+                                                               p = output->data[ 0 ] + i * output->linesize[ 0 ];
+
+                                                               #ifndef __DARWIN__
+                                                               p += 3;
+                                                               #endif
+
+                                                               switch( width % 8 )
+                                                               {
+                                                                       case 0: do { *p = *alpha++; p += 4;
+                                                                       case 7:          *p = *alpha++; p += 4;
+                                                                       case 6:          *p = *alpha++; p += 4;
+                                                                       case 5:          *p = *alpha++; p += 4;
+                                                                       case 4:          *p = *alpha++; p += 4;
+                                                                       case 3:          *p = *alpha++; p += 4;
+                                                                       case 2:          *p = *alpha++; p += 4;
+                                                                       case 1:          *p = *alpha++; p += 4;
+                                                                                       }
+                                                                                       while( --n );
+                                                               }
+                                                       }
+                                               }
+                                       }
+
+                                       if (oc->oformat->flags & AVFMT_RAWPICTURE) 
+                                       {
+                                               // raw video case. The API will change slightly in the near future for that
+                                               AVPacket pkt;
+                                               av_init_packet(&pkt);
+
+                                               pkt.flags |= PKT_FLAG_KEY;
+                                               pkt.stream_index= video_st->index;
+                                               pkt.data= (uint8_t *)output;
+                                               pkt.size= sizeof(AVPicture);
+
+                                               ret = av_write_frame(oc, &pkt);
+                                               video_pts += c->frame_size;
+                                       } 
+                                       else 
+                                       {
+                                               // Set the quality
+                                               output->quality = video_st->quality;
+
+                                               // Set frame interlace hints
+                                               output->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" );
+                                               output->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" );
+
+                                               // Encode the image
+                                               out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, output );
+
+                                               // If zero size, it means the image was buffered
+                                               if (out_size > 0) 
+                                               {
+                                                       AVPacket pkt;
+                                                       av_init_packet( &pkt );
+
+                                                       if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+                                                               pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, video_st->time_base );
+                                                       if( c->coded_frame && c->coded_frame->key_frame )
+                                                               pkt.flags |= PKT_FLAG_KEY;
+                                                       pkt.stream_index= video_st->index;
+                                                       pkt.data= video_outbuf;
+                                                       pkt.size= out_size;
+
+                                                       // write the compressed frame in the media file
+                                                       ret = av_interleaved_write_frame(oc, &pkt);
+                                                       video_pts += c->frame_size;
+                                                       
+                                                       // Dual pass logging
+                                                       if ( mlt_properties_get_data( properties, "_logfile", NULL ) && c->stats_out)
+                                                               fprintf( mlt_properties_get_data( properties, "_logfile", NULL ), "%s", c->stats_out );
+                                               } 
+                                               else
+                                               {
+                                                       fprintf( stderr, "%s: error with video encode\n", __FILE__ );
+                                               }
+                                       }
+                                       frame_count++;
+                                       mlt_frame_close( frame );
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+               }
+
+               if ( real_time_output == 1 && frames % 12 == 0 )
+               {
+                       long passed = time_difference( &ante );
+                       if ( fifo != NULL )
+                       {
+                               long pending = ( ( ( long )sample_fifo_used( fifo ) * 1000 ) / frequency ) * 1000;
+                               passed -= pending;
+                       }
+                       if ( passed < total_time )
+                       {
+                               long total = ( total_time - passed );
+                               struct timespec t = { total / 1000000, ( total % 1000000 ) * 1000 };
+                               nanosleep( &t, NULL );
+                       }
+               }
+       }
+
+#ifdef FLUSH
+       if ( ! real_time_output )
+       {
+               // Flush audio fifo
+               if ( audio_st && audio_st->codec->frame_size > 1 ) for (;;)
+               {
+                       AVCodecContext *c = audio_st->codec;
+                       AVPacket pkt;
+                       av_init_packet( &pkt );
+                       pkt.size = 0;
+
+                       if ( /*( c->capabilities & CODEC_CAP_SMALL_LAST_FRAME ) &&*/
+                               ( channels * audio_input_frame_size < sample_fifo_used( fifo ) ) )
+                       {
+                               sample_fifo_fetch( fifo, buffer, channels * audio_input_frame_size );
+                               pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, buffer );
+                       }
+                       if ( pkt.size <= 0 )
+                               pkt.size = avcodec_encode_audio( c, audio_outbuf, audio_outbuf_size, NULL );
+                       if ( pkt.size <= 0 )
+                               break;
+
+                       // Write the compressed frame in the media file
+                       if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+                               pkt.pts = av_rescale_q( c->coded_frame->pts, c->time_base, audio_st->time_base );
+                       pkt.flags |= PKT_FLAG_KEY;
+                       pkt.stream_index = audio_st->index;
+                       pkt.data = audio_outbuf;
+                       if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
+                       {
+                               fprintf( stderr, "%s: Error while writing flushed audio frame\n", __FILE__ );
+                               break;
+                       }
+               }
+
+               // Flush video
+               if ( video_st && !( oc->oformat->flags & AVFMT_RAWPICTURE ) ) for (;;)
+               {
+                       AVCodecContext *c = video_st->codec;
+                       AVPacket pkt;
+                       av_init_packet( &pkt );
+
+                       // Encode the image
+                       pkt.size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, NULL );
+                       if ( pkt.size <= 0 )
+                               break;
+
+                       if ( c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE )
+                               pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, video_st->time_base );
+                       if( c->coded_frame && c->coded_frame->key_frame )
+                               pkt.flags |= PKT_FLAG_KEY;
+                       pkt.stream_index = video_st->index;
+                       pkt.data = video_outbuf;
+
+                       // write the compressed frame in the media file
+                       if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
+                       {
+                               fprintf( stderr, "%s: Error while writing flushed video frame\n". __FILE__ );
+                               break;
+                       }
+               }
+       }
+#endif
+
+       // close each codec 
+       if (video_st)
+               close_video(oc, video_st);
+       if (audio_st)
+               close_audio(oc, audio_st);
+
+       // Write the trailer, if any
+       av_write_trailer(oc);
+
+       // Free the streams
+       for(i = 0; i < oc->nb_streams; i++)
+               av_freep(&oc->streams[i]);
+
+       // Close the output file
+       if (!(fmt->flags & AVFMT_NOFILE))
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(0<<8)+0)
+               url_fclose(oc->pb);
+#else
+               url_fclose(&oc->pb);
+#endif
+
+       // Clean up input and output frames
+       if ( output )
+               av_free( output->data[0] );
+       av_free( output );
+       av_free( input->data[0] );
+       av_free( input );
+       av_free( video_outbuf );
+       av_free( buffer );
+
+       // Free the stream
+       av_free(oc);
+
+       // Just in case we terminated on pause
+       mlt_properties_set_int( properties, "running", 0 );
+
+       mlt_consumer_stopped( this );
+
+       if ( mlt_properties_get_int( properties, "pass" ) == 2 )
+       {
+               // Remove the dual pass log file
+               if ( mlt_properties_get( properties, "_logfilename" ) )
+                       remove( mlt_properties_get( properties, "_logfilename" ) );
+
+               // Remove the x264 dual pass logs
+               char *cwd = getcwd( NULL, 0 );
+               const char *file = "x264_2pass.log";
+               char *full = malloc( strlen( cwd ) + strlen( file ) + 2 );
+               sprintf( full, "%s/%s", cwd, file );
+               remove( full );
+               free( full );
+               file = "x264_2pass.log.temp";
+               full = malloc( strlen( cwd ) + strlen( file ) + 2 );
+               sprintf( full, "%s/%s", cwd, file );
+               remove( full );
+               free( full );
+               free( cwd );
+               remove( "x264_2pass.log.temp" );
+       }
+
+       return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+       // Stop the consumer
+       mlt_consumer_stop( this );
+
+       // Close the parent
+       mlt_consumer_close( this );
+
+       // Free the memory
+       free( this );
+}
diff --git a/src/modules/avformat/factory.c b/src/modules/avformat/factory.c
new file mode 100644 (file)
index 0000000..54adf93
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <pthread.h>
+#include <limits.h>
+
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_avformat_init( mlt_profile profile, char *file );
+extern mlt_filter filter_avcolour_space_init( void *arg );
+extern mlt_filter filter_avdeinterlace_init( void *arg );
+extern mlt_filter filter_avresample_init( char *arg );
+extern mlt_filter filter_swscale_init( mlt_profile profile, char *arg );
+extern mlt_producer producer_avformat_init( mlt_profile profile, char *file );
+
+// ffmpeg Header files
+#include <avformat.h>
+
+// A static flag used to determine if avformat has been initialised
+static int avformat_initialised = 0;
+
+// A locking mutex
+static pthread_mutex_t avformat_mutex;
+
+#if 0
+// These 3 functions should override the alloc functions in libavformat
+// but some formats or codecs seem to crash when used (wmv in particular)
+
+void *av_malloc( unsigned int size )
+{
+       return mlt_pool_alloc( size );
+}
+
+void *av_realloc( void *ptr, unsigned int size )
+{
+       return mlt_pool_realloc( ptr, size );
+}
+
+void av_free( void *ptr )
+{
+       return mlt_pool_release( ptr );
+}
+#endif
+
+void avformat_destroy( void *ignore )
+{
+       // Clean up
+       // av_free_static( ); -XXX this is deprecated
+
+       // Destroy the mutex
+       pthread_mutex_destroy( &avformat_mutex );
+}
+
+void avformat_lock( )
+{
+       // Lock the mutex now
+       pthread_mutex_lock( &avformat_mutex );
+}
+
+void avformat_unlock( )
+{
+       // Unlock the mutex now
+       pthread_mutex_unlock( &avformat_mutex );
+}
+
+static void avformat_init( )
+{
+       // Initialise avformat if necessary
+       if ( avformat_initialised == 0 )
+       {
+               avformat_initialised = 1;
+               pthread_mutex_init( &avformat_mutex, NULL );
+               av_register_all( );
+               mlt_factory_register_for_clean_up( NULL, avformat_destroy );
+               av_log_set_level( mlt_log_get_level() );
+       }
+}
+
+static void *create_service( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
+{
+       avformat_init( );
+#ifdef CODECS
+       if ( !strcmp( id, "avformat" ) )
+       {
+               if ( type == producer_type )
+                       return producer_avformat_init( profile, arg );
+               else if ( type == consumer_type )
+                       return consumer_avformat_init( profile, arg );
+       }
+#endif
+#ifdef FILTERS
+       if ( !strcmp( id, "avcolour_space" ) )
+               return filter_avcolour_space_init( arg );
+       if ( !strcmp( id, "avdeinterlace" ) )
+               return filter_avdeinterlace_init( arg );
+       if ( !strcmp( id, "avresample" ) )
+               return filter_avresample_init( arg );
+#ifdef SWSCALE
+       if ( !strcmp( id, "swscale" ) )
+               return filter_swscale_init( profile, arg );
+#endif
+#endif
+       return NULL;
+}
+
+static mlt_properties avformat_metadata( mlt_service_type type, const char *id, void *data )
+{
+       char file[ PATH_MAX ];
+       const char *service_type = NULL;
+       switch ( type )
+       {
+               case consumer_type:
+                       service_type = "consumer";
+                       break;
+               case filter_type:
+                       service_type = "filter";
+                       break;
+               case producer_type:
+                       service_type = "producer";
+                       break;
+               case transition_type:
+                       service_type = "transition";
+                       break;
+               default:
+                       return NULL;
+       }
+       snprintf( file, PATH_MAX, "%s/avformat/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
+       return mlt_properties_parse_yaml( file );
+}
+
+MLT_REPOSITORY
+{
+#ifdef CODECS
+       MLT_REGISTER( consumer_type, "avformat", create_service );
+       MLT_REGISTER( producer_type, "avformat", create_service );
+       MLT_REGISTER_METADATA( producer_type, "avformat", avformat_metadata, NULL );
+#endif
+#ifdef FILTERS
+       MLT_REGISTER( filter_type, "avcolour_space", create_service );
+       MLT_REGISTER( filter_type, "avcolor_space", create_service );
+       MLT_REGISTER( filter_type, "avdeinterlace", create_service );
+       MLT_REGISTER( filter_type, "avresample", create_service );
+#ifdef SWSCALE
+       MLT_REGISTER( filter_type, "swscale", create_service );
+#endif
+#endif
+}
diff --git a/src/modules/avformat/filter_avcolour_space.c b/src/modules/avformat/filter_avcolour_space.c
new file mode 100644 (file)
index 0000000..7ca8e49
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * filter_avcolour_space.c -- Colour space filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+#ifdef SWSCALE
+#include <swscale.h>
+#endif
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static inline int is_big_endian( )
+{
+       union { int i; char c[ 4 ]; } big_endian_test;
+       big_endian_test.i = 1;
+
+       return big_endian_test.c[ 0 ] != 1;
+}
+
+static inline int convert_mlt_to_av_cs( mlt_image_format format )
+{
+       int value = 0;
+
+       switch( format )
+       {
+               case mlt_image_rgb24:
+                       value = PIX_FMT_RGB24;
+                       break;
+               case mlt_image_rgb24a:
+                       value = PIX_FMT_RGB32;
+                       break;
+               case mlt_image_yuv422:
+                       value = PIX_FMT_YUYV422;
+                       break;
+               case mlt_image_yuv420p:
+                       value = PIX_FMT_YUV420P;
+                       break;
+               case mlt_image_opengl:
+               case mlt_image_none:
+                       fprintf( stderr, "Invalid format...\n" );
+                       break;
+       }
+
+       return value;
+}
+
+static inline void convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int width, int height )
+{
+       AVPicture input;
+       AVPicture output;
+       avpicture_fill( &input, in, in_fmt, width, height );
+       avpicture_fill( &output, out, out_fmt, width, height );
+#ifdef SWSCALE
+       struct SwsContext *context = sws_getContext( width, height, in_fmt,
+               width, height, out_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+       sws_scale( context, input.data, input.linesize, 0, height,
+               output.data, output.linesize);
+       sws_freeContext( context );
+#else
+       img_convert( &output, out_fmt, &input, in_fmt, width, height );
+#endif
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_filter filter = mlt_frame_pop_service( this );
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+       int output_format = *format;
+       mlt_image_format forced = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "forced" );
+       int error = 0;
+
+       // Allow this filter to force processing in a colour space other than requested
+       *format = forced != 0 ? forced : *format;
+   
+       error = mlt_frame_get_image( this, image, format, width, height, 0 );
+
+       if ( error == 0 && *format != output_format && *image != NULL && output_format != mlt_image_opengl )
+       {
+               int in_fmt = convert_mlt_to_av_cs( *format );
+               int out_fmt = convert_mlt_to_av_cs( output_format );
+               int size = avpicture_get_size( out_fmt, *width, *height );
+               uint8_t *output = mlt_pool_alloc( size );
+               convert_image( output, *image, out_fmt, in_fmt, *width, *height );
+
+               // Special case for alpha rgb input
+               if ( *format == mlt_image_rgb24a )
+               {
+                       register uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+                       register int len = *width * *height;
+                       register uint8_t *bits = *image;
+                       register int n = ( len + 7 ) / 8;
+
+                       if( !is_big_endian( ) )
+                               bits += 3;
+
+                       // Extract alpha mask from the image using Duff's Device
+                       switch( len % 8 )
+                       {
+                               case 0: do { *alpha ++ = *bits; bits += 4;
+                               case 7:          *alpha ++ = *bits; bits += 4;
+                               case 6:          *alpha ++ = *bits; bits += 4;
+                               case 5:          *alpha ++ = *bits; bits += 4;
+                               case 4:          *alpha ++ = *bits; bits += 4;
+                               case 3:          *alpha ++ = *bits; bits += 4;
+                               case 2:          *alpha ++ = *bits; bits += 4;
+                               case 1:          *alpha ++ = *bits; bits += 4;
+                                               }
+                                               while( --n );
+                       }
+               }
+
+               // Update the output
+               *image = output;
+               *format = output_format;
+               mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "format", output_format );
+
+               // Special case for alpha rgb output
+               if ( *format == mlt_image_rgb24a )
+               {
+                       // Fetch the alpha
+                       register uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+                       if ( alpha != NULL )
+                       {
+                               register uint8_t *bits = *image;
+                               register int len = *width * *height;
+                               register int n = ( len + 7 ) / 8;
+                               
+                               if( !is_big_endian( ) )
+                                       bits += 3;
+
+                               // Merge the alpha mask into the RGB image using Duff's Device
+                               switch( len % 8 )
+                               {
+                                       case 0: do { *bits = *alpha++; bits += 4;
+                                       case 7:          *bits = *alpha++; bits += 4;
+                                       case 6:          *bits = *alpha++; bits += 4;
+                                       case 5:          *bits = *alpha++; bits += 4;
+                                       case 4:          *bits = *alpha++; bits += 4;
+                                       case 3:          *bits = *alpha++; bits += 4;
+                                       case 2:          *bits = *alpha++; bits += 4;
+                                       case 1:          *bits = *alpha++; bits += 4;
+                                                       }
+                                                       while( --n );
+                               }
+                       }
+               }
+       }
+       else if ( error == 0 && *format != output_format && *image != NULL && output_format == mlt_image_opengl )
+       {
+               if ( *format == mlt_image_yuv422 )
+               {
+                       int size = *width * *height * 4;
+                       uint8_t *output = mlt_pool_alloc( size );
+                       int h = *height;
+                       int w = *width;
+                       uint8_t *o = output + size;
+                       int ostride = w * 4;
+                       uint8_t *p = *image;
+                       uint8_t *alpha = mlt_frame_get_alpha_mask( this ) + *width * *height;
+                       int r, g, b;
+
+                       while( h -- )
+                       {
+                               w = *width;
+                               o -= ostride;
+                               alpha -= *width;
+                               while( w >= 2 )
+                               {
+                                       YUV2RGB( *p, *( p + 1 ), *( p + 3 ), r, g, b );
+                                       *o ++ = r;
+                                       *o ++ = g;
+                                       *o ++ = b;
+                                       *o ++ = *alpha ++;
+                                       YUV2RGB( *( p + 2 ), *( p + 1 ), *( p + 3 ), r, g, b );
+                                       *o ++ = r;
+                                       *o ++ = g;
+                                       *o ++ = b;
+                                       *o ++ = *alpha ++;
+                                       w -= 2;
+                                       p += 4;
+                               }
+                               o -= ostride;
+                               alpha -= *width;
+                       }
+
+                       mlt_properties_set_data( properties, "image", output, size, mlt_pool_release, NULL );
+                       mlt_properties_set_int( properties, "format", output_format );
+                       *image = output;
+                       *format = output_format;
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avcolour_space_init( void *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+               this->process = filter_process;
+       return this;
+}
+
diff --git a/src/modules/avformat/filter_avdeinterlace.c b/src/modules/avformat/filter_avdeinterlace.c
new file mode 100644 (file)
index 0000000..d4afa35
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * filter_avdeinterlace.c -- deinterlace filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+
+#ifdef USE_MMX
+#include "mmx.h"
+#else
+#define MAX_NEG_CROP 1024
+extern uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP];
+#endif
+
+#ifdef USE_MMX
+#define DEINT_INPLACE_LINE_LUM \
+                    movd_m2r(lum_m4[0],mm0);\
+                    movd_m2r(lum_m3[0],mm1);\
+                    movd_m2r(lum_m2[0],mm2);\
+                    movd_m2r(lum_m1[0],mm3);\
+                    movd_m2r(lum[0],mm4);\
+                    punpcklbw_r2r(mm7,mm0);\
+                    movd_r2m(mm2,lum_m4[0]);\
+                    punpcklbw_r2r(mm7,mm1);\
+                    punpcklbw_r2r(mm7,mm2);\
+                    punpcklbw_r2r(mm7,mm3);\
+                    punpcklbw_r2r(mm7,mm4);\
+                    paddw_r2r(mm3,mm1);\
+                    psllw_i2r(1,mm2);\
+                    paddw_r2r(mm4,mm0);\
+                    psllw_i2r(2,mm1);\
+                    paddw_r2r(mm6,mm2);\
+                    paddw_r2r(mm2,mm1);\
+                    psubusw_r2r(mm0,mm1);\
+                    psrlw_i2r(3,mm1);\
+                    packuswb_r2r(mm7,mm1);\
+                    movd_r2m(mm1,lum_m2[0]);
+
+#define DEINT_LINE_LUM \
+                    movd_m2r(lum_m4[0],mm0);\
+                    movd_m2r(lum_m3[0],mm1);\
+                    movd_m2r(lum_m2[0],mm2);\
+                    movd_m2r(lum_m1[0],mm3);\
+                    movd_m2r(lum[0],mm4);\
+                    punpcklbw_r2r(mm7,mm0);\
+                    punpcklbw_r2r(mm7,mm1);\
+                    punpcklbw_r2r(mm7,mm2);\
+                    punpcklbw_r2r(mm7,mm3);\
+                    punpcklbw_r2r(mm7,mm4);\
+                    paddw_r2r(mm3,mm1);\
+                    psllw_i2r(1,mm2);\
+                    paddw_r2r(mm4,mm0);\
+                    psllw_i2r(2,mm1);\
+                    paddw_r2r(mm6,mm2);\
+                    paddw_r2r(mm2,mm1);\
+                    psubusw_r2r(mm0,mm1);\
+                    psrlw_i2r(3,mm1);\
+                    packuswb_r2r(mm7,mm1);\
+                    movd_r2m(mm1,dst[0]);
+#endif
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+/* filter parameters: [-1 4 2 4 -1] // 8 */
+static inline void deinterlace_line(uint8_t *dst, 
+                            const uint8_t *lum_m4, const uint8_t *lum_m3, 
+                            const uint8_t *lum_m2, const uint8_t *lum_m1, 
+                            const uint8_t *lum,
+                            int size)
+{
+#ifndef USE_MMX
+    uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+    int sum;
+
+    for(;size > 0;size--) {
+        sum = -lum_m4[0];
+        sum += lum_m3[0] << 2;
+        sum += lum_m2[0] << 1;
+        sum += lum_m1[0] << 2;
+        sum += -lum[0];
+        dst[0] = cm[(sum + 4) >> 3];
+        lum_m4++;
+        lum_m3++;
+        lum_m2++;
+        lum_m1++;
+        lum++;
+        dst++;
+    }
+#else
+
+    {
+        mmx_t rounder;
+        rounder.uw[0]=4;
+        rounder.uw[1]=4;
+        rounder.uw[2]=4;
+        rounder.uw[3]=4;
+        pxor_r2r(mm7,mm7);
+        movq_m2r(rounder,mm6);
+    }
+    for (;size > 3; size-=4) {
+        DEINT_LINE_LUM
+        lum_m4+=4;
+        lum_m3+=4;
+        lum_m2+=4;
+        lum_m1+=4;
+        lum+=4;
+        dst+=4;
+    }
+#endif
+}
+static inline void deinterlace_line_inplace(uint8_t *lum_m4, uint8_t *lum_m3, uint8_t *lum_m2, uint8_t *lum_m1, uint8_t *lum,
+                             int size)
+{
+#ifndef USE_MMX
+    uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
+    int sum;
+
+    for(;size > 0;size--) {
+        sum = -lum_m4[0];
+        sum += lum_m3[0] << 2;
+        sum += lum_m2[0] << 1;
+        lum_m4[0]=lum_m2[0];
+        sum += lum_m1[0] << 2;
+        sum += -lum[0];
+        lum_m2[0] = cm[(sum + 4) >> 3];
+        lum_m4++;
+        lum_m3++;
+        lum_m2++;
+        lum_m1++;
+        lum++;
+    }
+#else
+
+    {
+        mmx_t rounder;
+        rounder.uw[0]=4;
+        rounder.uw[1]=4;
+        rounder.uw[2]=4;
+        rounder.uw[3]=4;
+        pxor_r2r(mm7,mm7);
+        movq_m2r(rounder,mm6);
+    }
+    for (;size > 3; size-=4) {
+        DEINT_INPLACE_LINE_LUM
+        lum_m4+=4;
+        lum_m3+=4;
+        lum_m2+=4;
+        lum_m1+=4;
+        lum+=4;
+    }
+#endif
+}
+
+/* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The
+   top field is copied as is, but the bottom field is deinterlaced
+   against the top field. */
+static inline void deinterlace_bottom_field(uint8_t *dst, int dst_wrap,
+                                    const uint8_t *src1, int src_wrap,
+                                    int width, int height)
+{
+    const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2;
+    int y;
+
+    src_m2 = src1;
+    src_m1 = src1;
+    src_0=&src_m1[src_wrap];
+    src_p1=&src_0[src_wrap];
+    src_p2=&src_p1[src_wrap];
+    for(y=0;y<(height-2);y+=2) {
+        memcpy(dst,src_m1,width);
+        dst += dst_wrap;
+        deinterlace_line(dst,src_m2,src_m1,src_0,src_p1,src_p2,width);
+        src_m2 = src_0;
+        src_m1 = src_p1;
+        src_0 = src_p2;
+        src_p1 += 2*src_wrap;
+        src_p2 += 2*src_wrap;
+        dst += dst_wrap;
+    }
+    memcpy(dst,src_m1,width);
+    dst += dst_wrap;
+    /* do last line */
+    deinterlace_line(dst,src_m2,src_m1,src_0,src_0,src_0,width);
+}
+
+static inline void deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap,
+                                            int width, int height)
+{
+    uint8_t *src_m1, *src_0, *src_p1, *src_p2;
+    int y;
+    uint8_t *buf;
+    buf = (uint8_t*)av_malloc(width);
+
+    src_m1 = src1;
+    memcpy(buf,src_m1,width);
+    src_0=&src_m1[src_wrap];
+    src_p1=&src_0[src_wrap];
+    src_p2=&src_p1[src_wrap];
+    for(y=0;y<(height-2);y+=2) {
+        deinterlace_line_inplace(buf,src_m1,src_0,src_p1,src_p2,width);
+        src_m1 = src_p1;
+        src_0 = src_p2;
+        src_p1 += 2*src_wrap;
+        src_p2 += 2*src_wrap;
+    }
+    /* do last line */
+    deinterlace_line_inplace(buf,src_m1,src_0,src_0,src_0,width);
+    av_free(buf);
+}
+
+
+/* deinterlace - if not supported return -1 */
+static int mlt_avpicture_deinterlace(AVPicture *dst, const AVPicture *src,
+                          int pix_fmt, int width, int height)
+{
+    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)
+        return -1;
+    if ((width & 3) != 0 || (height & 3) != 0)
+        return -1;
+
+       if ( pix_fmt != PIX_FMT_YUYV422 )
+       {
+      for(i=0;i<3;i++) {
+          if (i == 1) {
+              switch(pix_fmt) {
+              case PIX_FMT_YUV420P:
+                  width >>= 1;
+                  height >>= 1;
+                  break;
+              case PIX_FMT_YUV422P:
+                  width >>= 1;
+                  break;
+              case PIX_FMT_YUV411P:
+                  width >>= 2;
+                  break;
+              default:
+                  break;
+              }
+          }
+          if (src == dst) {
+              deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i],
+                                   width, height);
+          } else {
+              deinterlace_bottom_field(dst->data[i],dst->linesize[i],
+                                          src->data[i], src->linesize[i],
+                                          width, height);
+          }
+         }
+    }
+       else {
+      if (src == dst) {
+          deinterlace_bottom_field_inplace(dst->data[0], dst->linesize[0],
+                               width<<1, height);
+      } else {
+          deinterlace_bottom_field(dst->data[0],dst->linesize[0],
+                                      src->data[0], src->linesize[0],
+                                      width<<1, height);
+      }
+       }
+
+#ifdef USE_MMX
+    emms();
+#endif
+    return 0;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = 0;
+       int deinterlace = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "consumer_deinterlace" );
+
+       // Determine if we need a writable version or not
+       if ( deinterlace && !writable )
+                writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" );
+
+       // Get the input image
+       error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+       // Check that we want progressive and we aren't already progressive
+       if ( deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" ) )
+       {
+               // Create a picture
+               AVPicture *output = mlt_pool_alloc( sizeof( AVPicture ) );
+
+               // Fill the picture
+               if ( *format == mlt_image_yuv422 )
+               {
+                       avpicture_fill( output, *image, PIX_FMT_YUYV422, *width, *height );
+                       mlt_avpicture_deinterlace( output, output, PIX_FMT_YUYV422, *width, *height );
+               }
+
+               // Free the picture
+               mlt_pool_release( output );
+
+               // Make sure that others know the frame is deinterlaced
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "progressive", 1 );
+       }
+
+       return error;
+}
+
+/** Deinterlace filter processing - this should be lazy evaluation here...
+*/
+
+static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the get_image method on to the stack
+       mlt_frame_push_get_image( frame, filter_get_image );
+       
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avdeinterlace_init( void *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+               this->process = deinterlace_process;
+       return this;
+}
+
diff --git a/src/modules/avformat/filter_avresample.c b/src/modules/avformat/filter_avresample.c
new file mode 100644 (file)
index 0000000..3252246
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * filter_avresample.c -- adjust audio sample frequency
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+
+/** Get the audio.
+*/
+
+static int resample_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_audio( frame );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the resample information
+       int output_rate = mlt_properties_get_int( filter_properties, "frequency" );
+       int16_t *sample_buffer = mlt_properties_get_data( filter_properties, "buffer", NULL );
+
+       // Obtain the resample context if it exists
+       ReSampleContext *resample = mlt_properties_get_data( filter_properties, "audio_resample", NULL );
+
+       // Used to return number of channels in the source
+       int channels_avail = *channels;
+
+       // Loop variable
+       int i;
+
+       // If no resample frequency is specified, default to requested value
+       if ( output_rate == 0 )
+               output_rate = *frequency;
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+       // Duplicate channels as necessary
+       if ( channels_avail < *channels )
+       {
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               int j, k = 0;
+               
+               // Duplicate the existing channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+                               k = ( k + 1 ) % channels_avail;
+                       }
+               }
+               
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+       else if ( channels_avail == 6 && *channels == 2 )
+       {
+               // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               
+               // Drop all but the first *channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+                       new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+               }
+
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+
+       // Return now if no work to do
+       if ( output_rate != *frequency )
+       {
+               // Will store number of samples created
+               int used = 0;
+
+               // Create a resampler if nececessary
+               if ( resample == NULL || *frequency != mlt_properties_get_int( filter_properties, "last_frequency" ) )
+               {
+                       // Create the resampler
+                       resample = audio_resample_init( *channels, *channels, output_rate, *frequency );
+
+                       // And store it on properties
+                       mlt_properties_set_data( filter_properties, "audio_resample", resample, 0, ( mlt_destructor )audio_resample_close, NULL );
+
+                       // And remember what it was created for
+                       mlt_properties_set_int( filter_properties, "last_frequency", *frequency );
+               }
+
+               // Resample the audio
+               used = audio_resample( resample, sample_buffer, *buffer, *samples );
+
+               // Resize if necessary
+               if ( used > *samples )
+               {
+                       *buffer = mlt_pool_realloc( *buffer, *samples * *channels * sizeof( int16_t ) );
+                       mlt_properties_set_data( properties, "audio", *buffer, *channels * used * sizeof( int16_t ), mlt_pool_release, NULL );
+               }
+
+               // Copy samples
+               memcpy( *buffer, sample_buffer, *channels * used * sizeof( int16_t ) );
+
+               // Update output variables
+               *samples = used;
+               *frequency = output_rate;
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Only call this if we have a means to get audio
+       if ( mlt_frame_is_test_audio( frame ) == 0 )
+       {
+               // Push the filter on to the stack
+               mlt_frame_push_audio( frame, this );
+
+               // Assign our get_audio method
+               mlt_frame_push_audio( frame, resample_get_audio );
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_avresample_init( char *arg )
+{
+       // Create a filter
+       mlt_filter this = mlt_filter_new( );
+
+       // Initialise if successful
+       if ( this != NULL )
+       {
+               // Calculate size of the buffer
+               int size = AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t );
+
+               // Allocate the buffer
+               int16_t *buffer = mlt_pool_alloc( size );
+
+               // Assign the process method
+               this->process = filter_process;
+
+               // Deal with argument
+               if ( arg != NULL )
+                       mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "frequency", arg );
+
+               // Default to 2 channel output
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+
+               // Store the buffer
+               mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "buffer", buffer, size, mlt_pool_release, NULL );
+       }
+
+       return this;
+}
diff --git a/src/modules/avformat/filter_swscale.c b/src/modules/avformat/filter_swscale.c
new file mode 100644 (file)
index 0000000..d1266ae
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * filter_swscale.c -- image scaling filter
+ * Copyright (C) 2008-2009 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_factory.h>
+
+
+// ffmpeg Header files
+#include <avformat.h>
+#include <swscale.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_RGB32 PIX_FMT_RGBA32
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+static inline int is_big_endian( )
+{
+       union { int i; char c[ 4 ]; } big_endian_test;
+       big_endian_test.i = 1;
+
+       return big_endian_test.c[ 0 ] != 1;
+}
+
+static inline int convert_mlt_to_av_cs( mlt_image_format format )
+{
+       int value = 0;
+
+       switch( format )
+       {
+               case mlt_image_rgb24:
+                       value = PIX_FMT_RGB24;
+                       break;
+               case mlt_image_rgb24a:
+                       value = PIX_FMT_RGB32;
+                       break;
+               case mlt_image_yuv422:
+                       value = PIX_FMT_YUYV422;
+                       break;
+               case mlt_image_yuv420p:
+                       value = PIX_FMT_YUV420P;
+                       break;
+               case mlt_image_opengl:
+               case mlt_image_none:
+                       fprintf( stderr, "Invalid format...\n" );
+                       break;
+       }
+
+       return value;
+}
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+       // Get the properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the requested interpolation method
+       char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+       // Convert to the SwScale flag
+       int interp = SWS_BILINEAR;
+       if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
+               interp = SWS_POINT;
+       else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
+               interp = SWS_FAST_BILINEAR;
+       else if ( strcmp( interps, "bilinear" ) == 0 )
+               interp = SWS_BILINEAR;
+       else if ( strcmp( interps, "bicubic" ) == 0 )
+               interp = SWS_BICUBIC;
+       else if ( strcmp( interps, "bicublin" ) == 0 )
+               interp = SWS_BICUBLIN;
+       else if ( strcmp( interps, "gauss" ) == 0 )
+               interp = SWS_GAUSS;
+       else if ( strcmp( interps, "sinc" ) == 0 )
+               interp = SWS_SINC;
+       else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "lanczos" ) == 0 )
+               interp = SWS_LANCZOS;
+       else if ( strcmp( interps, "spline" ) == 0 )
+               interp = SWS_SPLINE;
+
+       AVPicture input;
+       AVPicture output;
+       uint8_t *outbuf = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+       // Convert the pixel formats
+       iformat = convert_mlt_to_av_cs( iformat );
+       oformat = convert_mlt_to_av_cs( oformat );
+
+       avpicture_fill( &input, *image, iformat, iwidth, iheight );
+       avpicture_fill( &output, outbuf, oformat, owidth, oheight );
+
+       // Extract the alpha channel
+       if ( iformat == PIX_FMT_RGB32 && oformat == PIX_FMT_YUYV422 )
+       {
+               // Allocate the alpha mask
+               uint8_t *alpha = mlt_pool_alloc( iwidth * ( iheight + 1 ) );
+               if ( alpha )
+               {
+                       // Convert the image and extract alpha
+                       mlt_convert_rgb24a_to_yuv422( *image, iwidth, iheight, iwidth * 4, outbuf, alpha );
+                       mlt_properties_set_data( properties, "alpha", alpha, iwidth * ( iheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+                       iformat = PIX_FMT_YUYV422;
+                       avpicture_fill( &input, outbuf, iformat, iwidth, iheight );
+                       avpicture_fill( &output, *image, oformat, owidth, oheight );
+               }
+       }
+
+       // Create the context and output image
+       struct SwsContext *context = sws_getContext( iwidth, iheight, iformat, owidth, oheight, oformat, interp, NULL, NULL, NULL);
+       assert(context);
+
+       // Perform the scaling
+       sws_scale( context, input.data, input.linesize, 0, iheight, output.data, output.linesize);
+       sws_freeContext( context );
+
+       // Now update the frame
+       mlt_properties_set_data( properties, "image", output.data[0], owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+       mlt_properties_set_int( properties, "width", owidth );
+       mlt_properties_set_int( properties, "height", oheight );
+
+       // Return the output
+       *image = output.data[0];
+
+       // Scale the alpha channel only if exists and not correct size
+       int alpha_size = 0;
+       mlt_properties_get_data( properties, "alpha", &alpha_size );
+       if ( alpha_size > 0 && alpha_size != ( owidth * oheight ) )
+       {
+               // Create the context and output image
+               uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+               if ( alpha )
+               {
+                       iformat = oformat = PIX_FMT_GRAY8;
+                       struct SwsContext *context = sws_getContext( iwidth, iheight, iformat, owidth, oheight, oformat, interp, NULL, NULL, NULL);
+                       avpicture_fill( &input, alpha, iformat, iwidth, iheight );
+                       outbuf = mlt_pool_alloc( owidth * oheight );
+                       avpicture_fill( &output, outbuf, oformat, owidth, oheight );
+
+                       // Perform the scaling
+                       sws_scale( context, input.data, input.linesize, 0, iheight, output.data, output.linesize);
+                       sws_freeContext( context );
+
+                       // Set it back on the frame
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", output.data[0], owidth * oheight, mlt_pool_release, NULL );
+               }
+       }
+
+       return 0;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_swscale_init( mlt_profile profile, void *arg )
+{
+       // Create a new scaler
+       mlt_filter this = mlt_factory_filter( profile, "rescale", arg );
+
+       // If successful, then initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Set the inerpolation
+               mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+               // Set the method
+               mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+       }
+
+       return this;
+}
diff --git a/src/modules/avformat/mmx.h b/src/modules/avformat/mmx.h
new file mode 100644 (file)
index 0000000..7e94cfd
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * mmx.h
+ * Copyright (C) 1997-2001 H. Dietz and R. Fisher
+ */
+#ifndef AVCODEC_I386MMX_H
+#define AVCODEC_I386MMX_H
+
+/*
+ * The type of an value that fits in an MMX register (note that long
+ * long constant values MUST be suffixed by LL and unsigned long long
+ * values by ULL, lest they be truncated by the compiler)
+ */
+
+typedef        union {
+       long long               q;      /* Quadword (64-bit) value */
+       unsigned long long      uq;     /* Unsigned Quadword */
+       int                     d[2];   /* 2 Doubleword (32-bit) values */
+       unsigned int            ud[2];  /* 2 Unsigned Doubleword */
+       short                   w[4];   /* 4 Word (16-bit) values */
+       unsigned short          uw[4];  /* 4 Unsigned Word */
+       char                    b[8];   /* 8 Byte (8-bit) values */
+       unsigned char           ub[8];  /* 8 Unsigned Byte */
+       float                   s[2];   /* Single-precision (32-bit) value */
+} mmx_t;       /* On an 8-byte (64-bit) boundary */
+
+
+#define        mmx_i2r(op,imm,reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "i" (imm) )
+
+#define        mmx_m2r(op,mem,reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "m" (mem))
+
+#define        mmx_r2m(op,reg,mem) \
+       __asm__ __volatile__ (#op " %%" #reg ", %0" \
+                             : "=m" (mem) \
+                             : /* nothing */ )
+
+#define        mmx_r2r(op,regs,regd) \
+       __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+
+#define        emms() __asm__ __volatile__ ("emms")
+
+#define        movd_m2r(var,reg)       mmx_m2r (movd, var, reg)
+#define        movd_r2m(reg,var)       mmx_r2m (movd, reg, var)
+#define        movd_r2r(regs,regd)     mmx_r2r (movd, regs, regd)
+
+#define        movq_m2r(var,reg)       mmx_m2r (movq, var, reg)
+#define        movq_r2m(reg,var)       mmx_r2m (movq, reg, var)
+#define        movq_r2r(regs,regd)     mmx_r2r (movq, regs, regd)
+
+#define        packssdw_m2r(var,reg)   mmx_m2r (packssdw, var, reg)
+#define        packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
+#define        packsswb_m2r(var,reg)   mmx_m2r (packsswb, var, reg)
+#define        packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
+
+#define        packuswb_m2r(var,reg)   mmx_m2r (packuswb, var, reg)
+#define        packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
+
+#define        paddb_m2r(var,reg)      mmx_m2r (paddb, var, reg)
+#define        paddb_r2r(regs,regd)    mmx_r2r (paddb, regs, regd)
+#define        paddd_m2r(var,reg)      mmx_m2r (paddd, var, reg)
+#define        paddd_r2r(regs,regd)    mmx_r2r (paddd, regs, regd)
+#define        paddw_m2r(var,reg)      mmx_m2r (paddw, var, reg)
+#define        paddw_r2r(regs,regd)    mmx_r2r (paddw, regs, regd)
+
+#define        paddsb_m2r(var,reg)     mmx_m2r (paddsb, var, reg)
+#define        paddsb_r2r(regs,regd)   mmx_r2r (paddsb, regs, regd)
+#define        paddsw_m2r(var,reg)     mmx_m2r (paddsw, var, reg)
+#define        paddsw_r2r(regs,regd)   mmx_r2r (paddsw, regs, regd)
+
+#define        paddusb_m2r(var,reg)    mmx_m2r (paddusb, var, reg)
+#define        paddusb_r2r(regs,regd)  mmx_r2r (paddusb, regs, regd)
+#define        paddusw_m2r(var,reg)    mmx_m2r (paddusw, var, reg)
+#define        paddusw_r2r(regs,regd)  mmx_r2r (paddusw, regs, regd)
+
+#define        pand_m2r(var,reg)       mmx_m2r (pand, var, reg)
+#define        pand_r2r(regs,regd)     mmx_r2r (pand, regs, regd)
+
+#define        pandn_m2r(var,reg)      mmx_m2r (pandn, var, reg)
+#define        pandn_r2r(regs,regd)    mmx_r2r (pandn, regs, regd)
+
+#define        pcmpeqb_m2r(var,reg)    mmx_m2r (pcmpeqb, var, reg)
+#define        pcmpeqb_r2r(regs,regd)  mmx_r2r (pcmpeqb, regs, regd)
+#define        pcmpeqd_m2r(var,reg)    mmx_m2r (pcmpeqd, var, reg)
+#define        pcmpeqd_r2r(regs,regd)  mmx_r2r (pcmpeqd, regs, regd)
+#define        pcmpeqw_m2r(var,reg)    mmx_m2r (pcmpeqw, var, reg)
+#define        pcmpeqw_r2r(regs,regd)  mmx_r2r (pcmpeqw, regs, regd)
+
+#define        pcmpgtb_m2r(var,reg)    mmx_m2r (pcmpgtb, var, reg)
+#define        pcmpgtb_r2r(regs,regd)  mmx_r2r (pcmpgtb, regs, regd)
+#define        pcmpgtd_m2r(var,reg)    mmx_m2r (pcmpgtd, var, reg)
+#define        pcmpgtd_r2r(regs,regd)  mmx_r2r (pcmpgtd, regs, regd)
+#define        pcmpgtw_m2r(var,reg)    mmx_m2r (pcmpgtw, var, reg)
+#define        pcmpgtw_r2r(regs,regd)  mmx_r2r (pcmpgtw, regs, regd)
+
+#define        pmaddwd_m2r(var,reg)    mmx_m2r (pmaddwd, var, reg)
+#define        pmaddwd_r2r(regs,regd)  mmx_r2r (pmaddwd, regs, regd)
+
+#define        pmulhw_m2r(var,reg)     mmx_m2r (pmulhw, var, reg)
+#define        pmulhw_r2r(regs,regd)   mmx_r2r (pmulhw, regs, regd)
+
+#define        pmullw_m2r(var,reg)     mmx_m2r (pmullw, var, reg)
+#define        pmullw_r2r(regs,regd)   mmx_r2r (pmullw, regs, regd)
+
+#define        por_m2r(var,reg)        mmx_m2r (por, var, reg)
+#define        por_r2r(regs,regd)      mmx_r2r (por, regs, regd)
+
+#define        pslld_i2r(imm,reg)      mmx_i2r (pslld, imm, reg)
+#define        pslld_m2r(var,reg)      mmx_m2r (pslld, var, reg)
+#define        pslld_r2r(regs,regd)    mmx_r2r (pslld, regs, regd)
+#define        psllq_i2r(imm,reg)      mmx_i2r (psllq, imm, reg)
+#define        psllq_m2r(var,reg)      mmx_m2r (psllq, var, reg)
+#define        psllq_r2r(regs,regd)    mmx_r2r (psllq, regs, regd)
+#define        psllw_i2r(imm,reg)      mmx_i2r (psllw, imm, reg)
+#define        psllw_m2r(var,reg)      mmx_m2r (psllw, var, reg)
+#define        psllw_r2r(regs,regd)    mmx_r2r (psllw, regs, regd)
+
+#define        psrad_i2r(imm,reg)      mmx_i2r (psrad, imm, reg)
+#define        psrad_m2r(var,reg)      mmx_m2r (psrad, var, reg)
+#define        psrad_r2r(regs,regd)    mmx_r2r (psrad, regs, regd)
+#define        psraw_i2r(imm,reg)      mmx_i2r (psraw, imm, reg)
+#define        psraw_m2r(var,reg)      mmx_m2r (psraw, var, reg)
+#define        psraw_r2r(regs,regd)    mmx_r2r (psraw, regs, regd)
+
+#define        psrld_i2r(imm,reg)      mmx_i2r (psrld, imm, reg)
+#define        psrld_m2r(var,reg)      mmx_m2r (psrld, var, reg)
+#define        psrld_r2r(regs,regd)    mmx_r2r (psrld, regs, regd)
+#define        psrlq_i2r(imm,reg)      mmx_i2r (psrlq, imm, reg)
+#define        psrlq_m2r(var,reg)      mmx_m2r (psrlq, var, reg)
+#define        psrlq_r2r(regs,regd)    mmx_r2r (psrlq, regs, regd)
+#define        psrlw_i2r(imm,reg)      mmx_i2r (psrlw, imm, reg)
+#define        psrlw_m2r(var,reg)      mmx_m2r (psrlw, var, reg)
+#define        psrlw_r2r(regs,regd)    mmx_r2r (psrlw, regs, regd)
+
+#define        psubb_m2r(var,reg)      mmx_m2r (psubb, var, reg)
+#define        psubb_r2r(regs,regd)    mmx_r2r (psubb, regs, regd)
+#define        psubd_m2r(var,reg)      mmx_m2r (psubd, var, reg)
+#define        psubd_r2r(regs,regd)    mmx_r2r (psubd, regs, regd)
+#define        psubw_m2r(var,reg)      mmx_m2r (psubw, var, reg)
+#define        psubw_r2r(regs,regd)    mmx_r2r (psubw, regs, regd)
+
+#define        psubsb_m2r(var,reg)     mmx_m2r (psubsb, var, reg)
+#define        psubsb_r2r(regs,regd)   mmx_r2r (psubsb, regs, regd)
+#define        psubsw_m2r(var,reg)     mmx_m2r (psubsw, var, reg)
+#define        psubsw_r2r(regs,regd)   mmx_r2r (psubsw, regs, regd)
+
+#define        psubusb_m2r(var,reg)    mmx_m2r (psubusb, var, reg)
+#define        psubusb_r2r(regs,regd)  mmx_r2r (psubusb, regs, regd)
+#define        psubusw_m2r(var,reg)    mmx_m2r (psubusw, var, reg)
+#define        psubusw_r2r(regs,regd)  mmx_r2r (psubusw, regs, regd)
+
+#define        punpckhbw_m2r(var,reg)          mmx_m2r (punpckhbw, var, reg)
+#define        punpckhbw_r2r(regs,regd)        mmx_r2r (punpckhbw, regs, regd)
+#define        punpckhdq_m2r(var,reg)          mmx_m2r (punpckhdq, var, reg)
+#define        punpckhdq_r2r(regs,regd)        mmx_r2r (punpckhdq, regs, regd)
+#define        punpckhwd_m2r(var,reg)          mmx_m2r (punpckhwd, var, reg)
+#define        punpckhwd_r2r(regs,regd)        mmx_r2r (punpckhwd, regs, regd)
+
+#define        punpcklbw_m2r(var,reg)          mmx_m2r (punpcklbw, var, reg)
+#define        punpcklbw_r2r(regs,regd)        mmx_r2r (punpcklbw, regs, regd)
+#define        punpckldq_m2r(var,reg)          mmx_m2r (punpckldq, var, reg)
+#define        punpckldq_r2r(regs,regd)        mmx_r2r (punpckldq, regs, regd)
+#define        punpcklwd_m2r(var,reg)          mmx_m2r (punpcklwd, var, reg)
+#define        punpcklwd_r2r(regs,regd)        mmx_r2r (punpcklwd, regs, regd)
+
+#define        pxor_m2r(var,reg)       mmx_m2r (pxor, var, reg)
+#define        pxor_r2r(regs,regd)     mmx_r2r (pxor, regs, regd)
+
+
+/* 3DNOW extensions */
+
+#define pavgusb_m2r(var,reg)   mmx_m2r (pavgusb, var, reg)
+#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
+
+
+/* AMD MMX extensions - also available in intel SSE */
+
+
+#define mmx_m2ri(op,mem,reg,imm) \
+        __asm__ __volatile__ (#op " %1, %0, %%" #reg \
+                              : /* nothing */ \
+                              : "X" (mem), "X" (imm))
+#define mmx_r2ri(op,regs,regd,imm) \
+        __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+                              : /* nothing */ \
+                              : "X" (imm) )
+
+#define        mmx_fetch(mem,hint) \
+       __asm__ __volatile__ ("prefetch" #hint " %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+
+#define        maskmovq(regs,maskreg)          mmx_r2ri (maskmovq, regs, maskreg)
+
+#define        movntq_r2m(mmreg,var)           mmx_r2m (movntq, mmreg, var)
+
+#define        pavgb_m2r(var,reg)              mmx_m2r (pavgb, var, reg)
+#define        pavgb_r2r(regs,regd)            mmx_r2r (pavgb, regs, regd)
+#define        pavgw_m2r(var,reg)              mmx_m2r (pavgw, var, reg)
+#define        pavgw_r2r(regs,regd)            mmx_r2r (pavgw, regs, regd)
+
+#define        pextrw_r2r(mmreg,reg,imm)       mmx_r2ri (pextrw, mmreg, reg, imm)
+
+#define        pinsrw_r2r(reg,mmreg,imm)       mmx_r2ri (pinsrw, reg, mmreg, imm)
+
+#define        pmaxsw_m2r(var,reg)             mmx_m2r (pmaxsw, var, reg)
+#define        pmaxsw_r2r(regs,regd)           mmx_r2r (pmaxsw, regs, regd)
+
+#define        pmaxub_m2r(var,reg)             mmx_m2r (pmaxub, var, reg)
+#define        pmaxub_r2r(regs,regd)           mmx_r2r (pmaxub, regs, regd)
+
+#define        pminsw_m2r(var,reg)             mmx_m2r (pminsw, var, reg)
+#define        pminsw_r2r(regs,regd)           mmx_r2r (pminsw, regs, regd)
+
+#define        pminub_m2r(var,reg)             mmx_m2r (pminub, var, reg)
+#define        pminub_r2r(regs,regd)           mmx_r2r (pminub, regs, regd)
+
+#define        pmovmskb(mmreg,reg) \
+       __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
+
+#define        pmulhuw_m2r(var,reg)            mmx_m2r (pmulhuw, var, reg)
+#define        pmulhuw_r2r(regs,regd)          mmx_r2r (pmulhuw, regs, regd)
+
+#define        prefetcht0(mem)                 mmx_fetch (mem, t0)
+#define        prefetcht1(mem)                 mmx_fetch (mem, t1)
+#define        prefetcht2(mem)                 mmx_fetch (mem, t2)
+#define        prefetchnta(mem)                mmx_fetch (mem, nta)
+
+#define        psadbw_m2r(var,reg)             mmx_m2r (psadbw, var, reg)
+#define        psadbw_r2r(regs,regd)           mmx_r2r (psadbw, regs, regd)
+
+#define        pshufw_m2r(var,reg,imm)         mmx_m2ri(pshufw, var, reg, imm)
+#define        pshufw_r2r(regs,regd,imm)       mmx_r2ri(pshufw, regs, regd, imm)
+
+#define        sfence() __asm__ __volatile__ ("sfence\n\t")
+
+#endif /* AVCODEC_I386MMX_H */
diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c
new file mode 100644 (file)
index 0000000..2474f9a
--- /dev/null
@@ -0,0 +1,1469 @@
+/*
+ * producer_avformat.c -- avformat producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// MLT Header files
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// ffmpeg Header files
+#include <avformat.h>
+#include <opt.h>
+#ifdef SWSCALE
+#  include <swscale.h>
+#endif
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+#  include "audioconvert.h"
+#endif
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+
+#if LIBAVUTIL_VERSION_INT < (50<<16)
+#define PIX_FMT_YUYV422 PIX_FMT_YUV422
+#endif
+
+void avformat_lock( );
+void avformat_unlock( );
+
+// Forward references.
+static int producer_open( mlt_producer this, mlt_profile profile, char *file );
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Constructor for libavformat.
+*/
+
+mlt_producer producer_avformat_init( mlt_profile profile, char *file )
+{
+       int error = 0;
+
+       // Report information about available demuxers and codecs as YAML Tiny
+       if ( file && strstr( file, "f-list" ) )
+       {
+               fprintf( stderr, "---\nformats:\n" );
+               AVInputFormat *format = NULL;
+               while ( ( format = av_iformat_next( format ) ) )
+                       fprintf( stderr, "  - %s\n", format->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+       if ( file && strstr( file, "acodec-list" ) )
+       {
+               fprintf( stderr, "---\naudio_codecs:\n" );
+               AVCodec *codec = NULL;
+               while ( ( codec = av_codec_next( codec ) ) )
+                       if ( codec->decode && codec->type == CODEC_TYPE_AUDIO )
+                               fprintf( stderr, "  - %s\n", codec->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+       if ( file && strstr( file, "vcodec-list" ) )
+       {
+               fprintf( stderr, "---\nvideo_codecs:\n" );
+               AVCodec *codec = NULL;
+               while ( ( codec = av_codec_next( codec ) ) )
+                       if ( codec->decode && codec->type == CODEC_TYPE_VIDEO )
+                               fprintf( stderr, "  - %s\n", codec->name );
+               fprintf( stderr, "...\n" );
+               error = 1;
+       }
+       if ( error )
+               return NULL;
+
+       mlt_producer this = NULL;
+
+       // Check that we have a non-NULL argument
+       if ( file != NULL )
+       {
+               // Construct the producer
+               this = calloc( 1, sizeof( struct mlt_producer_s ) );
+
+               // Initialise it
+               if ( mlt_producer_init( this, NULL ) == 0 )
+               {
+                       // Get the properties
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+                       // Set the resource property (required for all producers)
+                       mlt_properties_set( properties, "resource", file );
+
+                       // Register our get_frame implementation
+                       this->get_frame = producer_get_frame;
+
+                       // Open the file
+                       if ( producer_open( this, profile, file ) != 0 )
+                       {
+                               // Clean up
+                               mlt_producer_close( this );
+                               this = NULL;
+                       }
+                       else
+                       {
+                               // Close the file to release resources for large playlists - reopen later as needed
+                               mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+                               mlt_properties_set_data( properties, "audio_context", NULL, 0, NULL, NULL );
+                               mlt_properties_set_data( properties, "video_context", NULL, 0, NULL, NULL );
+
+                               // Default the user-selectable indices from the auto-detected indices
+                               mlt_properties_set_int( properties, "audio_index",  mlt_properties_get_int( properties, "_audio_index" ) );
+                               mlt_properties_set_int( properties, "video_index",  mlt_properties_get_int( properties, "_video_index" ) );
+                       }
+               }
+       }
+
+       return this;
+}
+
+/** Find the default streams.
+*/
+
+static mlt_properties find_default_streams( mlt_properties meta_media, AVFormatContext *context, int *audio_index, int *video_index )
+{
+       int i;
+       char key[200];
+
+       mlt_properties_set_int( meta_media, "meta.media.nb_streams", context->nb_streams );
+
+       // Allow for multiple audio and video streams in the file and select first of each (if available)
+       for( i = 0; i < context->nb_streams; i++ )
+       {
+               // Get the codec context
+               AVStream *stream = context->streams[ i ];
+               if ( ! stream ) continue;
+               AVCodecContext *codec_context = stream->codec;
+               if ( ! codec_context ) continue;
+               AVCodec *codec = avcodec_find_decoder( codec_context->codec_id );
+               if ( ! codec ) continue;
+
+               snprintf( key, sizeof(key), "meta.media.%d.stream.type", i );
+
+               // Determine the type and obtain the first index of each type
+               switch( codec_context->codec_type )
+               {
+                       case CODEC_TYPE_VIDEO:
+                               if ( *video_index < 0 )
+                                       *video_index = i;
+                               mlt_properties_set( meta_media, key, "video" );
+                               snprintf( key, sizeof(key), "meta.media.%d.stream.frame_rate", i );
+                               mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->r_frame_rate ) );
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+                               snprintf( key, sizeof(key), "meta.media.%d.stream.sample_aspect_ratio", i );
+                               mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->sample_aspect_ratio ) );
+#endif
+                               snprintf( key, sizeof(key), "meta.media.%d.codec.pix_fmt", i );
+                               mlt_properties_set( meta_media, key, avcodec_get_pix_fmt_name( codec_context->pix_fmt ) );
+                               snprintf( key, sizeof(key), "meta.media.%d.codec.sample_aspect_ratio", i );
+                               mlt_properties_set_double( meta_media, key, av_q2d( codec_context->sample_aspect_ratio ) );
+                               break;
+                       case CODEC_TYPE_AUDIO:
+                               if ( *audio_index < 0 )
+                                       *audio_index = i;
+                               mlt_properties_set( meta_media, key, "audio" );
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+                               snprintf( key, sizeof(key), "meta.media.%d.codec.sample_fmt", i );
+                               mlt_properties_set( meta_media, key, avcodec_get_sample_fmt_name( codec_context->sample_fmt ) );
+#endif
+                               snprintf( key, sizeof(key), "meta.media.%d.codec.sample_rate", i );
+                               mlt_properties_set_int( meta_media, key, codec_context->sample_rate );
+                               snprintf( key, sizeof(key), "meta.media.%d.codec.channels", i );
+                               mlt_properties_set_int( meta_media, key, codec_context->channels );
+                               break;
+                       default:
+                               break;
+               }
+//             snprintf( key, sizeof(key), "meta.media.%d.stream.time_base", i );
+//             mlt_properties_set_double( meta_media, key, av_q2d( context->streams[ i ]->time_base ) );
+               snprintf( key, sizeof(key), "meta.media.%d.codec.name", i );
+               mlt_properties_set( meta_media, key, codec->name );
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(55<<8)+0))
+               snprintf( key, sizeof(key), "meta.media.%d.codec.long_name", i );
+               mlt_properties_set( meta_media, key, codec->long_name );
+#endif
+               snprintf( key, sizeof(key), "meta.media.%d.codec.bit_rate", i );
+               mlt_properties_set_int( meta_media, key, codec_context->bit_rate );
+//             snprintf( key, sizeof(key), "meta.media.%d.codec.time_base", i );
+//             mlt_properties_set_double( meta_media, key, av_q2d( codec_context->time_base ) );
+               snprintf( key, sizeof(key), "meta.media.%d.codec.profile", i );
+               mlt_properties_set_int( meta_media, key, codec_context->profile );
+               snprintf( key, sizeof(key), "meta.media.%d.codec.level", i );
+               mlt_properties_set_int( meta_media, key, codec_context->level );
+       }
+
+       return meta_media;
+}
+
+/** Producer file destructor.
+*/
+
+static void producer_file_close( void *context )
+{
+       if ( context != NULL )
+       {
+               // Lock the mutex now
+               avformat_lock( );
+
+               // Close the file
+               av_close_input_file( context );
+
+               // Unlock the mutex now
+               avformat_unlock( );
+       }
+}
+
+/** Producer file destructor.
+*/
+
+static void producer_codec_close( void *codec )
+{
+       if ( codec != NULL )
+       {
+               // Lock the mutex now
+               avformat_lock( );
+
+               // Close the file
+               avcodec_close( codec );
+
+               // Unlock the mutex now
+               avformat_unlock( );
+       }
+}
+
+static inline int dv_is_pal( AVPacket *pkt )
+{
+       return pkt->data[3] & 0x80;
+}
+
+static int dv_is_wide( AVPacket *pkt )
+{
+       int i = 80 /* block size */ *3 /* VAUX starts at block 3 */ +3 /* skip block header */;
+
+       for ( ; i < pkt->size; i += 5 /* packet size */ )
+       {
+               if ( pkt->data[ i ] == 0x61 )
+               {
+                       uint8_t x = pkt->data[ i + 2 ] & 0x7;
+                       return ( x == 2 ) || ( x == 7 );
+               }
+       }
+       return 0;
+}
+
+static double get_aspect_ratio( AVStream *stream, AVCodecContext *codec_context, AVPacket *pkt )
+{
+       double aspect_ratio = 1.0;
+
+       if ( codec_context->codec_id == CODEC_ID_DVVIDEO )
+       {
+               if ( pkt )
+               {
+                       if ( dv_is_pal( pkt ) )
+                       {
+                               aspect_ratio = dv_is_wide( pkt )
+                                       ? 64.0/45.0 // 16:9 PAL
+                                       : 16.0/15.0; // 4:3 PAL
+                       }
+                       else
+                       {
+                               aspect_ratio = dv_is_wide( pkt )
+                                       ? 32.0/27.0 // 16:9 NTSC
+                                       : 8.0/9.0; // 4:3 NTSC
+                       }
+               }
+               else
+               {
+                       AVRational ar =
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+                               stream->sample_aspect_ratio;
+#else
+                               codec_context->sample_aspect_ratio;
+#endif
+                       // Override FFmpeg's notion of DV aspect ratios, which are
+                       // based upon a width of 704. Since we do not have a normaliser
+                       // that crops (nor is cropping 720 wide ITU-R 601 video always desirable)
+                       // we just coerce the values to facilitate a passive behaviour through
+                       // the rescale normaliser when using equivalent producers and consumers.
+                       // = display_aspect / (width * height)
+                       if ( ar.num == 10 && ar.den == 11 )
+                               aspect_ratio = 8.0/9.0; // 4:3 NTSC
+                       else if ( ar.num == 59 && ar.den == 54 )
+                               aspect_ratio = 16.0/15.0; // 4:3 PAL
+                       else if ( ar.num == 40 && ar.den == 33 )
+                               aspect_ratio = 32.0/27.0; // 16:9 NTSC
+                       else if ( ar.num == 118 && ar.den == 81 )
+                               aspect_ratio = 64.0/45.0; // 16:9 PAL
+               }
+       }
+       else
+       {
+               AVRational codec_sar = codec_context->sample_aspect_ratio;
+               AVRational stream_sar =
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(21<<8)+0)
+                       stream->sample_aspect_ratio;
+#else
+                       { 0, 1 };
+#endif
+               if ( codec_sar.num > 0 )
+                       aspect_ratio = av_q2d( codec_sar );
+               else if ( stream_sar.num > 0 )
+                       aspect_ratio = av_q2d( stream_sar );
+       }
+       return aspect_ratio;
+}
+
+/** Open the file.
+*/
+
+static int producer_open( mlt_producer this, mlt_profile profile, char *file )
+{
+       // Return an error code (0 == no error)
+       int error = 0;
+
+       // Context for avformat
+       AVFormatContext *context = NULL;
+
+       // Get the properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // We will treat everything with the producer fps
+       double fps = mlt_profile_fps( profile );
+
+       // Lock the mutex now
+       avformat_lock( );
+
+       // If "MRL", then create AVInputFormat
+       AVInputFormat *format = NULL;
+       AVFormatParameters *params = NULL;
+       char *standard = NULL;
+       char *mrl = strchr( file, ':' );
+
+       // AV option (0 = both, 1 = video, 2 = audio)
+       int av = 0;
+
+       // Setting lowest log level
+       av_log_set_level( -1 );
+
+       // Only if there is not a protocol specification that avformat can handle
+       if ( mrl && !url_exist( file ) )
+       {
+               // 'file' becomes format abbreviation
+               mrl[0] = 0;
+
+               // Lookup the format
+               format = av_find_input_format( file );
+
+               // Eat the format designator
+               file = ++mrl;
+
+               if ( format )
+               {
+                       // Allocate params
+                       params = calloc( sizeof( AVFormatParameters ), 1 );
+
+                       // These are required by video4linux (defaults)
+                       params->width = 640;
+                       params->height = 480;
+                       params->time_base= (AVRational){1,25};
+                       // params->device = file;
+                       params->channels = 2;
+                       params->sample_rate = 48000;
+               }
+
+               // XXX: this does not work anymore since avdevice
+               // TODO: make producer_avddevice?
+               // Parse out params
+               mrl = strchr( file, '?' );
+               while ( mrl )
+               {
+                       mrl[0] = 0;
+                       char *name = strdup( ++mrl );
+                       char *value = strchr( name, ':' );
+                       if ( value )
+                       {
+                               value[0] = 0;
+                               value++;
+                               char *t = strchr( value, '&' );
+                               if ( t )
+                                       t[0] = 0;
+                               if ( !strcmp( name, "frame_rate" ) )
+                                       params->time_base.den = atoi( value );
+                               else if ( !strcmp( name, "frame_rate_base" ) )
+                                       params->time_base.num = atoi( value );
+                               else if ( !strcmp( name, "sample_rate" ) )
+                                       params->sample_rate = atoi( value );
+                               else if ( !strcmp( name, "channels" ) )
+                                       params->channels = atoi( value );
+                               else if ( !strcmp( name, "width" ) )
+                                       params->width = atoi( value );
+                               else if ( !strcmp( name, "height" ) )
+                                       params->height = atoi( value );
+                               else if ( !strcmp( name, "standard" ) )
+                               {
+                                       standard = strdup( value );
+                                       params->standard = standard;
+                               }
+                               else if ( !strcmp( name, "av" ) )
+                                       av = atoi( value );
+                       }
+                       free( name );
+                       mrl = strchr( mrl, '&' );
+               }
+       }
+
+       // Now attempt to open the file
+       error = av_open_input_file( &context, file, format, 0, params ) < 0;
+
+       // Cleanup AVFormatParameters
+       free( standard );
+       free( params );
+
+       // If successful, then try to get additional info
+       if ( error == 0 )
+       {
+               // Get the stream info
+               error = av_find_stream_info( context ) < 0;
+
+               // Continue if no error
+               if ( error == 0 )
+               {
+                       // We will default to the first audio and video streams found
+                       int audio_index = -1;
+                       int video_index = -1;
+                       int av_bypass = 0;
+
+                       // Now set properties where we can (use default unknowns if required)
+                       if ( context->duration != AV_NOPTS_VALUE )
+                       {
+                               // This isn't going to be accurate for all formats
+                               mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps + 0.5 );
+                               mlt_properties_set_position( properties, "out", frames - 1 );
+                               mlt_properties_set_position( properties, "length", frames );
+                       }
+
+                       // Find default audio and video streams
+                       find_default_streams( properties, context, &audio_index, &video_index );
+
+                       if ( context->start_time != AV_NOPTS_VALUE )
+                               mlt_properties_set_double( properties, "_start_time", context->start_time );
+
+                       // Check if we're seekable (something funny about mpeg here :-/)
+                       if ( strcmp( file, "pipe:" ) && strncmp( file, "http://", 6 )  && strncmp( file, "udp:", 4 )  && strncmp( file, "tcp:", 4 ) && strncmp( file, "rtsp:", 5 )  && strncmp( file, "rtp:", 4 ) )
+                       {
+                               mlt_properties_set_int( properties, "seekable", av_seek_frame( context, -1, mlt_properties_get_double( properties, "_start_time" ), AVSEEK_FLAG_BACKWARD ) >= 0 );
+                               mlt_properties_set_data( properties, "dummy_context", context, 0, producer_file_close, NULL );
+                               av_open_input_file( &context, file, NULL, 0, NULL );
+                               av_find_stream_info( context );
+                       }
+                       else
+                               av_bypass = 1;
+
+                       // Store selected audio and video indexes on properties
+                       mlt_properties_set_int( properties, "_audio_index", audio_index );
+                       mlt_properties_set_int( properties, "_video_index", video_index );
+                       mlt_properties_set_int( properties, "_last_position", -1 );
+
+                       // Fetch the width, height and aspect ratio
+                       if ( video_index != -1 )
+                       {
+                               AVCodecContext *codec_context = context->streams[ video_index ]->codec;
+                               mlt_properties_set_int( properties, "width", codec_context->width );
+                               mlt_properties_set_int( properties, "height", codec_context->height );
+
+                               if ( codec_context->codec_id == CODEC_ID_DVVIDEO )
+                               {
+                                       // Fetch the first frame of DV so we can read it directly
+                                       AVPacket pkt;
+                                       int ret = 0;
+                                       while ( ret >= 0 )
+                                       {
+                                               ret = av_read_frame( context, &pkt );
+                                               if ( ret >= 0 && pkt.stream_index == video_index && pkt.size > 0 )
+                                               {
+                                                       mlt_properties_set_double( properties, "aspect_ratio",
+                                                               get_aspect_ratio( context->streams[ video_index ], codec_context, &pkt ) );
+                                                       break;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       mlt_properties_set_double( properties, "aspect_ratio",
+                                               get_aspect_ratio( context->streams[ video_index ], codec_context, NULL ) );
+                               }
+                       }
+
+                       // Read Metadata
+                       if (context->title != NULL)
+                               mlt_properties_set(properties, "meta.attr.title.markup", context->title );
+                       if (context->author != NULL)
+                               mlt_properties_set(properties, "meta.attr.author.markup", context->author );
+                       if (context->copyright != NULL)
+                               mlt_properties_set(properties, "meta.attr.copyright.markup", context->copyright );
+                       if (context->comment != NULL)
+                               mlt_properties_set(properties, "meta.attr.comment.markup", context->comment );
+                       if (context->album != NULL)
+                               mlt_properties_set(properties, "meta.attr.album.markup", context->album );
+                       if (context->year != 0)
+                               mlt_properties_set_int(properties, "meta.attr.year.markup", context->year );
+                       if (context->track != 0)
+                               mlt_properties_set_int(properties, "meta.attr.track.markup", context->track );
+
+                       // We're going to cheat here - for a/v files, we will have two contexts (reasoning will be clear later)
+                       if ( av == 0 && audio_index != -1 && video_index != -1 )
+                       {
+                               // We'll use the open one as our video_context
+                               mlt_properties_set_data( properties, "video_context", context, 0, producer_file_close, NULL );
+
+                               // And open again for our audio context
+                               av_open_input_file( &context, file, NULL, 0, NULL );
+                               av_find_stream_info( context );
+
+                               // Audio context
+                               mlt_properties_set_data( properties, "audio_context", context, 0, producer_file_close, NULL );
+                       }
+                       else if ( av != 2 && video_index != -1 )
+                       {
+                               // We only have a video context
+                               mlt_properties_set_data( properties, "video_context", context, 0, producer_file_close, NULL );
+                       }
+                       else if ( audio_index != -1 )
+                       {
+                               // We only have an audio context
+                               mlt_properties_set_data( properties, "audio_context", context, 0, producer_file_close, NULL );
+                       }
+                       else
+                       {
+                               // Something has gone wrong
+                               error = -1;
+                       }
+
+                       mlt_properties_set_int( properties, "av_bypass", av_bypass );
+               }
+       }
+
+       // Unlock the mutex now
+       avformat_unlock( );
+
+       return error;
+}
+
+/** Convert a frame position to a time code.
+*/
+
+static double producer_time_of_frame( mlt_producer this, mlt_position position )
+{
+       return ( double )position / mlt_producer_get_fps( this );
+}
+
+static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format format, int width, int height )
+{
+#ifdef SWSCALE
+       if ( format == mlt_image_yuv420p )
+       {
+               struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+                       width, height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+               AVPicture output;
+               output.data[0] = buffer;
+               output.data[1] = buffer + width * height;
+               output.data[2] = buffer + ( 3 * width * height ) / 2;
+               output.linesize[0] = width;
+               output.linesize[1] = width >> 1;
+               output.linesize[2] = width >> 1;
+               sws_scale( context, frame->data, frame->linesize, 0, height,
+                       output.data, output.linesize);
+               sws_freeContext( context );
+       }
+       else if ( format == mlt_image_rgb24 )
+       {
+               struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+                       width, height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+               AVPicture output;
+               avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height );
+               sws_scale( context, frame->data, frame->linesize, 0, height,
+                       output.data, output.linesize);
+               sws_freeContext( context );
+       }
+       else
+       {
+               struct SwsContext *context = sws_getContext( width, height, pix_fmt,
+                       width, height, PIX_FMT_YUYV422, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+               AVPicture output;
+               avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height );
+               sws_scale( context, frame->data, frame->linesize, 0, height,
+                       output.data, output.linesize);
+               sws_freeContext( context );
+       }
+#else
+       if ( format == mlt_image_yuv420p )
+       {
+               AVPicture pict;
+               pict.data[0] = buffer;
+               pict.data[1] = buffer + width * height;
+               pict.data[2] = buffer + ( 3 * width * height ) / 2;
+               pict.linesize[0] = width;
+               pict.linesize[1] = width >> 1;
+               pict.linesize[2] = width >> 1;
+               img_convert( &pict, PIX_FMT_YUV420P, (AVPicture *)frame, pix_fmt, width, height );
+       }
+       else if ( format == mlt_image_rgb24 )
+       {
+               AVPicture output;
+               avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height );
+               img_convert( &output, PIX_FMT_RGB24, (AVPicture *)frame, pix_fmt, width, height );
+       }
+       else
+       {
+               AVPicture output;
+               avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height );
+               img_convert( &output, PIX_FMT_YUYV422, (AVPicture *)frame, pix_fmt, width, height );
+       }
+#endif
+}
+
+/** Allocate the image buffer and set it on the frame.
+*/
+
+static int allocate_buffer( mlt_properties frame_properties, AVCodecContext *codec_context, uint8_t **buffer, mlt_image_format *format, int *width, int *height )
+{
+       int size = 0;
+
+       if ( codec_context->width == 0 || codec_context->height == 0 )
+               return size;
+
+       *width = codec_context->width;
+       *height = codec_context->height;
+       mlt_properties_set_int( frame_properties, "width", *width );
+       mlt_properties_set_int( frame_properties, "height", *height );
+
+       switch ( *format )
+       {
+               case mlt_image_yuv420p:
+                       size = *width * 3 * ( *height + 1 ) / 2;
+                       break;
+               case mlt_image_rgb24:
+                       size = *width * ( *height + 1 ) * 3;
+                       break;
+               default:
+                       *format = mlt_image_yuv422;
+                       size = *width * ( *height + 1 ) * 2;
+                       break;
+       }
+
+       // Construct the output image
+       *buffer = mlt_pool_alloc( size );
+       if ( *buffer )
+               mlt_properties_set_data( frame_properties, "image", *buffer, size, (mlt_destructor)mlt_pool_release, NULL );
+       else
+               size = 0;
+
+       return size;
+}
+
+/** Get an image from a frame.
+*/
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the properties from the frame
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the frame number of this frame
+       mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" );
+
+       // Get the producer
+       mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL );
+
+       // Get the producer properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Fetch the video_context
+       AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL );
+
+       // Get the video_index
+       int index = mlt_properties_get_int( properties, "video_index" );
+
+       // Obtain the expected frame numer
+       mlt_position expected = mlt_properties_get_position( properties, "_video_expected" );
+
+       // Get the video stream
+       AVStream *stream = context->streams[ index ];
+
+       // Get codec context
+       AVCodecContext *codec_context = stream->codec;
+
+       // Packet
+       AVPacket pkt;
+
+       // Get the conversion frame
+       AVFrame *av_frame = mlt_properties_get_data( properties, "av_frame", NULL );
+
+       // Special case pause handling flag
+       int paused = 0;
+
+       // Special case ffwd handling
+       int ignore = 0;
+
+       // We may want to use the source fps if available
+       double source_fps = mlt_properties_get_double( properties, "source_fps" );
+       double fps = mlt_producer_get_fps( this );
+
+       // This is the physical frame position in the source
+       int req_position = ( int )( position / fps * source_fps + 0.5 );
+
+       // Get the seekable status
+       int seekable = mlt_properties_get_int( properties, "seekable" );
+
+       // Hopefully provide better support for streams...
+       int av_bypass = mlt_properties_get_int( properties, "av_bypass" );
+
+       // Determines if we have to decode all frames in a sequence
+       int must_decode = 1;
+
+       // Temporary hack to improve intra frame only
+       must_decode = strcmp( codec_context->codec->name, "dnxhd" ) &&
+                                 strcmp( codec_context->codec->name, "dvvideo" ) &&
+                                 strcmp( codec_context->codec->name, "huffyuv" ) &&
+                                 strcmp( codec_context->codec->name, "mjpeg" ) &&
+                                 strcmp( codec_context->codec->name, "rawvideo" );
+
+       // Seek if necessary
+       if ( position != expected )
+       {
+               if ( av_frame != NULL && position + 1 == expected )
+               {
+                       // We're paused - use last image
+                       paused = 1;
+               }
+               else if ( !seekable && position > expected && ( position - expected ) < 250 )
+               {
+                       // Fast forward - seeking is inefficient for small distances - just ignore following frames
+                       ignore = ( int )( ( position - expected ) / fps * source_fps );
+               }
+               else if ( seekable && ( position < expected || position - expected >= 12 ) )
+               {
+                       // Calculate the timestamp for the requested frame
+                       int64_t timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 );
+                       if ( ( uint64_t )context->start_time != AV_NOPTS_VALUE )
+                               timestamp += context->start_time;
+                       if ( must_decode )
+                               timestamp -= AV_TIME_BASE;
+                       if ( timestamp < 0 )
+                               timestamp = 0;
+
+                       // Set to the timestamp
+                       av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD );
+
+                       // Remove the cached info relating to the previous position
+                       mlt_properties_set_int( properties, "_current_position", -1 );
+                       mlt_properties_set_int( properties, "_last_position", -1 );
+                       mlt_properties_set_data( properties, "av_frame", NULL, 0, NULL, NULL );
+                       av_frame = NULL;
+               }
+       }
+
+       // Duplicate the last image if necessary (see comment on rawvideo below)
+       int current_position = mlt_properties_get_int( properties, "_current_position" );
+       int got_picture = mlt_properties_get_int( properties, "_got_picture" );
+       if ( av_frame != NULL && got_picture && ( paused || current_position >= req_position ) && av_bypass == 0 )
+       {
+               // Duplicate it
+               if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
+                       convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height );
+               else
+                       mlt_frame_get_image( frame, buffer, format, width, height, writable );
+       }
+       else
+       {
+               int ret = 0;
+               int int_position = 0;
+               got_picture = 0;
+
+               av_init_packet( &pkt );
+
+               // Construct an AVFrame for YUV422 conversion
+               if ( av_frame == NULL )
+                       av_frame = avcodec_alloc_frame( );
+
+               while( ret >= 0 && !got_picture )
+               {
+                       // Read a packet
+                       ret = av_read_frame( context, &pkt );
+
+                       // We only deal with video from the selected video_index
+                       if ( ret >= 0 && pkt.stream_index == index && pkt.size > 0 )
+                       {
+                               // Determine time code of the packet
+                               int_position = ( int )( av_q2d( stream->time_base ) * pkt.dts * source_fps + 0.5 );
+                               if ( context->start_time != AV_NOPTS_VALUE )
+                                       int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 );
+                               int last_position = mlt_properties_get_int( properties, "_last_position" );
+                               if ( int_position == last_position )
+                                       int_position = last_position + 1;
+                               mlt_properties_set_int( properties, "_last_position", int_position );
+
+                               // Decode the image
+                               if ( must_decode || int_position >= req_position )
+                                       ret = avcodec_decode_video( codec_context, av_frame, &got_picture, pkt.data, pkt.size );
+
+                               if ( got_picture )
+                               {
+                                       // Handle ignore
+                                       if ( int_position < req_position )
+                                       {
+                                               ignore = 0;
+                                               got_picture = 0;
+                                       }
+                                       else if ( int_position >= req_position )
+                                       {
+                                               ignore = 0;
+                                       }
+                                       else if ( ignore -- )
+                                       {
+                                               got_picture = 0;
+                                       }
+                               }
+                               av_free_packet( &pkt );
+                       }
+                       else if ( ret >= 0 )
+                       {
+                               av_free_packet( &pkt );
+                       }
+
+                       // Now handle the picture if we have one
+                       if ( got_picture )
+                       {
+                               if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
+                               {
+                                       convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height );
+                                       mlt_properties_set_int( frame_properties, "progressive", !av_frame->interlaced_frame );
+                                       mlt_properties_set_int( properties, "top_field_first", av_frame->top_field_first );
+                                       mlt_properties_set_int( properties, "_current_position", int_position );
+                                       mlt_properties_set_int( properties, "_got_picture", 1 );
+                                       mlt_properties_set_data( properties, "av_frame", av_frame, 0, av_free, NULL );
+                               }
+                               else
+                               {
+                                       got_picture = 0;
+                               }
+                       }
+               }
+               if ( !got_picture )
+                       mlt_frame_get_image( frame, buffer, format, width, height, writable );
+       }
+
+       // Very untidy - for rawvideo, the packet contains the frame, hence the free packet
+       // above will break the pause behaviour - so we wipe the frame now
+       if ( !strcmp( codec_context->codec->name, "rawvideo" ) )
+               mlt_properties_set_data( properties, "av_frame", NULL, 0, NULL, NULL );
+
+       // Set the field order property for this frame
+       mlt_properties_set_int( frame_properties, "top_field_first", mlt_properties_get_int( properties, "top_field_first" ) );
+
+       // Regardless of speed, we expect to get the next frame (cos we ain't too bright)
+       mlt_properties_set_position( properties, "_video_expected", position + 1 );
+
+       return 0;
+}
+
+/** Process properties as AVOptions and apply to AV context obj
+*/
+
+static void apply_properties( void *obj, mlt_properties properties, int flags )
+{
+       int i;
+       int count = mlt_properties_count( properties );
+       for ( i = 0; i < count; i++ )
+       {
+               const char *opt_name = mlt_properties_get_name( properties, i );
+               const AVOption *opt = av_find_opt( obj, opt_name, NULL, flags, flags );
+               if ( opt != NULL )
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(7<<8)+0)
+                       av_set_string3( obj, opt_name, mlt_properties_get( properties, opt_name), 0, NULL );
+#elif LIBAVCODEC_VERSION_INT >= ((51<<16)+(59<<8)+0)
+                       av_set_string2( obj, opt_name, mlt_properties_get( properties, opt_name), 0 );
+#else
+                       av_set_string( obj, opt_name, mlt_properties_get( properties, opt_name) );
+#endif
+       }
+}
+
+/** Set up video handling.
+*/
+
+static void producer_set_up_video( mlt_producer this, mlt_frame frame )
+{
+       // Get the properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Fetch the video_context
+       AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL );
+
+       // Get the video_index
+       int index = mlt_properties_get_int( properties, "video_index" );
+
+       // Reopen the file if necessary
+       if ( !context && index > -1 )
+       {
+               mlt_events_block( properties, this );
+               producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ),
+                       mlt_properties_get( properties, "resource" ) );
+               context = mlt_properties_get_data( properties, "video_context", NULL );
+               mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+               mlt_events_unblock( properties, this );
+
+               // Process properties as AVOptions
+               apply_properties( context, properties, AV_OPT_FLAG_DECODING_PARAM );
+       }
+
+       // Exception handling for video_index
+       if ( context && index >= (int) context->nb_streams )
+       {
+               // Get the last video stream
+               for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO; --index );
+               mlt_properties_set_int( properties, "video_index", index );
+       }
+       if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO )
+       {
+               // Invalidate the video stream
+               index = -1;
+               mlt_properties_set_int( properties, "video_index", index );
+       }
+
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       if ( context && index > -1 )
+       {
+               // Get the video stream
+               AVStream *stream = context->streams[ index ];
+
+               // Get codec context
+               AVCodecContext *codec_context = stream->codec;
+
+               // Get the codec
+               AVCodec *codec = mlt_properties_get_data( properties, "video_codec", NULL );
+
+               // Update the video properties if the index changed
+               if ( index != mlt_properties_get_int( properties, "_video_index" ) )
+               {
+                       // Reset the video properties if the index changed
+                       mlt_properties_set_int( properties, "_video_index", index );
+                       mlt_properties_set_data( properties, "video_codec", NULL, 0, NULL, NULL );
+                       mlt_properties_set_int( properties, "width", codec_context->width );
+                       mlt_properties_set_int( properties, "height", codec_context->height );
+                       // TODO: get the first usable AVPacket and reset the stream position
+                       mlt_properties_set_double( properties, "aspect_ratio",
+                               get_aspect_ratio( context->streams[ index ], codec_context, NULL ) );
+                       codec = NULL;
+               }
+
+               // Initialise the codec if necessary
+               if ( codec == NULL )
+               {
+                       // Initialise multi-threading
+                       int thread_count = mlt_properties_get_int( properties, "threads" );
+                       if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) )
+                               thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) );
+                       if ( thread_count > 1 )
+                       {
+                               avcodec_thread_init( codec_context, thread_count );
+                               codec_context->thread_count = thread_count;
+                       }
+
+                       // Find the codec
+                       codec = avcodec_find_decoder( codec_context->codec_id );
+
+                       // If we don't have a codec and we can't initialise it, we can't do much more...
+                       avformat_lock( );
+                       if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 )
+                       {
+                               // Now store the codec with its destructor
+                               mlt_properties_set_data( properties, "video_codec", codec_context, 0, producer_codec_close, NULL );
+                       }
+                       else
+                       {
+                               // Remember that we can't use this later
+                               mlt_properties_set_int( properties, "video_index", -1 );
+                               index = -1;
+                       }
+                       avformat_unlock( );
+
+                       // Process properties as AVOptions
+                       apply_properties( codec_context, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM );
+               }
+
+               // No codec, no show...
+               if ( codec && index > -1 )
+               {
+                       double source_fps = 0;
+                       double force_aspect_ratio = mlt_properties_get_double( properties, "force_aspect_ratio" );
+                       double aspect_ratio = ( force_aspect_ratio > 0.0 ) ?
+                               force_aspect_ratio : mlt_properties_get_double( properties, "aspect_ratio" );
+
+                       // Determine the fps
+                       source_fps = ( double )codec_context->time_base.den / ( codec_context->time_base.num == 0 ? 1 : codec_context->time_base.num );
+
+                       // We'll use fps if it's available
+                       if ( source_fps > 0 )
+                               mlt_properties_set_double( properties, "source_fps", source_fps );
+                       else
+                               mlt_properties_set_double( properties, "source_fps", mlt_producer_get_fps( this ) );
+                       mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio );
+
+                       // Set the width and height
+                       mlt_properties_set_int( frame_properties, "width", codec_context->width );
+                       mlt_properties_set_int( frame_properties, "height", codec_context->height );
+                       mlt_properties_set_int( frame_properties, "real_width", codec_context->width );
+                       mlt_properties_set_int( frame_properties, "real_height", codec_context->height );
+                       mlt_properties_set_double( frame_properties, "aspect_ratio", aspect_ratio );
+
+                       mlt_frame_push_get_image( frame, producer_get_image );
+                       mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL );
+               }
+               else
+               {
+                       mlt_properties_set_int( frame_properties, "test_image", 1 );
+               }
+       }
+       else
+       {
+               mlt_properties_set_int( frame_properties, "test_image", 1 );
+       }
+}
+
+/** Get the audio from a frame.
+*/
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties from the frame
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the frame number of this frame
+       mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" );
+
+       // Get the producer
+       mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL );
+
+       // Get the producer properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Fetch the audio_context
+       AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL );
+
+       // Get the audio_index
+       int index = mlt_properties_get_int( properties, "audio_index" );
+
+       // Get the seekable status
+       int seekable = mlt_properties_get_int( properties, "seekable" );
+
+       // Obtain the expected frame numer
+       mlt_position expected = mlt_properties_get_position( properties, "_audio_expected" );
+
+       // Obtain the resample context if it exists (not always needed)
+       ReSampleContext *resample = mlt_properties_get_data( properties, "audio_resample", NULL );
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+       // Get the format converter context if it exists
+       AVAudioConvert *convert = mlt_properties_get_data( properties, "audio_convert", NULL );
+#endif
+
+       // Obtain the audio buffers
+       int16_t *audio_buffer = mlt_properties_get_data( properties, "audio_buffer", NULL );
+       int16_t *decode_buffer = mlt_properties_get_data( properties, "decode_buffer", NULL );
+       int16_t *convert_buffer = mlt_properties_get_data( properties, "convert_buffer", NULL );
+
+       // Get amount of audio used
+       int audio_used =  mlt_properties_get_int( properties, "_audio_used" );
+
+       // Calculate the real time code
+       double real_timecode = producer_time_of_frame( this, position );
+
+       // Get the audio stream
+       AVStream *stream = context->streams[ index ];
+
+       // Get codec context
+       AVCodecContext *codec_context = stream->codec;
+
+       // Packet
+       AVPacket pkt;
+
+       // Number of frames to ignore (for ffwd)
+       int ignore = 0;
+
+       // Flag for paused (silence)
+       int paused = 0;
+
+       // Check for resample and create if necessary
+       if ( resample == NULL && codec_context->channels <= 2 )
+       {
+               // Create the resampler
+               resample = audio_resample_init( *channels, codec_context->channels, *frequency, codec_context->sample_rate );
+
+               // And store it on properties
+               mlt_properties_set_data( properties, "audio_resample", resample, 0, ( mlt_destructor )audio_resample_close, NULL );
+       }
+       else if ( resample == NULL )
+       {
+               *channels = codec_context->channels;
+               *frequency = codec_context->sample_rate;
+       }
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+       // Check for audio format converter and create if necessary
+       // TODO: support higher resolutions than 16-bit.
+       if ( convert == NULL && codec_context->sample_fmt != SAMPLE_FMT_S16 )
+       {
+               // Create single channel converter for interleaved with no mixing matrix
+               convert = av_audio_convert_alloc( SAMPLE_FMT_S16, 1, codec_context->sample_fmt, 1, NULL, 0 );
+               mlt_properties_set_data( properties, "audio_convert", convert, 0, ( mlt_destructor )av_audio_convert_free, NULL );
+       }
+#endif
+
+       // Check for audio buffer and create if necessary
+       if ( audio_buffer == NULL )
+       {
+               // Allocate the audio buffer
+               audio_buffer = mlt_pool_alloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+               // And store it on properties for reuse
+               mlt_properties_set_data( properties, "audio_buffer", audio_buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+       }
+
+       // Check for decoder buffer and create if necessary
+       if ( decode_buffer == NULL )
+       {
+               // Allocate the audio buffer
+               decode_buffer = av_malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+               // And store it on properties for reuse
+               mlt_properties_set_data( properties, "decode_buffer", decode_buffer, 0, ( mlt_destructor )av_free, NULL );
+       }
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+       // Check for format converter buffer and create if necessary
+       if ( resample && convert && convert_buffer == NULL )
+       {
+               // Allocate the audio buffer
+               convert_buffer = mlt_pool_alloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
+
+               // And store it on properties for reuse
+               mlt_properties_set_data( properties, "convert_buffer", convert_buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+       }
+#endif
+
+       // Seek if necessary
+       if ( position != expected )
+       {
+               if ( position + 1 == expected )
+               {
+                       // We're paused - silence required
+                       paused = 1;
+               }
+               else if ( !seekable && position > expected && ( position - expected ) < 250 )
+               {
+                       // Fast forward - seeking is inefficient for small distances - just ignore following frames
+                       ignore = position - expected;
+               }
+               else if ( position < expected || position - expected >= 12 )
+               {
+                       int64_t timestamp = ( int64_t )( real_timecode * AV_TIME_BASE + 0.5 );
+                       if ( context->start_time != AV_NOPTS_VALUE )
+                               timestamp += context->start_time;
+                       if ( timestamp < 0 )
+                               timestamp = 0;
+
+                       // Set to the real timecode
+                       if ( av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ) != 0 )
+                               paused = 1;
+
+                       // Clear the usage in the audio buffer
+                       audio_used = 0;
+               }
+       }
+
+       // Get the audio if required
+       if ( !paused )
+       {
+               int ret = 0;
+               int got_audio = 0;
+
+               av_init_packet( &pkt );
+
+               while( ret >= 0 && !got_audio )
+               {
+                       // Check if the buffer already contains the samples required
+                       if ( audio_used >= *samples && ignore == 0 )
+                       {
+                               got_audio = 1;
+                               break;
+                       }
+
+                       // Read a packet
+                       ret = av_read_frame( context, &pkt );
+
+                       int len = pkt.size;
+                       uint8_t *ptr = pkt.data;
+
+                       // We only deal with audio from the selected audio_index
+                       while ( ptr != NULL && ret >= 0 && pkt.stream_index == index && len > 0 )
+                       {
+                               int data_size = sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE;
+
+                               // Decode the audio
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(29<<8)+0))
+                               ret = avcodec_decode_audio2( codec_context, decode_buffer, &data_size, ptr, len );
+#else
+                               ret = avcodec_decode_audio( codec_context, decode_buffer, &data_size, ptr, len );
+#endif
+                               if ( ret < 0 )
+                               {
+                                       ret = 0;
+                                       break;
+                               }
+
+                               len -= ret;
+                               ptr += ret;
+
+                               if ( data_size > 0 )
+                               {
+                                       int src_stride[6]= { av_get_bits_per_sample_format( codec_context->sample_fmt ) / 8 };
+                                       int dst_stride[6]= { av_get_bits_per_sample_format( SAMPLE_FMT_S16 ) / 8 };
+
+                                       if ( resample )
+                                       {
+                                               int16_t *source = decode_buffer;
+                                               int16_t *dest = &audio_buffer[ audio_used * *channels ];
+                                               int convert_samples = data_size / src_stride[0];
+
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+                                               if ( convert )
+                                               {
+                                                       const void *src_buf[6] = { decode_buffer };
+                                                       void *dst_buf[6] = { convert_buffer };
+                                                       av_audio_convert( convert, dst_buf, dst_stride, src_buf, src_stride, convert_samples );
+                                                       source = convert_buffer;
+                                               }
+#endif
+                                               audio_used += audio_resample( resample, dest, source, convert_samples / codec_context->channels );
+                                       }
+                                       else
+                                       {
+#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
+                                               if ( convert )
+                                               {
+                                                       const void *src_buf[6] = { decode_buffer };
+                                                       void *dst_buf[6] = { &audio_buffer[ audio_used * *channels ] };
+                                                       av_audio_convert( convert, dst_buf, dst_stride, src_buf, src_stride, data_size / src_stride[0] );
+                                               }
+                                               else
+#endif
+                                               {
+                                                       memcpy( &audio_buffer[ audio_used * *channels ], decode_buffer, data_size );
+                                               }
+                                               audio_used += data_size / *channels / src_stride[0];
+                                       }
+
+                                       // Handle ignore
+                                       while ( ignore && audio_used > *samples )
+                                       {
+                                               ignore --;
+                                               audio_used -= *samples;
+                                               memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * sizeof( int16_t ) );
+                                       }
+                               }
+
+                               // If we're behind, ignore this packet
+                               if ( pkt.pts >= 0 )
+                               {
+                                       double current_pts = av_q2d( stream->time_base ) * pkt.pts;
+                                       double source_fps = mlt_properties_get_double( properties, "source_fps" );
+                                       int req_position = ( int )( real_timecode * source_fps + 0.5 );
+                                       int int_position = ( int )( current_pts * source_fps + 0.5 );
+
+                                       if ( context->start_time != AV_NOPTS_VALUE )
+                                               int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 );
+                                       if ( seekable && !ignore && int_position < req_position )
+                                               ignore = 1;
+                               }
+                       }
+
+                       // We're finished with this packet regardless
+                       av_free_packet( &pkt );
+               }
+
+               *buffer = mlt_pool_alloc( *samples * *channels * sizeof( int16_t ) );
+               mlt_properties_set_data( frame_properties, "audio", *buffer, 0, ( mlt_destructor )mlt_pool_release, NULL );
+
+               // Now handle the audio if we have enough
+               if ( audio_used >= *samples )
+               {
+                       memcpy( *buffer, audio_buffer, *samples * *channels * sizeof( int16_t ) );
+                       audio_used -= *samples;
+                       memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) );
+               }
+               else
+               {
+                       memset( *buffer, 0, *samples * *channels * sizeof( int16_t ) );
+               }
+
+               // Store the number of audio samples still available
+               mlt_properties_set_int( properties, "_audio_used", audio_used );
+       }
+       else
+       {
+               // Get silence and don't touch the context
+               mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+       }
+
+       // Regardless of speed (other than paused), we expect to get the next frame
+       if ( !paused )
+               mlt_properties_set_position( properties, "_audio_expected", position + 1 );
+
+       return 0;
+}
+
+/** Set up audio handling.
+*/
+
+static void producer_set_up_audio( mlt_producer this, mlt_frame frame )
+{
+       // Get the properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Fetch the audio_context
+       AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL );
+
+       // Get the audio_index
+       int index = mlt_properties_get_int( properties, "audio_index" );
+
+       // Reopen the file if necessary
+       if ( !context && index > -1 )
+       {
+               mlt_events_block( properties, this );
+               producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ),
+                       mlt_properties_get( properties, "resource" ) );
+               context = mlt_properties_get_data( properties, "audio_context", NULL );
+               mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL );
+               mlt_events_unblock( properties, this );
+       }
+
+       // Exception handling for audio_index
+       if ( context && index >= (int) context->nb_streams )
+       {
+               for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO; --index );
+               mlt_properties_set_int( properties, "audio_index", index );
+       }
+       if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO )
+       {
+               index = -1;
+               mlt_properties_set_int( properties, "audio_index", index );
+       }
+
+       // Update the audio properties if the index changed
+       if ( index > -1 && index != mlt_properties_get_int( properties, "_audio_index" ) )
+       {
+               mlt_properties_set_int( properties, "_audio_index", index );
+               mlt_properties_set_data( properties, "audio_codec", NULL, 0, NULL, NULL );
+       }
+
+       // Deal with audio context
+       if ( context != NULL && index > -1 )
+       {
+               // Get the frame properties
+               mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+               // Get the audio stream
+               AVStream *stream = context->streams[ index ];
+
+               // Get codec context
+               AVCodecContext *codec_context = stream->codec;
+
+               // Get the codec
+               AVCodec *codec = mlt_properties_get_data( properties, "audio_codec", NULL );
+
+               // Initialise the codec if necessary
+               if ( codec == NULL )
+               {
+                       // Find the codec
+                       codec = avcodec_find_decoder( codec_context->codec_id );
+
+                       // If we don't have a codec and we can't initialise it, we can't do much more...
+                       avformat_lock( );
+                       if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 )
+                       {
+                               // Now store the codec with its destructor
+                               mlt_properties_set_data( properties, "audio_codec", codec_context, 0, producer_codec_close, NULL );
+
+                       }
+                       else
+                       {
+                               // Remember that we can't use this later
+                               mlt_properties_set_int( properties, "audio_index", -1 );
+                               index = -1;
+                       }
+                       avformat_unlock( );
+
+                       // Process properties as AVOptions
+                       apply_properties( codec_context, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM );
+               }
+
+               // No codec, no show...
+               if ( codec && index > -1 )
+               {
+                       mlt_frame_push_audio( frame, producer_get_audio );
+                       mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL );
+                       mlt_properties_set_int( frame_properties, "frequency", codec_context->sample_rate );
+                       mlt_properties_set_int( frame_properties, "channels", codec_context->channels );
+               }
+       }
+}
+
+/** Our get frame implementation.
+*/
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Create an empty frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+       // Set the position of this producer
+       mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "avformat_position", mlt_producer_frame( this ) );
+
+       // Set up the video
+       producer_set_up_video( this, *frame );
+
+       // Set up the audio
+       producer_set_up_audio( this, *frame );
+
+       // Set the aspect_ratio
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "aspect_ratio", mlt_properties_get_double( MLT_PRODUCER_PROPERTIES( this ), "aspect_ratio" ) );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( this );
+
+       return 0;
+}
diff --git a/src/modules/avformat/producer_avformat.yml b/src/modules/avformat/producer_avformat.yml
new file mode 100644 (file)
index 0000000..86ce738
--- /dev/null
@@ -0,0 +1,171 @@
+schema_version: 0.1
+type: producer # consumer, filter, producer, or transition
+identifier: avformat
+title: FFmpeg Reader
+version: 0.2.5
+copyright: Copyright (C) 2003-2008 Ushodaya Enterprises Limited
+license: LGPL
+language: en
+url: http://www.ffmpeg.org/
+creator: Charles Yates
+contributor:
+  - Dan Dennedy
+tags:
+  - Audio # this may produce audio
+  - Video # this may produce video
+description: Read an audio and/or video file using FFmpeg
+icon:
+  filename: avformat/producer.png # relative to $MLT_DATA/modules/
+  content-type: image/png
+  content-encoding: base64 # could also be hex or none if inline SVG
+  content: |
+    iVBORw0KGgoAAAANSUhEUgAAABgAAAAPCAYAAAD+pA/bAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
+    WXMAAAsSAAALEgHS3X78AAAAB3RJTUUH1gsBEgMLZIL+swAAAB10RVh0Q29tbWVudABDcmVhdGVk
+    IHdpdGggVGhlIEdJTVDvZCVuAAAEgUlEQVQ4y2VUSWwbZRT+5p/xP4v3eIntceLEThOylKgkB070
+    UAUOCNFDFYkIVeqBijMHQKiCAwLEDbVIPVQCUalFQggph6pUoWKRWoS6RCGJ04biOG7sOI638TJe
+    xjM/FwwRfKf3vve99+kd3uNubNxgVb0Km2W7Tbt06fQLpwv4G8u3k6/88nvJcXYhnJ+dGfsRR/Dl
+    8tqpfKkZJ736d++cf6nU53Nra0utTOarVrMpdCj9XFhJr2C1sQq1pZ6ac8zNAljpi7ezzc+e5Lvx
+    je29H8rlyk8DA14GAOl05tT1n6tf39uqBM69GFgAsNjvaezsvKVfuyZkKxXIi4tvknn/PCQi4cA4
+    QMQf+aIv3Nvbm0sVTGe9zfDg8eGxUrk83689eFQcyhStQLluYG+/NNfny6VSsLW5Ge3pOjSHAy2e
+    /4TwjP84KAfBKRzW99f59G7aAQCVav20SZQAtdlQbfGxteTuNACkd/dsguR8W9NNiKKEVFbjisUi
+    BQAtnz8rFAq+LgAWDjN/IpEkk97JzYQjAdkhI2fkwtVG9X0AWN+pwTA5iJSH1qEoaMZC8tFjfv3J
+    IckcdiYNk0GUZVRbXKipt5YAoP7wYZDXNKHOGKR4fCs+NbVGBpwDt3zw3VEUBbqo437qvjuXy7kr
+    HbtqWQzUJgC8Ar2Dpe1UXpgZC763W+RAOAaR2tAxRTmV3kvsJJPjSrf7umEYqAoC1NnZVCAY3CLD
+    6nBpfnC+4hJc4B08DGqc1xr1l5126RzAIHAWqCjjj1wb8eHQq9uZ6nS9a4PAmaACD8bLWN16itrG
+    hoJ8Ptw2TVjRKAZjsWUAIABACHk3RmOQ7BIyegbpg/1oqWGBoAc0U5BFAt20o1CqXSk26YLZM8Aa
+    u5AEAwKV4XQNLHpGRq5zpRJ0QuCdmUGpUPgGAAQAoKBPJ5wTyDfzKLMyGMGntRZAmIHxMA/TTnDA
+    e3FvM+siXhfMdgXjIQbmUpCvMjTa7fFG5imUdhtNSYIcCCxHx8Y6/2zgUTwNq2Nd8FIvYAHFch1d
+    RmHjuhhVfdnjI1LPYVeQaw3gsMZAmYYTk+oT1SdBkiSU6z3U6k30AHR9PrhU9WYoHP7XYHR41Jpw
+    TxSDQhAuzgXLtIMQG0RioFwuX3IrwiWPnUByDsKyGPxKB5S3Phj0UkgiRdMQ0FYcqBECFgj8GY7F
+    7vZvg/QDl+S66mg5fg2IATDBC45jkPgOJhODLa8dN4d8BJQKYN06RgfF/eNTibRpWVcdsg2MV1An
+    FJrbDfXEiWxEVdf/ZxCPxVvTnmkjqgzB4p0gMGGnPUyMjUANupNusXNHpDxYt4wR1XtrKBq9a3W0
+    B047hSg7ofUIxMlJePz+K0d/FjmahLyh146rzyUNw0Jby8LGWvckSbwcjUayx1RpzWZWELR3jZln
+    Eh8CwDFVgcLr4HpNVDtMi588ecEbCHx7dKZwNBkODed+W318Zipk3Rx1iTG9GbwYiUQMAAj53Zef
+    H2u/Ua16L4ZCgykA8A94vn/W1DdH3dK0wIdXwkNDH+E/+Avfv/E5LPIz8wAAAABJRU5ErkJggg==
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - Audio sync discrepancy with some content.
+  - Not all libavformat supported formats are seekable.
+  - >
+    Seeking is not always accurate. Sometimes it doesn't seek to I-frames so you
+    may get junk for a few frames.
+  - >
+    Fails to play beyond first frame of video of sources with PTS not starting
+    at 0 (video4linux).
+
+parameters:
+  - identifier: argument # 'argument' is a reserved name for a value supplied to the factory
+    title: File  # the title can be used as a label for the widget
+    type: string
+    description: |
+      A file name specification or URL in the form:
+      [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]]
+      For example, video4linux:/dev/video1?width:320&height:240
+      Note: on the bash command line, & must be escaped as '\&'.
+      Also, note the use of ':' instead of '=' for parameters.
+      Use 'f-list' to see a list of supported file formats.
+      Use 'vcodec-list' to see a list of supported video decoders.
+      Use 'acodec-list' to see a list of supported audio decoders.
+    readonly: no
+    required: yes
+    mutable: no
+    widget: fileopen # could provide a button to use a file-open dialog 
+
+  - identifier: audio_index # the name is the mlt_properties name
+    title: Audio Index
+    type: integer
+    # the description can be used in a tool tip
+    description: Choose the index of audio stream to use (-1 is off)
+    readonly: no
+    mutable: no
+    minimum: -1
+    # when maximum not specified, the scalar limit is used
+    default: 0
+    widget: spinner
+
+  - identifier: video_index
+    title: Video Index
+    type: integer
+    description: Choose the index of video stream to use (-1 is off)
+    readonly: no
+    mutable: no
+    minimum: -1
+    default: 0
+    widget: spinner
+
+  - identifier: in
+    title: In Point
+    type: time # time is not implemented, but it will correspond to the mlt_position replacement
+    description: Set the start time offset to use within the clip
+    readonly: no
+    mutable: no
+    minimum: 0
+    default: 0
+    widget: timecode # this is a special form of time value/code entry (e.g. see Kino)
+
+  - identifier: out
+    title: Out Point
+    type: time
+    description: Set the ending time offset to use within the clip
+    readonly: no
+    minimum: 0
+    mutable: no
+    widget: timecode # as opposed to time, which could be confused for a wallclock-style time widget
+
+  - identifier: threads
+    title: Decoding Threads
+    type: integer
+    description: Choose the number of threads to use in the decoder(s)
+    readonly: no
+    mutable: no
+    minimum: 0
+    maximum: 4
+    default: 1
+    widget: spinner
+    unit: threads # the unit is a label that appears after the widget
+
+  - identifier: force_aspect_ratio
+    title: Sample Aspect Ratio
+    type: float
+    description: Optionally override a (mis)detected aspect ratio
+    readonly: no
+    mutable: yes
+    minimum: 0.001 # just a UI suggestion
+    maximum: 9.999 # just a suggestion
+    # no default property means it should be blank in the UI and not applied unless provided
+
+  - identifier: resource
+    title: File
+    type: string
+    description: file or protocol specification
+    readonly: yes
+
+  - identifier: source_fps
+    title: Frame Rate
+    type: float
+    scale: 2 # scale is the number of digits to display after the decimal point
+    description: the framerate of the resource
+    readonly: yes
+    unit: frames/second
+
+  - identifier: aspect_ratio
+    title: Sample Aspect Ratio
+    type: float
+    description: >
+      The sample aspect ratio of the resource.
+      This is determined on every frame read.
+    readonly: yes
+
+  - identifier: length
+    title: Duration
+    type: time
+    description: duration
+    readonly: yes
+    widget: timecode
+
+  - identifier: seekable
+    title: Supports Seek
+    type: integer
+    description: if the resource can seek
+    readonly: yes
diff --git a/src/modules/configure b/src/modules/configure
new file mode 100755 (executable)
index 0000000..e80a585
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Clean up disables if not in help mode
+[ "$help" != "1" ] && rm -f disable-* producers.dat filters.dat transitions.dat consumers.dat
+
+# Create the make.inc file
+echo SUBDIRS = `find . -maxdepth 1 -type d | grep -v .svn | grep -v "^.$" | sed 's/\.\///'` > make.inc
+
+# Iterate through arguments
+for i in "$@"
+do
+       case $i in
+               --disable-* )   touch disable-${i#--disable-} ;;
+       esac
+done
+
+# Iterate through each of the components
+for i in *
+do
+       if [ -d $i -a \( "$help" = "1" -o ! -f disable-$i \) ]
+       then
+               if [ "$gpl" = "true" -o ! -f $i/gpl ]
+               then
+                       [ -f $i/Makefile -a "$help" = "0" ] && echo "Configuring modules/$i:"
+                       if [ -x $i/configure ]
+                       then
+                               olddir2=`pwd`
+                               cd $i
+                               ./configure "$@"
+                               [ $? != 0 ] && exit 1
+                               cd $olddir2
+                       elif [ -f $i/configure ]
+                       then
+                               echo "  configure script is not set executable!"
+                       fi
+               elif [ "$help" = "0" ]
+               then
+                       touch disable-$i
+               fi
+       fi
+done
+
diff --git a/src/modules/core/Makefile b/src/modules/core/Makefile
new file mode 100644 (file)
index 0000000..a3132ae
--- /dev/null
@@ -0,0 +1,63 @@
+include ../../../config.mak
+
+TARGET = ../libmltcore$(LIBSUF)
+
+OBJS = factory.o \
+          producer_colour.o \
+          producer_consumer.o \
+          producer_noise.o \
+          producer_ppm.o \
+          filter_brightness.o \
+          filter_channelcopy.o \
+          filter_crop.o \
+          filter_data_feed.o \
+          filter_data_show.o \
+          filter_gamma.o \
+          filter_greyscale.o \
+          filter_luma.o \
+          filter_mirror.o \
+          filter_mono.o \
+          filter_obscure.o \
+          filter_region.o \
+          filter_rescale.o \
+          filter_resize.o \
+          filter_transition.o \
+          filter_watermark.o \
+          transition_composite.o \
+          transition_luma.o \
+          transition_mix.o \
+          transition_region.o \
+          consumer_null.o
+
+ASM_OBJS = 
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS) $(ASM_OBJS)
+               $(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
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(ASM_OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+       install -m 644 ../data_fx.properties "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/core/composite_line_yuv_mmx.S b/src/modules/core/composite_line_yuv_mmx.S
new file mode 100644 (file)
index 0000000..27af4dc
--- /dev/null
@@ -0,0 +1,211 @@
+       .file "composite_line_yuv_mmx"
+       .version "01.01"
+       
+gcc2_compiled.:
+.data
+
+.text
+       .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)
+.globl composite_line_yuv_mmx
+       .type    composite_line_yuv_mmx,@function
+composite_line_yuv_mmx:
+#else
+.globl _composite_line_yuv_mmx
+_composite_line_yuv_mmx:
+#endif
+
+/*
+ * Arguments
+ *             
+ * dest:            8(%ebp)            %esi
+ * src:         12(%ebp)
+ * width_src:   16(%ebp)       
+ * alpha:       20(%ebp)       
+ * weight:      24(%ebp)       
+ * luma:        28(%ebp)
+ * softness:    32(%ebp)
+ */
+
+/*
+ * Function call entry
+ */
+       pushl %ebp
+       movl %esp,%ebp
+       subl $28,%esp
+       pushl %edi
+       pushl %esi
+       pushl %ebx
+
+/* Initialise */
+       movl 8(%ebp), %esi            # get dest
+       movl $0, %edx                     # j = 0
+       
+.loop:
+
+       movl $0xffff, %ecx           # a = 255
+       cmpl $0, 20(%ebp)         # if alpha == NULL
+       je .noalpha
+       movl 20(%ebp), %edi       # a = alpha[ j ]
+       movb (%edi,%edx), %cl
+.noalpha:
+       movl %ecx, -24(%ebp)      # save ecx
+
+       movl 24(%ebp), %eax       # mix = weight
+       cmpl $0, 28(%ebp)         # if luma == NULL
+       je .noluma
+       movl 28(%ebp), %edi       # mix = ...
+       movl %edx, %ebx
+       sall $1, %ebx
+       movw (%edi,%ebx), %bx # luma[ j*2 ]
+       cmpl %ebx, %eax
+       jl .luma0
+       movl %ebx, %ecx
+       addl 32(%ebp), %ecx       # + softness
+       cmpl %ecx, %eax
+       jge .luma1
+       /* TODO: linear interpolate between edges */
+       subw %bx, %ax
+       sall $8, %eax
+       subw %bx, %cx
+       movl %edx, %ebx
+       divw %cx
+       movl %ebx, %edx
+       jmp .noluma
+.luma0:
+       movl $0, %eax
+       jmp .noluma
+.luma1:
+       movl $0xffff, %eax
+.noluma:
+       shrl $8, %eax
+
+       movl %edx, %ebx           # edx will be destroyed by mulw
+       movl -24(%ebp), %ecx      # restore ecx
+       mull %ecx                  # mix = mix * a...
+       movl %ebx, %edx           # restore edx
+       shrl $8, %eax             # >>8
+       andl $0xff, %eax
+       
+/* put alpha and (1-alpha) into mm0 */
+/* 0 aa 0 1-a 0 aa 0 1-a */
+
+       /* duplicate word */
+       movl %eax, %ecx
+       shll $16, %ecx
+       orl %eax, %ecx
+       
+       movd %ecx, %mm1
+       
+       /* (1 << 16) - mix */
+       movl $0x000000ff, %ecx
+       subl %eax, %ecx
+       andl $0xff, %ecx
+       
+       /* duplicate word */
+       movl %ecx, %eax
+       shll $16, %eax
+       orl %eax, %ecx
+       
+       movd %ecx, %mm0
+       
+       /* unpack words into double words */
+       punpcklwd %mm1, %mm0
+       
+/* put src yuv and dest yuv into mm1 */
+/* 0 UVs 0 UVd 0 Ys 0 Yd */
+
+       movl 12(%ebp), %edi       # get src
+       movb (%edi), %cl
+       shll $8, %ecx
+       movb 1(%edi), %al
+       shll $24, %eax
+       orl %eax, %ecx
+       
+       movb (%esi), %al         # get dest
+       orl %eax, %ecx
+       movb 1(%esi), %al
+       shll $16, %eax
+       orl %eax, %ecx
+       
+       movd %ecx, %mm1
+       punpcklbw %mm4, %mm1
+       
+/* alpha composite */
+       pmaddwd %mm1, %mm0
+       psrld $8, %mm0
+
+/* store result */
+       movd %mm0, %eax
+       movb %al, (%esi)
+       pextrw $2, %mm0, %eax
+               movl $128, %eax
+       movb %al, 1(%esi)
+
+/* for..next */
+       addl $1, %edx             # j++
+       cmpl 16(%ebp), %edx       # if ( j == width_src )
+       jge .out
+       
+       addl $2, %esi
+       addl $2, 12(%ebp)
+       
+       jmp .loop
+
+.out:
+       emms
+       leal -40(%ebp),%esp
+       popl %ebx
+       popl %esi
+       popl %edi
+       movl %ebp,%esp
+       popl %ebp
+       ret
+
+
+/********************************************/
+
+.align 8
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)      
+.globl composite_have_mmx
+       .type    composite_have_mmx,@function
+composite_have_mmx:
+#else
+.globl _composite_have_mmx
+_composite_have_mmx:
+#endif
+       
+       push    %ebx
+
+# Check if bit 21 in flags word is writeable
+
+       pushfl  
+       popl    %eax
+       movl    %eax,%ebx
+       xorl    $0x00200000, %eax
+       pushl   %eax
+       popfl
+       pushfl
+       popl    %eax
+
+       cmpl    %eax, %ebx
+
+       je .notfound
+
+# OK, we have CPUID
+
+       movl    $1, %eax
+       cpuid
+       
+       test    $0x00800000, %edx
+       jz      .notfound
+
+       movl    $1, %eax
+       jmp     .out2
+
+.notfound:
+       movl    $0, %eax
+.out2: 
+       popl    %ebx
+       ret
diff --git a/src/modules/core/consumer_null.c b/src/modules/core/consumer_null.c
new file mode 100644 (file)
index 0000000..e96c8cd
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * consumer_null.c -- a null consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Allocate the consumer
+       mlt_consumer this = mlt_consumer_new( profile );
+
+       // If memory allocated and initialises without error
+       if ( this != NULL )
+       {
+               // Assign close callback
+               this->close = consumer_close;
+
+               // Set up start/stop/terminated callbacks
+               this->start = consumer_start;
+               this->stop = consumer_stop;
+               this->is_stopped = consumer_is_stopped;
+       }
+
+       // Return this
+       return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check that we're not already running
+       if ( !mlt_properties_get_int( properties, "running" ) )
+       {
+               // Allocate a thread
+               pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+               // Assign the thread to properties
+               mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+               // Set the running state
+               mlt_properties_set_int( properties, "running", 1 );
+               mlt_properties_set_int( properties, "joined", 0 );
+
+               // Create the thread
+               pthread_create( thread, NULL, consumer_thread, this );
+       }
+       return 0;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check that we're running
+       if ( !mlt_properties_get_int( properties, "joined" ) )
+       {
+               // Get the thread
+               pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+               // Stop the thread
+               mlt_properties_set_int( properties, "running", 0 );
+               mlt_properties_set_int( properties, "joined", 1 );
+
+               // Wait for termination
+               pthread_join( *thread, NULL );
+       }
+
+       return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+       return !mlt_properties_get_int( properties, "running" );
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+       // Map the argument to the object
+       mlt_consumer this = arg;
+
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Convenience functionality
+       int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
+       int terminated = 0;
+
+       // Frame and size
+       mlt_frame frame = NULL;
+
+       // Loop while running
+       while( !terminated && mlt_properties_get_int( properties, "running" ) )
+       {
+               // Get the frame
+               frame = mlt_consumer_rt_frame( this );
+
+               // Check for termination
+               if ( terminate_on_pause && frame != NULL )
+                       terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
+
+               // Check that we have a frame to work with
+               if ( frame != NULL )
+               {
+                       // Close the frame
+                       mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+                       mlt_frame_close( frame );
+               }
+       }
+
+       // Indicate that the consumer is stopped
+       mlt_properties_set_int( properties, "running", 0 );
+       mlt_consumer_stopped( this );
+
+       return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+       // Stop the consumer
+       mlt_consumer_stop( this );
+
+       // Close the parent
+       mlt_consumer_close( this );
+
+       // Free the memory
+       free( this );
+}
diff --git a/src/modules/core/factory.c b/src/modules/core/factory.c
new file mode 100644 (file)
index 0000000..755597f
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt.h>
+#include <string.h>
+
+extern mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_channelcopy_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_data_feed_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_data_show_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_gamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_greyscale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_obscure_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_rescale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_transition_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_colour_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#include "transition_composite.h"
+extern mlt_transition transition_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#include "transition_region.h"
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "null", consumer_null_init );
+       MLT_REGISTER( filter_type, "brightness", filter_brightness_init );
+       MLT_REGISTER( filter_type, "channelcopy", filter_channelcopy_init );
+       MLT_REGISTER( filter_type, "crop", filter_crop_init );
+       MLT_REGISTER( filter_type, "data_feed", filter_data_feed_init );
+       MLT_REGISTER( filter_type, "data_show", filter_data_show_init );
+       MLT_REGISTER( filter_type, "gamma", filter_gamma_init );
+       MLT_REGISTER( filter_type, "greyscale", filter_greyscale_init );
+       MLT_REGISTER( filter_type, "grayscale", filter_greyscale_init );
+       MLT_REGISTER( filter_type, "luma", filter_luma_init );
+       MLT_REGISTER( filter_type, "mirror", filter_mirror_init );
+       MLT_REGISTER( filter_type, "mono", filter_mono_init );
+       MLT_REGISTER( filter_type, "obscure", filter_obscure_init );
+       MLT_REGISTER( filter_type, "region", filter_region_init );
+       MLT_REGISTER( filter_type, "rescale", filter_rescale_init );
+       MLT_REGISTER( filter_type, "resize", filter_resize_init );
+       MLT_REGISTER( filter_type, "transition", filter_transition_init );
+       MLT_REGISTER( filter_type, "watermark", filter_watermark_init );
+       MLT_REGISTER( producer_type, "color", producer_colour_init );
+       MLT_REGISTER( producer_type, "colour", producer_colour_init );
+       MLT_REGISTER( producer_type, "consumer", producer_consumer_init );
+       MLT_REGISTER( producer_type, "noise", producer_noise_init );
+       MLT_REGISTER( producer_type, "ppm", producer_ppm_init );
+       MLT_REGISTER( transition_type, "composite", transition_composite_init );
+       MLT_REGISTER( transition_type, "luma", transition_luma_init );
+       MLT_REGISTER( transition_type, "mix", transition_mix_init );
+       MLT_REGISTER( transition_type, "region", transition_region_init );
+}
diff --git a/src/modules/core/filter_brightness.c b/src/modules/core/filter_brightness.c
new file mode 100644 (file)
index 0000000..07dce1c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * filter_brightness.c -- gamma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define CLAMP( x, min, max ) (x) < (min) ? (min) : (x) > (max) ? (max) : (x)
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // Get the brightness level
+               double level = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "brightness" );
+
+               // Only process if level is something other than 1
+               if ( level != 1.0 )
+               {
+                       int i = *width * *height + 1;
+                       uint8_t *p = *image;
+                       int32_t m = level * ( 1 << 16 );
+                       int32_t n = 128 * ( ( 1 << 16 ) - m );
+
+                       while ( --i )
+                       {
+                               p[0] = CLAMP( (p[0] * m) >> 16, 16, 235 );
+                               p[1] = CLAMP( (p[1] * m + n) >> 16, 16, 240 );
+                               p += 2;
+                       }
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the starting brightness level
+       double level = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "start" ) );
+
+       // If there is an end adjust gain to the range
+       if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+       {
+               // Determine the time position of this frame in the transition duration
+               mlt_position in = mlt_filter_get_in( this );
+               mlt_position out = mlt_filter_get_out( this );
+               mlt_position time = mlt_frame_get_position( frame );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "end" ) );
+               level += ( end - level ) * position;
+       }
+
+       // Push the frame filter
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "brightness", level );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_brightness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "1" : arg );
+       }
+       return this;
+}
+
diff --git a/src/modules/core/filter_channelcopy.c b/src/modules/core/filter_channelcopy.c
new file mode 100644 (file)
index 0000000..d4dd697
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * filter_channelcopy.c -- copy one audio channel to another
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#define __USE_ISOC99 1
+#include <math.h>
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the a frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+       int i, j;
+       int from = mlt_properties_get_int( properties, "channelcopy.from" );
+       int to = mlt_properties_get_int( properties, "channelcopy.to" );
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+       // Duplicate channels as necessary
+       {
+               int size = *channels * *samples * 2;
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               // Duplicate the existing channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * *channels ) + ( j == to ? from : j ) ];
+                       }
+               }
+               *buffer = new_buffer;
+       }
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+       mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+
+       // Propogate the parameters
+       mlt_properties_set_int( frame_props, "channelcopy.to", mlt_properties_get_int( properties, "to" ) );
+       mlt_properties_set_int( frame_props, "channelcopy.from", mlt_properties_get_int( properties, "from" ) );
+
+       // Override the get_audio method
+       mlt_frame_push_audio( frame, filter_get_audio );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_channelcopy_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               if ( arg != NULL )
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", atoi( arg ) );
+               else
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "to", 1 );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_crop.c b/src/modules/core/filter_crop.c
new file mode 100644 (file)
index 0000000..0857c1d
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * filter_crop.c -- cropping filter
+ * Copyright (C) 2009 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+static void crop( uint8_t *src, uint8_t *dest, int bpp, int width, int height, int left, int right, int top, int bottom )
+{
+       int stride = ( width - left - right ) * bpp + 1;
+       int y      = height - top - bottom + 1;
+       uint8_t *s = &src[ ( ( top * width ) + left ) * bpp ];
+
+       while ( --y )
+       {
+               int x = stride;
+               while ( --x )
+                       *dest ++ = *s ++;
+               s += ( right + left ) * bpp;
+       }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = 0;
+
+       // Get the properties from the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Correct Width/height if necessary
+       if ( *width == 0 || *height == 0 )
+       {
+               *width  = mlt_properties_get_int( properties, "normalised_width" );
+               *height = mlt_properties_get_int( properties, "normalised_height" );
+       }
+
+       // Now get the image
+       error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+       int left    = mlt_properties_get_int( properties, "crop.left" );
+       int right   = mlt_properties_get_int( properties, "crop.right" );
+       int top     = mlt_properties_get_int( properties, "crop.top" );
+       int bottom  = mlt_properties_get_int( properties, "crop.bottom" );
+       int owidth  = *width - left - right;
+       int oheight = *height - top - bottom;
+
+       // We only know how to process yuv422 at the moment
+       if ( ( owidth != *width || oheight != *height ) &&
+               error == 0 && *format == mlt_image_yuv422 && *image != NULL && owidth > 0 && oheight > 0 )
+       {
+               // Provides a manual override for misreported field order
+               if ( mlt_properties_get( properties, "meta.top_field_first" ) )
+               {
+                       mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( properties, "meta.top_field_first" ) );
+                       mlt_properties_set_int( properties, "meta.top_field_first", 0 );
+               }
+
+               if ( top % 2 )
+                       mlt_properties_set_int( properties, "top_field_first", !mlt_properties_get_int( properties, "top_field_first" ) );
+
+               left  -= left % 2;
+               owidth = *width - left - right;
+
+               // Create the output image
+               uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+               if ( output )
+               {
+                       // Call the generic resize
+                       crop( *image, output, 2, *width, *height, left, right, top, bottom );
+
+                       // Now update the frame
+                       *image = output;
+                       mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+                       mlt_properties_set_int( properties, "width", owidth );
+                       mlt_properties_set_int( properties, "height", oheight );
+               }
+
+               // We should resize the alpha too
+               uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+               if ( alpha != NULL )
+               {
+                       uint8_t *newalpha = mlt_pool_alloc( owidth * oheight );
+                       if ( newalpha )
+                       {
+                               crop( alpha, newalpha, 1, *width, *height, left, right, top, bottom );
+                               mlt_properties_set_data( properties, "alpha", newalpha, owidth * oheight, ( mlt_destructor )mlt_pool_release, NULL );
+                               this->get_alpha_mask = NULL;
+                       }
+               }
+               *width = owidth;
+               *height = oheight;
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "active" ) )
+       {
+               // Push the get_image method on to the stack
+               mlt_frame_push_get_image( frame, filter_get_image );
+       }
+       else
+       {
+               mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+               mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+               int left   = mlt_properties_get_int( filter_props, "left" );
+               int right  = mlt_properties_get_int( filter_props, "right" );
+               int top    = mlt_properties_get_int( filter_props, "top" );
+               int bottom = mlt_properties_get_int( filter_props, "bottom" );
+               int width  = mlt_properties_get_int( frame_props, "real_width" );
+               int height = mlt_properties_get_int( frame_props, "real_height" );
+
+               mlt_properties_set_int( frame_props, "crop.left", left );
+               mlt_properties_set_int( frame_props, "crop.right", right );
+               mlt_properties_set_int( frame_props, "crop.top", top );
+               mlt_properties_set_int( frame_props, "crop.bottom", bottom );
+               mlt_properties_set_int( frame_props, "real_width", width - left - right );
+               mlt_properties_set_int( frame_props, "real_height", height - top - bottom );
+       }
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+       if ( mlt_filter_init( this, this ) == 0 )
+       {
+               this->process = filter_process;
+               if ( arg )
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "active", atoi( arg ) );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_data_feed.c b/src/modules/core/filter_data_feed.c
new file mode 100644 (file)
index 0000000..e3cf81d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * filter_data_feed.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <framework/mlt.h>
+
+/** This filter should be used in conjuction with the data_show filter.
+       The concept of the data_feed is that it can be used to pass titles
+       or images to render on the frame, but doesn't actually do it 
+       itself. data_feed imposes few rules on what's passed on and the 
+       validity is confirmed in data_show before use.
+*/
+
+/** Data queue destructor.
+*/
+
+static void destroy_data_queue( void *arg )
+{
+       if ( arg != NULL )
+       {
+               // Assign the correct type
+               mlt_deque queue = arg;
+
+               // Iterate through each item and destroy them
+               while ( mlt_deque_peek_front( queue ) != NULL )
+                       mlt_properties_close( mlt_deque_pop_back( queue ) );
+
+               // Close the deque
+               mlt_deque_close( queue );
+       }
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( this );
+
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get the data queue
+       mlt_deque data_queue = mlt_properties_get_data( frame_properties, "data_queue", NULL );
+
+       // Get the type of the data feed
+       char *type = mlt_properties_get( filter_properties, "type" );
+
+       // Get the in and out points of this filter
+       int in = mlt_filter_get_in( this );
+       int out = mlt_filter_get_out( this );
+
+       // Create the data queue if it doesn't exist
+       if ( data_queue == NULL )
+       {
+               // Create the queue
+               data_queue = mlt_deque_init( );
+
+               // Assign it to the frame with the destructor
+               mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, destroy_data_queue, NULL );
+       }
+
+       // Now create the data feed
+       if ( data_queue != NULL && type != NULL && !strcmp( type, "attr_check" ) )
+       {
+               int i = 0;
+               int count = mlt_properties_count( frame_properties );
+
+               for ( i = 0; i < count; i ++ )
+               {
+                       char *name = mlt_properties_get_name( frame_properties, i );
+
+                       // Only deal with meta.attr.name values here - these should have a value of 1 to be considered
+                       // Additional properties of the form are meta.attr.name.property are passed down on the feed
+                       if ( !strncmp( name, "meta.attr.", 10 ) && strchr( name + 10, '.' ) == NULL && mlt_properties_get_int( frame_properties, name ) == 1 )
+                       {
+                               // Temp var to hold name + '.' for pass method
+                               char temp[ 132 ];
+
+                               // Create a new data feed
+                               mlt_properties feed = mlt_properties_new( );
+
+                               // Assign it the base properties
+                               mlt_properties_set( feed, "id", mlt_properties_get( filter_properties, "_unique_id" ) );
+                               mlt_properties_set( feed, "type", strrchr( name, '.' ) + 1 );
+                               mlt_properties_set_position( feed, "position", mlt_frame_get_position( frame ) );
+
+                               // Assign in/out of service we're connected to
+                               mlt_properties_set_position( feed, "in", mlt_properties_get_position( frame_properties, "in" ) );
+                               mlt_properties_set_position( feed, "out", mlt_properties_get_position( frame_properties, "out" ) );
+
+                               // Pass all meta properties 
+                               sprintf( temp, "%s.", name );
+                               mlt_properties_pass( feed, frame_properties, temp );
+
+                               // Push it on to the queue
+                               mlt_deque_push_back( data_queue, feed );
+
+                               // Make sure this attribute only gets processed once
+                               mlt_properties_set_int( frame_properties, name, 0 );
+                       }
+               }
+       }
+       else if ( data_queue != NULL )
+       {
+               // Create a new data feed
+               mlt_properties feed = mlt_properties_new( );
+
+               // Assign it the base properties
+               mlt_properties_set( feed, "id", mlt_properties_get( filter_properties, "_unique_id" ) );
+               mlt_properties_set( feed, "type", type );
+               mlt_properties_set_position( feed, "position", mlt_frame_get_position( frame ) );
+
+               // Assign in/out of service we're connected to
+               mlt_properties_set_position( feed, "in", mlt_properties_get_position( frame_properties, "in" ) );
+               mlt_properties_set_position( feed, "out", mlt_properties_get_position( frame_properties, "out" ) );
+
+               // Correct in/out to the filter if specified
+               if ( in != 0 )
+                       mlt_properties_set_position( feed, "in", in );
+               if ( out != 0 )
+                       mlt_properties_set_position( feed, "out", out );
+
+               // Pass the properties which start with a "feed." prefix 
+               // Note that 'feed.text' in the filter properties becomes 'text' on the feed
+               mlt_properties_pass( feed, filter_properties, "feed." );
+
+               // Push it on to the queue
+               mlt_deque_push_back( data_queue, feed );
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_feed_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the filter
+       mlt_filter this = mlt_filter_new( );
+
+       // Initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Assign the argument (default to titles)
+               mlt_properties_set( properties, "type", arg == NULL ? "titles" : arg );
+
+               // Specify the processing method
+               this->process = filter_process;
+       }
+
+       return this;
+}
+
diff --git a/src/modules/core/filter_data_show.c b/src/modules/core/filter_data_show.c
new file mode 100644 (file)
index 0000000..d62bd58
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * filter_data_show.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle the profile.
+*/
+
+static mlt_filter obtain_filter( mlt_filter filter, char *type )
+{
+       // Result to return
+       mlt_filter result = NULL;
+
+       // Miscelaneous variable
+       int i = 0;
+       int type_len = strlen( type );
+
+       // Get the properties of the data show filter
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the profile properties
+       mlt_properties profile_properties = mlt_properties_get_data( filter_properties, "profile_properties", NULL );
+
+       // Obtain the profile_properties if we haven't already
+       if ( profile_properties == NULL )
+       {
+               char temp[ 512 ];
+
+               // Get the profile requested
+               char *profile = mlt_properties_get( filter_properties, "resource" );
+
+               // If none is specified, pick up the default for this normalisation
+               if ( profile == NULL )
+                       sprintf( temp, "%s/feeds/%s/data_fx.properties", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ) );
+               else if ( strchr( profile, '%' ) )
+                       sprintf( temp, "%s/feeds/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( profile, '%' ) + 1 );
+               else
+                       strcpy( temp, profile );
+
+               // Load the specified profile or use the default
+               profile_properties = mlt_properties_load( temp );
+
+               // Store for later retrieval
+               mlt_properties_set_data( filter_properties, "profile_properties", profile_properties, 0, ( mlt_destructor )mlt_properties_close, NULL );
+       }
+
+       if ( profile_properties != NULL )
+       {
+               for ( i = 0; i < mlt_properties_count( profile_properties ); i ++ )
+               {
+                       char *name = mlt_properties_get_name( profile_properties, i );
+                       char *value = mlt_properties_get_value( profile_properties, i );
+       
+                       if ( result == NULL && !strcmp( name, type ) && result == NULL )
+                               result = mlt_factory_filter( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ), value, NULL );
+                       else if ( result != NULL && !strncmp( name, type, type_len ) && name[ type_len ] == '.' )
+                               mlt_properties_set( MLT_FILTER_PROPERTIES( result ), name + type_len + 1, value );
+                       else if ( result != NULL )
+                               break;
+               }
+       }
+
+       return result;
+}
+
+/** Retrieve medatata value 
+*/
+
+char* metadata_value(mlt_properties properties, char* name)
+{
+       if (name == NULL) return NULL;
+       char *meta = malloc( strlen(name) + 18 );
+       sprintf( meta, "meta.attr.%s.markup", name);
+       char *result = mlt_properties_get( properties, meta);
+       free(meta);
+       return result;
+}
+
+/** Convert frames to Timecode 
+*/
+
+char* frame_to_timecode( int frames , int fps)
+{
+       if (fps == 0) return strdup("-");
+       char *res = malloc(12);
+       int seconds = frames / (int) fps;
+       frames = frames % ((int) fps);
+       int minutes = seconds / 60;
+       seconds = seconds % 60;
+       int hours = minutes / 60;
+       minutes = minutes % 60;
+       sprintf(res, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames);
+       return res;
+}
+
+/** Process the frame for the requested type
+*/
+
+static int process_feed( mlt_properties feed, mlt_filter filter, mlt_frame frame )
+{
+       // Error return
+       int error = 1;
+
+       // Get the properties of the data show filter
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the type requested by the feeding filter
+       char *type = mlt_properties_get( feed, "type" );
+
+       // Fetch the filter associated to this type
+       mlt_filter requested = mlt_properties_get_data( filter_properties, type, NULL );
+
+       // If it doesn't exist, then create it now
+       if ( requested == NULL )
+       {
+               // Source filter from profile
+               requested = obtain_filter( filter, type );
+
+               // Store it on the properties for subsequent retrieval/destruction
+               mlt_properties_set_data( filter_properties, type, requested, 0, ( mlt_destructor )mlt_filter_close, NULL );
+       }
+
+       // If we have one, then process it now...
+       if ( requested != NULL )
+       {
+               int i = 0;
+               mlt_properties properties = MLT_FILTER_PROPERTIES( requested );
+               static const char *prefix = "properties.";
+               int len = strlen( prefix );
+
+               // Determine if this is an absolute or relative feed
+               int absolute = mlt_properties_get_int( feed, "absolute" );
+
+               // Make do with what we have
+               int length = !absolute ? 
+                                        mlt_properties_get_int( feed, "out" ) - mlt_properties_get_int( feed, "in" ) + 1 :
+                                        mlt_properties_get_int( feed, "out" ) + 1;
+
+               // Repeat period
+               int period = mlt_properties_get_int( properties, "period" );
+               period = period == 0 ? 1 : period;
+
+               // Pass properties from feed into requested
+               for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+               {
+                       char *name = mlt_properties_get_name( properties, i );
+                       char *key = mlt_properties_get_value( properties, i );
+                       if ( !strncmp( name, prefix, len ) )
+                       {
+                               if ( !strncmp( name + len, "length[", 7 ) )
+                               {
+                                       mlt_properties_set_position( properties, key, ( length - period ) / period );
+                               }
+                               else
+                               {
+                                       char *value = mlt_properties_get( feed, name + len );
+                                       if ( value != NULL )
+                                       {
+                                               // check for metadata keywords in metadata markup if user requested so
+                                               if ( mlt_properties_get_int( filter_properties, "dynamic" ) == 1  && !strcmp( name + strlen( name ) - 6, "markup") )
+                                               {
+                                                       // Find keywords which should be surrounded by '#', like: #title#
+                                                       char* keywords = strtok( value, "#" );
+                                                       char result[512] = ""; // XXX: how much is enough?
+                                                       int ct = 0;
+                                                       int fromStart = ( value[0] == '#' ) ? 1 : 0;
+                                                       
+                                                       while ( keywords != NULL )
+                                                       {
+                                                               if ( ct % 2 == fromStart )
+                                                               {
+                                                                       // backslash in front of # suppresses substitution
+                                                                       if ( keywords[ strlen( keywords ) -1 ] == '\\' )
+                                                                       {
+                                                                               // keep characters except backslash
+                                                                               strncat( result, keywords, strlen( keywords ) -1 );
+                                                                               strcat( result, "#" );
+                                                                               ct++;
+                                                                       }
+                                                                       else
+                                                                       {
+                                                                               strcat( result, keywords );
+                                                                       }
+                                                               }
+                                                               else if ( !strcmp( keywords, "timecode" ) )
+                                                               {
+                                                                       // special case: replace #timecode# with current frame timecode
+                                                                       int pos = mlt_properties_get_int( feed, "position" );
+                                                                       char *tc = frame_to_timecode( pos, mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ) );
+                                                                       strcat( result, tc );
+                                                                       free( tc );
+                                                               }
+                                                               else
+                                                               {
+                                                                       // replace keyword with metadata value
+                                                                       char *metavalue = metadata_value( MLT_FRAME_PROPERTIES( frame ), keywords );
+                                                                       strcat( result, metavalue ? metavalue : "-" );
+                                                               }
+                                                               keywords = strtok( NULL, "#" );
+                                                               ct++;
+                                                       }
+                                                       mlt_properties_set( properties, key, (char*) result );
+                                               }
+                                               else mlt_properties_set( properties, key, value );
+                                       }
+                               }
+                       }
+               }
+
+               // Set the original position on the frame
+               if ( absolute == 0 )
+                       mlt_frame_set_position( frame, mlt_properties_get_int( feed, "position" ) - mlt_properties_get_int( feed, "in" ) );
+               else
+                       mlt_frame_set_position( frame, mlt_properties_get_int( feed, "position" ) );
+
+               // Process the filter
+               mlt_filter_process( requested, frame );
+
+               // Should be ok...
+               error = 0;
+       }
+
+       return error;
+}
+
+void process_queue( mlt_deque data_queue, mlt_frame frame, mlt_filter filter )
+{
+       if ( data_queue != NULL )
+       {
+               // Create a new queue for those that we can't handle
+               mlt_deque temp_queue = mlt_deque_init( );
+
+               // Iterate through each entry on the queue
+               while ( mlt_deque_peek_front( data_queue ) != NULL )
+               {
+                       // Get the data feed
+                       mlt_properties feed = mlt_deque_pop_front( data_queue );
+
+                       if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ) != NULL )
+                               mlt_properties_debug( feed, mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ), stderr );
+
+                       // Process the data feed...
+                       if ( process_feed( feed, filter, frame ) == 0 )
+                               mlt_properties_close( feed );
+                       else
+                               mlt_deque_push_back( temp_queue, feed );
+               }
+       
+               // Now put the unprocessed feeds back on the stack
+               while ( mlt_deque_peek_front( temp_queue ) )
+               {
+                       // Get the data feed
+                       mlt_properties feed = mlt_deque_pop_front( temp_queue );
+       
+                       // Put it back on the data queue
+                       mlt_deque_push_back( data_queue, feed );
+               }
+       
+               // Close the temporary queue
+               mlt_deque_close( temp_queue );
+       }
+}
+
+/** Get the image.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Pop the service
+       mlt_filter filter = mlt_frame_pop_service( frame );
+
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Track specific
+       process_queue( mlt_properties_get_data( frame_properties, "data_queue", NULL ), frame, filter );
+
+       // Global
+       process_queue( mlt_properties_get_data( frame_properties, "global_queue", NULL ), frame, filter );
+
+       // Need to get the image
+       return mlt_frame_get_image( frame, image, format, width, height, 1 );
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the filter
+       mlt_frame_push_service( frame, this );
+
+       // Register the get image method
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       // Return the frame
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_show_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
+{
+       // Create the filter
+       mlt_filter this = mlt_filter_new( );
+
+       // Initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Assign the argument (default to titles)
+               mlt_properties_set( properties, "resource", arg == NULL ? NULL : arg );
+
+               // Specify the processing method
+               this->process = filter_process;
+       }
+
+       return this;
+}
+
diff --git a/src/modules/core/filter_gamma.c b/src/modules/core/filter_gamma.c
new file mode 100644 (file)
index 0000000..e4fe15b
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * filter_gamma.c -- gamma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // Get the gamma value
+               double gamma = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "gamma" );
+
+               if ( gamma != 1.0 )
+               {
+                       uint8_t *p = *image;
+                       uint8_t *q = *image + *width * *height * 2;
+
+                       // Calculate the look up table
+                       double exp = 1 / gamma;
+                       uint8_t lookup[ 256 ];
+                       int i;
+
+                       for( i = 0; i < 256; i ++ )
+                               lookup[ i ] = ( uint8_t )( pow( ( double )i / 255.0, exp ) * 255 );
+
+                       while ( p != q )
+                       {
+                               *p = lookup[ *p ];
+                               p += 2;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       double gamma = mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "gamma" );
+       gamma = gamma <= 0 ? 1 : gamma;
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "gamma", gamma );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_gamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "gamma", arg == NULL ? "1" : arg );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_greyscale.c b/src/modules/core/filter_greyscale.c
new file mode 100644 (file)
index 0000000..fd618c0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * filter_greyscale.c -- greyscale filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               uint8_t *p = *image;
+               uint8_t *q = *image + *width * *height * 2;
+               while ( p ++ != q )
+                       *p ++ = 128;
+       }
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_greyscale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+               this->process = filter_process;
+       return this;
+}
+
diff --git a/src/modules/core/filter_luma.c b/src/modules/core/filter_luma.c
new file mode 100644 (file)
index 0000000..f05d6c4
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * filter_luma.c -- luma filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_transition.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = 0;
+       mlt_filter filter = mlt_frame_pop_service( this );
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       mlt_transition luma = mlt_properties_get_data( properties, "luma", NULL );
+       mlt_frame b_frame = mlt_properties_get_data( properties, "frame", NULL );
+       mlt_properties b_frame_props = b_frame ? MLT_FRAME_PROPERTIES( b_frame ) : NULL;
+       int out = mlt_properties_get_int( properties, "period" );
+       
+       if ( out == 0 )
+               out = 24;
+
+       if ( b_frame == NULL || mlt_properties_get_int( b_frame_props, "width" ) != *width || mlt_properties_get_int( b_frame_props, "height" ) != *height )
+       {
+               b_frame = mlt_frame_init( MLT_FILTER_SERVICE( filter ) );
+               mlt_properties_set_data( properties, "frame", b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+       }
+
+       if ( luma == NULL )
+       {
+               char *resource = mlt_properties_get( properties, "resource" );
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+               luma = mlt_factory_transition( profile, "luma", resource );
+               if ( luma != NULL )
+               {
+                       mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma );
+                       mlt_properties_set_int( luma_properties, "in", 0 );
+                       mlt_properties_set_int( luma_properties, "out", out );
+                       mlt_properties_set_int( luma_properties, "reverse", 1 );
+                       mlt_properties_set_data( properties, "luma", luma, 0, ( mlt_destructor )mlt_transition_close, NULL );
+               }
+
+               // Prime the filter with the first image to prevent a transition from the white
+               // of a test card.
+               error = mlt_frame_get_image( this, image, format, width, height, 1 );
+               if ( error == 0 )
+               {
+                       mlt_properties a_props = MLT_FRAME_PROPERTIES( this );
+                       int size = 0;
+                       uint8_t *src = mlt_properties_get_data( a_props, "image", &size );
+                       uint8_t *dst = mlt_pool_alloc( size );
+       
+                       if ( dst != NULL )
+                       {
+                               mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+                               memcpy( dst, src, size );
+                               mlt_properties_set_data( b_props, "image", dst, size, mlt_pool_release, NULL );
+                               mlt_properties_set_int( b_props, "width", *width );
+                               mlt_properties_set_int( b_props, "height", *height );
+                               mlt_properties_set_int( b_props, "format", *format );
+                       }
+               }
+       }
+
+       if ( luma != NULL && 
+               ( mlt_properties_get( properties, "blur" ) != NULL || 
+                 (int)mlt_frame_get_position( this ) % ( out + 1 ) != out ) )
+       {
+               mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma );
+               mlt_properties_pass( luma_properties, properties, "luma." );
+               mlt_transition_process( luma, this, b_frame );
+       }
+
+       error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if ( error == 0 )
+       {
+               mlt_properties a_props = MLT_FRAME_PROPERTIES( this );
+               int size = 0;
+               uint8_t *src = mlt_properties_get_data( a_props, "image", &size );
+               uint8_t *dst = mlt_pool_alloc( size );
+
+               if ( dst != NULL )
+               {
+                       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+                       memcpy( dst, src, size );
+                       mlt_properties_set_data( b_props, "image", dst, size, mlt_pool_release, NULL );
+                       mlt_properties_set_int( b_props, "width", *width );
+                       mlt_properties_set_int( b_props, "height", *height );
+                       mlt_properties_set_int( b_props, "format", *format );
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the filter on to the stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the get_image on to the stack
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               this->process = filter_process;
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "resource", arg );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_mirror.c b/src/modules/core/filter_mirror.c
new file mode 100644 (file)
index 0000000..3dd6dbf
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * filter_mirror.c -- mirror filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Pop the mirror filter from the stack
+       mlt_filter this = mlt_frame_pop_service( frame );
+
+       // Get the mirror type
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+       // Get the properties
+       char *mirror = mlt_properties_get( properties, "mirror" );
+
+       // Determine if reverse is required
+       int reverse = mlt_properties_get_int( properties, "reverse" );
+
+       // Get the image
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       // Get the alpha
+       uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+
+       // If we have an image of the right colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // We'll KISS here
+               int hh = *height / 2;
+
+               if ( !strcmp( mirror, "horizontal" ) )
+               {
+                       uint8_t *p = NULL;
+                       uint8_t *q = NULL;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       int i;
+                       int uneven_w = ( *width % 2 ) * 2;
+                       for ( i = 0; i < *height; i ++ )
+                       {
+                               p = ( uint8_t * )*image + i * *width * 2;
+                               q = p + *width * 2;
+                               a = alpha + i * *width;
+                               b = a + *width - 1;
+                               if ( !reverse )
+                               {
+                                       while ( p < q )
+                                       {
+                                               *p ++ = *( q - 2 );
+                                               *p ++ = *( q - 3 - uneven_w );
+                                               *p ++ = *( q - 4 );
+                                               *p ++ = *( q - 1 - uneven_w );
+                                               q -= 4;
+                                               *a ++ = *b --;
+                                               *a ++ = *b --;
+                                       }
+                               }
+                               else
+                               {
+                                       while ( p < q )
+                                       {
+                                               *( q - 2 ) = *p ++;
+                                               *( q - 3 - uneven_w ) = *p ++;
+                                               *( q - 4 ) = *p ++;
+                                               *( q - 1 - uneven_w ) = *p ++;
+                                               q -= 4;
+                                               *b -- = *a ++;
+                                               *b -- = *a ++;
+                                       }
+                               }
+                       }
+               }
+               else if ( !strcmp( mirror, "vertical" ) )
+               {
+                       uint16_t *end = ( uint16_t *)*image + *width * *height;
+                       uint16_t *p = NULL;
+                       uint16_t *q = NULL;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       int i;
+                       int j;
+                       for ( i = 0; i < hh; i ++ )
+                       {
+                               p = ( uint16_t * )*image + i * *width;
+                               q = end - ( i + 1 ) * *width;
+                               j = *width;
+                               a = alpha + i * *width;
+                               b = alpha + ( *height - i - 1 ) * *width;
+                               if ( !reverse )
+                               {
+                                       while ( j -- )
+                                       {
+                                               *p ++ = *q ++;
+                                               *a ++ = *b ++;
+                                       }
+                               }
+                               else
+                               {
+                                       while ( j -- )
+                                       {
+                                               *q ++ = *p ++;
+                                               *b ++ = *a ++;
+                                       }
+                               }
+                       }
+               }
+               else if ( !strcmp( mirror, "diagonal" ) )
+               {
+                       uint8_t *end = ( uint8_t *)*image + *width * *height * 2;
+                       uint8_t *p = NULL;
+                       uint8_t *q = NULL;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       int i;
+                       int j;
+                       int uneven_w = ( *width % 2 ) * 2;
+                       for ( i = 0; i < *height; i ++ )
+                       {
+                               p = ( uint8_t * )*image + i * *width * 2;
+                               q = end - i * *width * 2;
+                               j = ( ( *width * ( *height - i ) ) / *height ) / 2;
+                               a = alpha + i * *width;
+                               b = alpha + ( *height - i - 1 ) * *width;
+                               if ( !reverse )
+                               {
+                                       while ( j -- )
+                                       {
+                                               *p ++ = *( q - 2 );
+                                               *p ++ = *( q - 3 - uneven_w );
+                                               *p ++ = *( q - 4 );
+                                               *p ++ = *( q - 1 - uneven_w );
+                                               q -= 4;
+                                               *a ++ = *b --;
+                                               *a ++ = *b --;
+                                       }
+                               }
+                               else
+                               {
+                                       while ( j -- )
+                                       {
+                                               *( q - 2 ) = *p ++;
+                                               *( q - 3 - uneven_w ) = *p ++;
+                                               *( q - 4 ) = *p ++;
+                                               *( q - 1 - uneven_w ) = *p ++;
+                                               q -= 4;
+                                               *b -- = *a ++;
+                                               *b -- = *a ++;
+                                       }
+                               }
+                       }
+               }
+               else if ( !strcmp( mirror, "xdiagonal" ) )
+               {
+                       uint8_t *end = ( uint8_t *)*image + *width * *height * 2;
+                       uint8_t *p = NULL;
+                       uint8_t *q = NULL;
+                       int i;
+                       int j;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       int uneven_w = ( *width % 2 ) * 2;
+                       for ( i = 0; i < *height; i ++ )
+                       {
+                               p = ( uint8_t * )*image + ( i + 1 ) * *width * 2;
+                               q = end - ( i + 1 ) * *width * 2;
+                               j = ( ( *width * ( *height - i ) ) / *height ) / 2;
+                               a = alpha + ( i + 1 ) * *width - 1;
+                               b = alpha + ( *height - i - 1 ) * *width;
+                               if ( !reverse )
+                               {
+                                       while ( j -- )
+                                       {
+                                               *q ++ = *( p - 2 );
+                                               *q ++ = *( p - 3 - uneven_w );
+                                               *q ++ = *( p - 4 );
+                                               *q ++ = *( p - 1 - uneven_w );
+                                               p -= 4;
+                                               *b ++ = *a --;
+                                               *b ++ = *a --;
+                                       }
+                               }
+                               else
+                               {
+                                       while ( j -- )
+                                       {
+                                               *( p - 2 ) = *q ++;
+                                               *( p - 3 - uneven_w ) = *q ++;
+                                               *( p - 4 ) = *q ++;
+                                               *( p - 1 - uneven_w ) = *q ++;
+                                               p -= 4;
+                                               *a -- = *b ++;
+                                               *a -- = *b ++;
+                                       }
+                               }
+                       }
+               }
+               else if ( !strcmp( mirror, "flip" ) )
+               {
+                       uint8_t t[ 4 ];
+                       uint8_t *p = NULL;
+                       uint8_t *q = NULL;
+                       int i;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       uint8_t c;
+                       int uneven_w = ( *width % 2 ) * 2;
+                       for ( i = 0; i < *height; i ++ )
+                       {
+                               p = ( uint8_t * )*image + i * *width * 2;
+                               q = p + *width * 2;
+                               a = alpha + i * *width;
+                               b = a + *width - 1;
+                               while ( p < q )
+                               {
+                                       t[ 0 ] = p[ 0 ];
+                                       t[ 1 ] = p[ 1 + uneven_w ];
+                                       t[ 2 ] = p[ 2 ];
+                                       t[ 3 ] = p[ 3 + uneven_w ];
+                                       *p ++ = *( q - 2 );
+                                       *p ++ = *( q - 3 - uneven_w );
+                                       *p ++ = *( q - 4 );
+                                       *p ++ = *( q - 1 - uneven_w );
+                                       *( -- q ) = t[ 3 ];
+                                       *( -- q ) = t[ 0 ];
+                                       *( -- q ) = t[ 1 ];
+                                       *( -- q ) = t[ 2 ];
+                                       c = *a;
+                                       *a ++ = *b;
+                                       *b -- = c;
+                                       c = *a;
+                                       *a ++ = *b;
+                                       *b -- = c;
+                               }
+                       }
+               }
+               else if ( !strcmp( mirror, "flop" ) )
+               {
+                       uint16_t *end = ( uint16_t *)*image + *width * *height;
+                       uint16_t *p = NULL;
+                       uint16_t *q = NULL;
+                       uint16_t t;
+                       uint8_t *a = NULL;
+                       uint8_t *b = NULL;
+                       uint8_t c;
+                       int i;
+                       int j;
+                       for ( i = 0; i < hh; i ++ )
+                       {
+                               p = ( uint16_t * )*image + i * *width;
+                               q = end - ( i + 1 ) * *width;
+                               a = alpha + i * *width;
+                               b = alpha + ( *height - i - 1 ) * *width;
+                               j = *width;
+                               while ( j -- )
+                               {
+                                       t = *p;
+                                       *p ++ = *q;
+                                       *q ++ = t;
+                                       c = *a;
+                                       *a ++ = *b;
+                                       *b ++ = c;
+                               }
+                       }
+               }
+       }
+
+       // Return the error
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the service on to the stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the filter method on to the stack
+       mlt_frame_push_service( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Construct a new filter
+       mlt_filter this = mlt_filter_new( );
+
+       // If we have a filter, initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Set the default mirror type
+               mlt_properties_set_or_default( properties, "mirror", arg, "horizontal" );
+
+               // Assign the process method
+               this->process = filter_process;
+       }
+
+       // Return the filter
+       return this;
+}
+
diff --git a/src/modules/core/filter_mono.c b/src/modules/core/filter_mono.c
new file mode 100644 (file)
index 0000000..882d6f3
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * filter_mono.c -- mix all channels to a mono signal across n channels
+ * Copyright (C) 2003-2006 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the a frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+       int channels_out = mlt_properties_get_int( properties, "mono.channels" );
+       int i, j, size;
+       int16_t *new_buffer;
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+       size = *samples * channels_out * sizeof( int16_t );
+       new_buffer = mlt_pool_alloc( size );
+       mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+       // Mix
+       for ( i = 0; i < *samples; i++ )
+       {
+               int16_t mixdown = 0;
+               for ( j = 0; j < *channels; j++ )
+                       mixdown += (*buffer)[ ( i * *channels ) + j ] / *channels;
+               for ( j = 0; j < channels_out; j++ )
+                       new_buffer[ ( i * channels_out ) + j ] = mixdown;
+       }
+
+       // Apply results
+       *buffer = new_buffer;
+       *channels = channels_out;
+       
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+       mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
+
+       // Propogate the parameters
+       mlt_properties_set_int( frame_props, "mono.channels", mlt_properties_get_int( properties, "channels" ) );
+
+       // Override the get_audio method
+       mlt_frame_push_audio( frame, filter_get_audio );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               if ( arg != NULL )
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", atoi( arg ) );
+               else
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_obscure.c b/src/modules/core/filter_obscure.c
new file mode 100644 (file)
index 0000000..7256f6c
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * filter_obscure.c -- obscure filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Geometry struct.
+*/
+
+struct geometry_s
+{
+       int nw;
+       int nh;
+       float x;
+       float y;
+       float w;
+       float h;
+       int mask_w;
+       int mask_h;
+};
+
+/** Parse a value from a geometry string.
+*/
+
+static inline float parse_value( char **ptr, int normalisation, char delim, float defaults )
+{
+       float value = defaults;
+
+       if ( *ptr != NULL && **ptr != '\0' )
+       {
+               char *end = NULL;
+               value = strtod( *ptr, &end );
+               if ( end != NULL )
+               {
+                       if ( *end == '%' )
+                               value = ( value / 100.0 ) * normalisation;
+                       while ( *end == delim || *end == '%' )
+                               end ++;
+               }
+               *ptr = end;
+       }
+
+       return value;
+}
+
+/** Parse a geometry property string.
+*/
+
+static void geometry_parse( struct geometry_s *geometry, struct geometry_s *defaults, char *property, int nw, int nh )
+{
+       // Assign normalised width and height
+       geometry->nw = nw;
+       geometry->nh = nh;
+
+       // Assign from defaults if available
+       if ( defaults != NULL )
+       {
+               geometry->x = defaults->x;
+               geometry->y = defaults->y;
+               geometry->w = defaults->w;
+               geometry->h = defaults->h;
+               geometry->mask_w = defaults->mask_w;
+               geometry->mask_h = defaults->mask_h;
+       }
+       else
+       {
+               geometry->x = 0;
+               geometry->y = 0;
+               geometry->w = nw;
+               geometry->h = nh;
+               geometry->mask_w = 20;
+               geometry->mask_h = 20;
+       }
+
+       // Parse the geomtry string
+       if ( property != NULL )
+       {
+               char *ptr = property;
+               geometry->x = parse_value( &ptr, nw, ',', geometry->x );
+               geometry->y = parse_value( &ptr, nh, ':', geometry->y );
+               geometry->w = parse_value( &ptr, nw, 'x', geometry->w );
+               geometry->h = parse_value( &ptr, nh, ':', geometry->h );
+               geometry->mask_w = parse_value( &ptr, nw, 'x', geometry->mask_w );
+               geometry->mask_h = parse_value( &ptr, nh, ' ', geometry->mask_h );
+       }
+}
+
+/** A Timism but not as clean ;-).
+*/
+
+static float lerp( float value, float lower, float upper )
+{
+       if ( value < lower )
+               return lower;
+       else if ( value > upper )
+               return upper;
+       return value;
+}
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, struct geometry_s *out, float position, int ow, int oh )
+{
+       // Calculate this frames geometry
+       output->x = lerp( ( in->x + ( out->x - in->x ) * position ) / ( float )out->nw * ow, 0, ow );
+       output->y = lerp( ( in->y + ( out->y - in->y ) * position ) / ( float )out->nh * oh, 0, oh );
+       output->w = lerp( ( in->w + ( out->w - in->w ) * position ) / ( float )out->nw * ow, 0, ow - output->x );
+       output->h = lerp( ( in->h + ( out->h - in->h ) * position ) / ( float )out->nh * oh, 0, oh - output->y );
+       output->mask_w = in->mask_w + ( out->mask_w - in->mask_w ) * position;
+       output->mask_h = in->mask_h + ( out->mask_h - in->mask_h ) * position;
+}
+
+/** Calculate the position for this frame.
+*/
+
+static float position_calculate( mlt_filter this, mlt_frame frame )
+{
+       // Get the in and out position
+       mlt_position in = mlt_filter_get_in( this );
+       mlt_position out = mlt_filter_get_out( this );
+
+       // Get the position of the frame
+       mlt_position position = mlt_frame_get_position( frame );
+
+       // Now do the calcs
+       return ( float )( position - in ) / ( float )( out - in + 1 );
+}
+
+/** The averaging function...
+*/
+
+static inline void obscure_average( uint8_t *start, int width, int height, int stride )
+{
+       register int y;
+       register int x;
+       register int Y = ( *start + *( start + 2 ) ) / 2;
+       register int U = *( start + 1 );
+       register int V = *( start + 3 );
+       register uint8_t *p;
+       register int components = width >> 1;
+
+       y = height;
+       while( y -- )
+       {
+               p = start;
+               x = components;
+               while( x -- )
+               {
+                       Y = ( Y + *p ++ ) >> 1;
+                       U = ( U + *p ++ ) >> 1;
+                       Y = ( Y + *p ++ ) >> 1;
+                       V = ( V + *p ++ ) >> 1;
+               }
+               start += stride;
+       }
+
+       start -= height * stride;
+       y = height;
+       while( y -- )
+       {
+               p = start;
+               x = components;
+               while( x -- )
+               {
+                       *p ++ = Y;
+                       *p ++ = U;
+                       *p ++ = Y;
+                       *p ++ = V;
+               }
+               start += stride;
+       }
+}
+
+
+/** The obscurer rendering function...
+*/
+
+static void obscure_render( uint8_t *image, int width, int height, struct geometry_s result )
+{
+       int area_x = result.x;
+       int area_y = result.y;
+       int area_w = result.w;
+       int area_h = result.h;
+
+       int mw = result.mask_w;
+       int mh = result.mask_h;
+       int w;
+       int h;
+       int aw;
+       int ah;
+
+       uint8_t *p = image + area_y * width * 2 + area_x * 2;
+
+       for ( w = 0; w < area_w; w += mw )
+       {
+               for ( h = 0; h < area_h; h += mh )
+               {
+                       aw = w + mw > area_w ? mw - ( w + mw - area_w ) : mw;
+                       ah = h + mh > area_h ? mh - ( h + mh - area_h ) : mh;
+                       if ( aw > 1 && ah > 1 )
+                               obscure_average( p + h * ( width << 1 ) + ( w << 1 ), aw, ah, width << 1 );
+               }
+       }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Pop the top of stack now
+       mlt_filter this = mlt_frame_pop_service( frame );
+
+       // Get the image from the frame
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       // Get the image from the frame
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               if ( this != NULL )
+               {
+                       // Get the filter properties
+                       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+                       // Obtain the normalised width and height from the frame
+                       int normalised_width = mlt_properties_get_int( frame_properties, "normalised_width" );
+                       int normalised_height = mlt_properties_get_int( frame_properties, "normalised_height" );
+
+                       // Structures for geometry
+                       struct geometry_s result;
+                       struct geometry_s start;
+                       struct geometry_s end;
+
+                       // Retrieve the position
+                       float position = mlt_properties_get_double(frame_properties, "filter_position");
+
+                       // Now parse the geometries
+                       geometry_parse( &start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
+                       geometry_parse( &end, &start, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
+
+                       // Do the calculation
+                       geometry_calculate( &result, &start, &end, position, *width, *height );
+
+                       // Now actually render it
+                       obscure_render( *image, *width, *height, result );
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push this on to the service stack
+       mlt_frame_push_service( frame, this );
+       
+       // Calculate the position for the filter effect
+       float position = position_calculate( this, frame );
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "filter_position", position );
+
+       // Push the get image call
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_obscure_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               this->process = filter_process;
+               mlt_properties_set( properties, "start", arg != NULL ? arg : "0%,0%:100%x100%" );
+               mlt_properties_set( properties, "end", "" );
+       }
+       return this;
+}
+
diff --git a/src/modules/core/filter_region.c b/src/modules/core/filter_region.c
new file mode 100644 (file)
index 0000000..8fd8e8d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * filter_region.c -- region filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "transition_region.h"
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the properties of the filter
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+       // Get the region transition
+       mlt_transition transition = mlt_properties_get_data( properties, "_transition", NULL );
+
+       // Create the transition if not available
+       if ( transition == NULL )
+       {
+               // Create the transition
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+               transition = mlt_factory_transition( profile, "region", NULL );
+
+               // Register with the filter
+               mlt_properties_set_data( properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+
+               // Pass a reference to this filter down
+               mlt_properties_set_data( MLT_TRANSITION_PROPERTIES( transition ), "_region_filter", this, 0, NULL, NULL );
+       }
+
+       // Pass all properties down
+       mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "" );
+
+       // Process the frame
+       return mlt_transition_process( transition, frame, NULL );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create a new filter
+       mlt_filter this = mlt_filter_new( );
+
+       // Further initialisation
+       if ( this != NULL )
+       {
+               // Get the properties from the filter
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Assign the filter process method
+               this->process = filter_process;
+
+               // Resource defines the shape of the region
+               mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg );
+
+               // Ensure that attached filters are handled privately
+               mlt_properties_set_int( properties, "_filter_private", 1 );
+       }
+
+       // Return the filter
+       return this;
+}
+
diff --git a/src/modules/core/filter_rescale.c b/src/modules/core/filter_rescale.c
new file mode 100644 (file)
index 0000000..25ca5fc
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * filter_rescale.c -- scale the producer video frame size to match the consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+typedef int ( *image_scaler )( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight );
+
+static void scale_alpha( mlt_frame this, int iwidth, int iheight, int owidth, int oheight );
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+       // Get the properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the rescaling interpolsation
+       char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+       // Carry out the rescaling
+       if ( iformat == mlt_image_yuv422 && oformat == mlt_image_yuv422 )
+       {
+               // Scale the frame
+               mlt_frame_rescale_yuv422( this, owidth, oheight );
+               
+               // Return the output
+               *image = mlt_properties_get_data( properties, "image", NULL );
+
+               // Scale the alpha channel only if exists and not correct size
+               int alpha_size = 0;
+               mlt_properties_get_data( properties, "alpha", &alpha_size );
+               if ( alpha_size > 0 && alpha_size != ( owidth * oheight ) )
+                       scale_alpha( this, iwidth, iheight, owidth, oheight );
+       }
+       else if ( iformat == mlt_image_rgb24 || iformat == mlt_image_rgb24a )
+       {
+               int bpp = (iformat == mlt_image_rgb24a ? 4 : 3 );
+                       
+               // Create the yuv image
+               uint8_t *output = mlt_pool_alloc( iwidth * ( iheight + 1 ) * 2 );
+
+               if ( strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) )
+               {
+                       // Extract YUV422 and alpha
+                       if ( bpp == 4 )
+                       {
+                               // Allocate the alpha mask
+                               uint8_t *alpha = mlt_pool_alloc( iwidth * ( iheight + 1 ) );
+
+                               // Convert the image and extract alpha
+                               mlt_convert_rgb24a_to_yuv422( *image, iwidth, iheight, iwidth * 4, output, alpha );
+
+                               mlt_properties_set_data( properties, "alpha", alpha, iwidth * ( iheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+
+                               scale_alpha( this, iwidth, iheight, owidth, oheight );
+                       }
+                       else
+                       {
+                               // No alpha to extract
+                               mlt_convert_rgb24_to_yuv422( *image, iwidth, iheight, iwidth * 3, output );
+                       }
+
+                       mlt_properties_set_data( properties, "image", output, iwidth * ( iheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+
+                       // Scale the frame
+                       output = mlt_frame_rescale_yuv422( this, owidth, oheight );
+
+               }
+               else
+               {
+                       // Extract YUV422 and alpha
+                       if ( bpp == 4 )
+                       {
+                               // Allocate the alpha mask
+                               uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+                               // Convert the image and extract alpha
+                               mlt_convert_rgb24a_to_yuv422( *image, owidth, oheight, owidth * 4, output, alpha );
+
+                               mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+
+                               scale_alpha( this, iwidth, iheight, owidth, oheight );
+                       }
+                       else
+                       {
+                               // No alpha to extract
+                               mlt_convert_rgb24_to_yuv422( *image, owidth, oheight, owidth * 3, output );
+                       }
+               }
+
+               // Now update the frame
+               mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", owidth );
+               mlt_properties_set_int( properties, "height", oheight );
+
+               *image = output;
+       }
+
+       return 0;
+}
+
+static void scale_alpha( mlt_frame this, int iwidth, int iheight, int owidth, int oheight )
+{
+       // Scale the alpha
+       uint8_t *output = NULL;
+       uint8_t *input = mlt_frame_get_alpha_mask( this );
+
+       if ( input != NULL )
+       {
+               uint8_t *out_line;
+               int x, y;
+               int ox = ( iwidth << 10 ) / owidth;
+               int oy = ( iheight << 10 ) / oheight;
+
+               output = mlt_pool_alloc( owidth * oheight );
+               out_line = output;
+
+               // Loop for the entirety of our output height.
+               for ( y = 0; y < oheight; y ++ )
+                       for ( x = 0; x < owidth; x ++ )
+                               *out_line ++ = *( input + ( ( 512 + ( y * oy * iwidth ) + x * ox ) >> 10 ) );
+
+               // Set it back on the frame
+               mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", output, owidth * oheight, mlt_pool_release, NULL );
+       }
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the frame properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the filter from the stack
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the image scaler method
+       image_scaler scaler_method = mlt_properties_get_data( filter_properties, "method", NULL );
+
+       // Correct Width/height if necessary
+       if ( *width == 0 || *height == 0 )
+       {
+               *width = mlt_properties_get_int( properties, "normalised_width" );
+               *height = mlt_properties_get_int( properties, "normalised_height" );
+       }
+
+       // There can be problems with small images - avoid them (by hacking - gah)
+       if ( *width >= 6 && *height >= 6 )
+       {
+               int iwidth = *width;
+               int iheight = *height;
+               int owidth = *width;
+               int oheight = *height;
+               char *interps = mlt_properties_get( properties, "rescale.interp" );
+               int wanted_format = *format;
+
+               // Default from the scaler if not specifed on the frame
+               if ( interps == NULL )
+               {
+                       interps = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "interpolation" );
+                       mlt_properties_set( properties, "rescale.interp", interps );
+               }
+       
+               // If real_width/height exist, we want that as minimum information
+               if ( mlt_properties_get_int( properties, "real_width" ) )
+               {
+                       iwidth = mlt_properties_get_int( properties, "real_width" );
+                       iheight = mlt_properties_get_int( properties, "real_height" );
+               }
+       
+               // Let the producer know what we are actually requested to obtain
+               if ( *format == mlt_image_yuv422 && strcmp( interps, "none" ) )
+               {
+                       mlt_properties_set_int( properties, "rescale_width", *width );
+                       mlt_properties_set_int( properties, "rescale_height", *height );
+               }
+               else
+               {
+                       // When no scaling is requested, revert the requested dimensions if possible
+                       mlt_properties_set_int( properties, "rescale_width", iwidth );
+                       mlt_properties_set_int( properties, "rescale_height", iheight );
+               }
+
+               // Deinterlace if height is changing to prevent fields mixing on interpolation
+               // One exception: non-interpolated, integral scaling
+               if ( iheight != oheight && ( strcmp( interps, "nearest" ) || ( iheight % oheight != 0 ) ) )
+                       mlt_properties_set_int( properties, "consumer_deinterlace", 1 );
+
+               // Get the image as requested
+               mlt_frame_get_image( this, image, format, &iwidth, &iheight, writable );
+
+               // Get rescale interpretation again, in case the producer wishes to override scaling
+               interps = mlt_properties_get( properties, "rescale.interp" );
+       
+               if ( *image != NULL && ( *format != mlt_image_yuv422 || ( iwidth != owidth || iheight != oheight ) ) )
+               {
+                       // If the colour space is correct and scaling is off, do nothing
+                       if ( *format == mlt_image_yuv422 && !strcmp( interps, "none" ) )
+                       {
+                               *width = iwidth;
+                               *height = iheight;
+                       }
+                       else if ( *format == mlt_image_yuv422 )
+                       {
+                               // Call the local scaler
+                               scaler_method( this, image, *format, mlt_image_yuv422, iwidth, iheight, owidth, oheight );
+                               *width = owidth;
+                               *height = oheight;
+                       }
+                       else if ( *format == mlt_image_rgb24 && wanted_format == mlt_image_rgb24 )
+                       {
+                               // Call the local scaler
+                               scaler_method( this, image, *format, mlt_image_rgb24, iwidth, iheight, owidth, oheight );
+
+                               // Return the output
+                               *width = owidth;
+                               *height = oheight;
+                       }
+                       else if ( *format == mlt_image_rgb24 || *format == mlt_image_rgb24a )
+                       {
+                               // Call the local scaler
+                               scaler_method( this, image, *format, mlt_image_yuv422, iwidth, iheight, owidth, oheight );
+
+                               // Return the output
+                               *format = mlt_image_yuv422;
+                               *width = owidth;
+                               *height = oheight;
+                       }
+                       else
+                       {
+                               *width = iwidth;
+                               *height = iheight;
+                       }
+               }
+               else
+               {
+                       *width = iwidth;
+                       *height = iheight;
+               }
+       }
+       else
+       {
+               // Store the requested width/height
+               int iwidth = *width;
+               int iheight = *height;
+
+               // Get the image as requested
+               mlt_frame_get_image( this, image, format, &iwidth, &iheight, writable );
+
+               // Too small - for now just assign as though we got what we wanted
+               *width = iwidth;
+               *height = iheight;
+       }
+
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the filter
+       mlt_frame_push_service( frame, this );
+
+       // Push the get image method
+       mlt_frame_push_service( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_rescale_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create a new scaler
+       mlt_filter this = mlt_filter_new( );
+
+       // If successful, then initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Set the process method
+               this->process = filter_process;
+
+               // Set the inerpolation
+               mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+               // Set the method
+               mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+       }
+
+       return this;
+}
+
diff --git a/src/modules/core/filter_resize.c b/src/modules/core/filter_resize.c
new file mode 100644 (file)
index 0000000..68244bf
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * filter_resize.c -- resizing filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Swapbytes inline.
+*/
+
+static inline void swap_bytes( uint8_t *upper, uint8_t *lower )
+{
+       uint8_t t = *lower;
+       *lower = *upper;
+       *upper = t;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = 0;
+
+       // Get the properties from the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Pop the top of stack now
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Retrieve the aspect ratio
+       double aspect_ratio = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( this ) );
+
+       // Correct Width/height if necessary
+       if ( *width == 0 || *height == 0 )
+       {
+               *width = mlt_properties_get_int( properties, "normalised_width" );
+               *height = mlt_properties_get_int( properties, "normalised_height" );
+       }
+
+       // Assign requested width/height from our subordinate
+       int owidth = *width;
+       int oheight = *height;
+
+       // Check for the special case - no aspect ratio means no problem :-)
+       if ( aspect_ratio == 0.0 )
+               aspect_ratio = mlt_properties_get_double( properties, "consumer_aspect_ratio" );
+
+       // Reset the aspect ratio
+       mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio );
+
+       // Hmmm...
+       char *rescale = mlt_properties_get( properties, "rescale.interp" );
+       if ( rescale != NULL && !strcmp( rescale, "none" ) )
+               return mlt_frame_get_image( this, image, format, width, height, writable );
+
+       if ( mlt_properties_get_int( properties, "distort" ) == 0 )
+       {
+               // Normalise the input and out display aspect
+               int normalised_width = mlt_properties_get_int( properties, "normalised_width" );
+               int normalised_height = mlt_properties_get_int( properties, "normalised_height" );
+               int real_width = mlt_properties_get_int( properties, "real_width" );
+               int real_height = mlt_properties_get_int( properties, "real_height" );
+               if ( real_width == 0 )
+                       real_width = mlt_properties_get_int( properties, "width" );
+               if ( real_height == 0 )
+                       real_height = mlt_properties_get_int( properties, "height" );
+               double input_ar = aspect_ratio * real_width / real_height;
+               double output_ar = mlt_properties_get_double( properties, "consumer_aspect_ratio" ) * owidth / oheight;
+               
+//             fprintf( stderr, "real %dx%d normalised %dx%d output %dx%d sar %f in-dar %f out-dar %f\n",
+//             real_width, real_height, normalised_width, normalised_height, owidth, oheight, aspect_ratio, input_ar, output_ar);
+
+               // Optimised for the input_ar > output_ar case (e.g. widescreen on standard)
+               int scaled_width = rint( ( input_ar * normalised_width ) / output_ar );
+               int scaled_height = normalised_height;
+
+               // Now ensure that our images fit in the output frame
+               if ( scaled_width > normalised_width )
+               {
+                       scaled_width = normalised_width;
+                       scaled_height = rint( ( output_ar * normalised_height ) / input_ar );
+               }
+
+               // Now calculate the actual image size that we want
+               owidth = rint( scaled_width * owidth / normalised_width );
+               oheight = rint( scaled_height * oheight / normalised_height );
+
+               // Tell frame we have conformed the aspect to the consumer
+               mlt_frame_set_aspect_ratio( this, mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
+       }
+
+       mlt_properties_set_int( properties, "distort", 0 );
+
+       // Now pass on the calculations down the line
+       mlt_properties_set_int( properties, "resize_width", *width );
+       mlt_properties_set_int( properties, "resize_height", *height );
+
+       // Now get the image
+       error = mlt_frame_get_image( this, image, format, &owidth, &oheight, writable );
+
+       // We only know how to process yuv422 at the moment
+       if ( error == 0 && *format == mlt_image_yuv422 && *image != NULL )
+       {
+               // Get the requested scale operation
+               char *op = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "scale" );
+
+               // Provides a manual override for misreported field order
+               if ( mlt_properties_get( properties, "meta.top_field_first" ) )
+                       mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( properties, "meta.top_field_first" ) );
+
+               // Correct field order if needed
+               if ( mlt_properties_get_int( properties, "top_field_first" ) == 1 )
+               {
+                       // Get the input image, width and height
+                       int size;
+                       uint8_t *image = mlt_properties_get_data( properties, "image", &size );
+                       uint8_t *ptr = image + owidth * 2;
+                       memmove( ptr, image, size - owidth * 2 );
+                       
+                       // Set the normalised field order
+                       mlt_properties_set_int( properties, "top_field_first", 0 );
+                       mlt_properties_set_int( properties, "meta.top_field_first", 0 );
+               }
+
+               if ( !strcmp( op, "affine" ) )
+               {
+                       *image = mlt_frame_rescale_yuv422( this, *width, *height );
+               }
+               else if ( strcmp( op, "none" ) != 0 )
+               {
+                       *image = mlt_frame_resize_yuv422( this, *width, *height );
+               }
+               else
+               {
+                       *width = owidth;
+                       *height = oheight;
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Store the aspect ratio reported by the source
+       mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( frame ), mlt_frame_get_aspect_ratio( frame ) );
+
+       // Push this on to the service stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the get_image method on to the stack
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+       if ( mlt_filter_init( this, this ) == 0 )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "scale", arg == NULL ? "off" : arg );
+       }
+       return this;
+}
diff --git a/src/modules/core/filter_transition.c b/src/modules/core/filter_transition.c
new file mode 100644 (file)
index 0000000..cd3f462
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * filter_transition.c -- Convert any transition into a filter
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_transition.h>
+
+/** Get the image via the transition.
+       NB: Not all transitions will accept a and b frames being the same...
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_transition transition = mlt_frame_pop_service( this );
+       if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "image_count" ) >= 1 )
+               mlt_transition_process( transition, this, this );
+       return mlt_frame_get_image( this, image, format, width, height, writable );
+}
+
+/** Get the audio via the transition.
+       NB: Not all transitions will accept a and b frames being the same...
+*/
+
+static int filter_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Obtain the transition instance
+       mlt_transition transition = mlt_frame_pop_audio( this );
+       mlt_transition_process( transition, this, this );
+       return mlt_frame_get_audio( this, buffer, format, frequency, channels, samples );
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Obtain the transition instance
+       mlt_transition transition = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "instance", NULL );
+
+       // If we haven't created the instance, do it now
+       if ( transition == NULL )
+       {
+               char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "transition" );
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+               transition = mlt_factory_transition( profile, name, NULL );
+               mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "instance", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+       }
+
+       // We may still not have a transition...
+       if ( transition != NULL )
+       {
+               // Get the transition type
+               int type = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type" );
+
+               // Set the basic info
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "in" ) );
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "out" ) );
+
+               // Refresh with current user values
+               mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), MLT_FILTER_PROPERTIES( this ), "transition." );
+
+               if ( type & 1 && !mlt_frame_is_test_card( frame ) && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "hide" ) & 1 ) )
+               {
+                       mlt_frame_push_service( frame, transition );
+                       mlt_frame_push_get_image( frame, filter_get_image );
+               }
+               if ( type & 2 && !mlt_frame_is_test_audio( frame ) && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "hide" ) & 2 ) )
+               {
+                       mlt_frame_push_audio( frame, transition );
+                       mlt_frame_push_audio( frame, filter_get_audio );
+               }
+
+               if ( type == 0 )
+                       mlt_properties_debug( MLT_TRANSITION_PROPERTIES( transition ), "unknown transition type", stderr );
+       }
+       else
+       {
+               mlt_properties_debug( MLT_FILTER_PROPERTIES( this ), "no transition", stderr );
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_transition_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "transition", arg );
+               this->process = filter_process;
+       }
+       return this;
+}
+
diff --git a/src/modules/core/filter_watermark.c b/src/modules/core/filter_watermark.c
new file mode 100644 (file)
index 0000000..2e88e08
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * filter_watermark.c -- watermark filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_transition.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Error we will return
+       int error = 0;
+
+       // Get the watermark filter object
+       mlt_filter this = mlt_frame_pop_service( frame );
+
+       // Get the properties of the filter
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+       // Get the producer from the filter
+       mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+       // Get the composite from the filter
+       mlt_transition composite = mlt_properties_get_data( properties, "composite", NULL );
+
+       // Get the resource to use
+       char *resource = mlt_properties_get( properties, "resource" );
+
+       // Get the old resource
+       char *old_resource = mlt_properties_get( properties, "_old_resource" );
+
+       // Create a composite if we don't have one
+       if ( composite == NULL )
+       {
+               // Create composite via the factory
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+               composite = mlt_factory_transition( profile, "composite", NULL );
+
+               // Register the composite for reuse/destruction
+               if ( composite != NULL )
+                       mlt_properties_set_data( properties, "composite", composite, 0, ( mlt_destructor )mlt_transition_close, NULL );
+       }
+
+       // If we have one
+       if ( composite != NULL )
+       {
+               // Get the properties
+               mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+
+               // Pass all the composite. properties on the filter down
+               mlt_properties_pass( composite_properties, properties, "composite." );
+
+               if ( mlt_properties_get( properties, "composite.out" ) == NULL )
+                       mlt_properties_set_int( composite_properties, "out", mlt_properties_get_int( properties, "_out" ) );
+
+               // Force a refresh
+               mlt_properties_set_int( composite_properties, "refresh", 1 );
+       }
+
+       // Create a producer if don't have one
+       if ( producer == NULL || ( old_resource != NULL && strcmp( resource, old_resource ) ) )
+       {
+               // Get the factory producer service
+               char *factory = mlt_properties_get( properties, "factory" );
+
+               // Create the producer
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+               producer = mlt_factory_producer( profile, factory, resource );
+
+               // If we have one
+               if ( producer != NULL )
+               {
+                       // Register the producer for reuse/destruction
+                       mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+                       // Ensure that we loop
+                       mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+
+                       // Set the old resource
+                       mlt_properties_set( properties, "_old_resource", resource );
+               }
+       }
+
+       if ( producer != NULL )
+       {
+               // Get the producer properties
+               mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+               // Now pass all producer. properties on the filter down
+               mlt_properties_pass( producer_properties, properties, "producer." );
+       }
+
+       // Only continue if we have both producer and composite
+       if ( composite != NULL && producer != NULL )
+       {
+               // Get the service of the producer
+               mlt_service service = MLT_PRODUCER_SERVICE( producer );
+
+               // We will get the 'b frame' from the producer
+               mlt_frame b_frame = NULL;
+
+               // Get the unique id of the filter (used to reacquire the producer position)
+               char *name = mlt_properties_get( properties, "_unique_id" );
+
+               // Get the original producer position
+               mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+               // Make sure the producer is in the correct position
+               mlt_producer_seek( producer, position );
+
+               // Resetting position to appease the composite transition
+               mlt_frame_set_position( frame, position );
+
+               // Get the b frame and process with composite if successful
+               if ( mlt_service_get_frame( service, &b_frame, 0 ) == 0 )
+               {
+                       // Get the a and b frame properties
+                       mlt_properties a_props = MLT_FRAME_PROPERTIES( frame );
+                       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+                       // Set the b frame to be in the same position and have same consumer requirements
+                       mlt_frame_set_position( b_frame, position );
+                       mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+                       mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) );
+                       mlt_properties_set_double( b_props, "output_ratio", mlt_properties_get_double( a_props, "output_ratio" ) );
+
+                       // Check for the special case - no aspect ratio means no problem :-)
+                       if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 )
+                               mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+                       if ( mlt_frame_get_aspect_ratio( frame ) == 0 )
+                               mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+                       mlt_properties_set_int( b_props, "normalised_width", mlt_properties_get_int( a_props, "normalised_width" ) );
+                       mlt_properties_set_int( b_props, "normalised_height", mlt_properties_get_int( a_props, "normalised_height" ) );
+
+                       if ( mlt_properties_get_int( properties, "distort" ) )
+                       {
+                               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( composite ), "distort", 1 );
+                               mlt_properties_set_int( a_props, "distort", 1 );
+                               mlt_properties_set_int( b_props, "distort", 1 );
+                       }
+
+                       if ( mlt_properties_get_int( properties, "reverse" ) == 0 )
+                       {
+                               // Apply all filters that are attached to this filter to the b frame
+                               mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 );
+
+                               // Process the frame
+                               mlt_transition_process( composite, frame, b_frame );
+
+                               // Get the image
+                               error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+                       }
+                       else
+                       {
+                               char temp[ 132 ];
+                               int count = 0;
+                               uint8_t *alpha = NULL;
+                               const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
+                               if ( rescale == NULL || !strcmp( rescale, "none" ) )
+                                       rescale = "hyper";
+                               mlt_transition_process( composite, b_frame, frame );
+                               mlt_properties_set_int( a_props, "consumer_deinterlace", 1 );
+                               mlt_properties_set_int( b_props, "consumer_deinterlace", 1 );
+                               mlt_properties_set( a_props, "rescale.interp", rescale );
+                               mlt_properties_set( b_props, "rescale.interp", rescale );
+                               mlt_service_apply_filters( MLT_FILTER_SERVICE( this ), b_frame, 0 );
+                               error = mlt_frame_get_image( b_frame, image, format, width, height, 1 );
+                               alpha = mlt_frame_get_alpha_mask( b_frame );
+                               mlt_properties_set_data( a_props, "image", *image, *width * *height * 2, NULL, NULL );
+                               mlt_properties_set_data( a_props, "alpha", alpha, *width * *height, NULL, NULL );
+                               mlt_properties_set_int( a_props, "width", *width );
+                               mlt_properties_set_int( a_props, "height", *height );
+                               mlt_properties_set_int( a_props, "progressive", 1 );
+                               mlt_properties_inc_ref( b_props );
+                               strcpy( temp, "_b_frame" );
+                               while( mlt_properties_get_data( a_props, temp, NULL ) != NULL )
+                                       sprintf( temp, "_b_frame%d", count ++ );
+                               mlt_properties_set_data( a_props, temp, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+                       }
+               }
+
+               // Close the b frame
+               mlt_frame_close( b_frame );
+       }
+       else
+       {
+               // Get the image from the frame without running fx
+               error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get a unique name to store the frame position
+       char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+
+       // Assign the frame out point to the filter (just in case we need it later)
+       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_out", mlt_properties_get_int( properties, "out" ) );
+
+       // Assign the current position to the name
+       mlt_properties_set_position( properties, name, mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) );
+
+       // Push the filter on to the stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the get_image on to the stack
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               this->process = filter_process;
+               mlt_properties_set( properties, "factory", "fezzik" );
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "resource", arg );
+               // Ensure that attached filters are handled privately
+               mlt_properties_set_int( properties, "_filter_private", 1 );
+       }
+       return this;
+}
+
diff --git a/src/modules/core/producer_colour.c b/src/modules/core/producer_colour.c
new file mode 100644 (file)
index 0000000..ac42001
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * producer_colour.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+typedef struct
+{
+       uint8_t r, g, b, a;
+} rgba_color;
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_colour_init( mlt_profile profile, mlt_service_type type, const char *id, char *colour )
+{
+       mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) );
+       if ( producer != NULL && mlt_producer_init( producer, NULL ) == 0 )
+       {
+               // Get the properties interface
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+       
+               // Callback registration
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Set the default properties
+               mlt_properties_set( properties, "resource", colour == NULL ? "0x000000ff" : colour );
+               mlt_properties_set( properties, "_resource", "" );
+               mlt_properties_set_double( properties, "aspect_ratio", 0 );
+               
+               return producer;
+       }
+       free( producer );
+       return NULL;
+}
+
+rgba_color parse_color( char *color, unsigned int color_int )
+{
+       rgba_color result = { 0xff, 0xff, 0xff, 0xff };
+
+       if ( !strcmp( color, "red" ) )
+       {
+               result.r = 0xff;
+               result.g = 0x00;
+               result.b = 0x00;
+       }
+       else if ( !strcmp( color, "green" ) )
+       {
+               result.r = 0x00;
+               result.g = 0xff;
+               result.b = 0x00;
+       }
+       else if ( !strcmp( color, "blue" ) )
+       {
+               result.r = 0x00;
+               result.g = 0x00;
+               result.b = 0xff;
+       }
+       else if ( strcmp( color, "white" ) )
+       {
+               result.r = ( color_int >> 24 ) & 0xff;
+               result.g = ( color_int >> 16 ) & 0xff;
+               result.b = ( color_int >> 8 ) & 0xff;
+               result.a = ( color_int ) & 0xff;
+       }
+
+       return result;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // May need to know the size of the image to clone it
+       int size = 0;
+       
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer for this frame
+       mlt_producer producer = mlt_properties_get_data( properties, "producer_colour", NULL );
+
+       // Obtain properties of producer
+       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Get the current and previous colour strings
+       char *now = mlt_properties_get( producer_props, "resource" );
+       char *then = mlt_properties_get( producer_props, "_resource" );
+
+       // Get the current image and dimensions cached in the producer
+       uint8_t *image = mlt_properties_get_data( producer_props, "image", &size );
+       int current_width = mlt_properties_get_int( producer_props, "_width" );
+       int current_height = mlt_properties_get_int( producer_props, "_height" );
+
+       // Parse the colour
+       if ( now && strchr( now, '/' ) )
+       {
+               now = strrchr( now, '/' ) + 1;
+               mlt_properties_set( producer_props, "resource", now );
+       }
+       rgba_color color = parse_color( now, mlt_properties_get_int( producer_props, "resource" ) );
+
+       // See if we need to regenerate
+       if ( strcmp( now, then ) || *width != current_width || *height != current_height )
+       {
+               // Color the image
+               uint8_t y, u, v;
+               int i = *height;
+               int j = 0;
+               int uneven = *width % 2;
+               int count = ( *width - uneven ) / 2;
+               uint8_t *p = NULL;
+
+               // Allocate the image
+               size = *width * *height * 2;
+               image = mlt_pool_alloc( size );
+
+               // Update the producer
+               mlt_properties_set_data( producer_props, "image", image, size, mlt_pool_release, NULL );
+               mlt_properties_set_int( producer_props, "_width", *width );
+               mlt_properties_set_int( producer_props, "_height", *height );
+               mlt_properties_set( producer_props, "_resource", now );
+
+               RGB2YUV( color.r, color.g, color.b, y, u, v );
+
+               p = image;
+
+               while ( i -- )
+               {
+                       j = count;
+                       while ( j -- )
+                       {
+                               *p ++ = y;
+                               *p ++ = u;
+                               *p ++ = y;
+                               *p ++ = v;
+                       }
+                       if ( uneven )
+                       {
+                               *p ++ = y;
+                               *p ++ = u;
+                       }
+               }
+       }
+
+       // Update the frame
+       mlt_properties_set_int( properties, "width", *width );
+       mlt_properties_set_int( properties, "height", *height );
+       
+       // Clone if necessary (deemed always necessary)
+       if ( 1 )
+       {
+               // Create the alpha channel
+               uint8_t *alpha = mlt_pool_alloc( size >> 1 );
+
+               // Clone our image
+               uint8_t *copy = mlt_pool_alloc( size );
+               memcpy( copy, image, size );
+
+               // We're going to pass the copy on
+               image = copy;
+
+               // Initialise the alpha
+               if ( alpha )
+                       memset( alpha, color.a, size >> 1 );
+
+               // Now update properties so we free the copy after
+               mlt_properties_set_data( properties, "image", copy, size, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "alpha", alpha, size >> 1, mlt_pool_release, NULL );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+       }
+
+       // Pass on the image
+       *buffer = image;
+       *format = mlt_image_yuv422;
+
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       if ( *frame != NULL )
+       {
+               // Obtain properties of frame and producer
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Obtain properties of producer
+               mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+               // Set the producer on the frame properties
+               mlt_properties_set_data( properties, "producer_colour", producer, 0, NULL, NULL );
+
+               // Update timecode on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+               // Set producer-specific frame properties
+               mlt_properties_set_int( properties, "progressive", 1 );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+
+               // colour is an alias for resource
+               if ( mlt_properties_get( producer_props, "colour" ) != NULL )
+                       mlt_properties_set( producer_props, "resource", mlt_properties_get( producer_props, "colour" ) );
+               
+               // Push the get_image method
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer producer )
+{
+       producer->close = NULL;
+       mlt_producer_close( producer );
+       free( producer );
+}
diff --git a/src/modules/core/producer_consumer.c b/src/modules/core/producer_consumer.c
new file mode 100644 (file)
index 0000000..78c5bcc
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * producer_consumer.c -- produce as a consumer of an encapsulated producer
+ * Copyright (C) 2008 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+struct context_s {
+       mlt_producer this;
+       mlt_producer producer;
+       mlt_consumer consumer;
+       mlt_profile profile;
+       int is_close_profile;
+};
+typedef struct context_s *context; 
+
+
+static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       context cx = mlt_frame_pop_service( frame );
+       mlt_frame nested_frame = mlt_frame_pop_service( frame );
+
+       *width = cx->profile->width;
+       *height = cx->profile->height;
+
+       int result = mlt_frame_get_image( nested_frame, image, format, width, height, writable );
+
+       // Allocate the image
+       int size = *width * *height * ( *format == mlt_image_yuv422 ? 2 : *format == mlt_image_rgb24 ? 3 : *format == mlt_image_rgb24a ? 4 : ( 3 / 2 ) );
+       uint8_t *new_image = mlt_pool_alloc( size );
+
+       // Update the frame
+       mlt_properties properties = mlt_frame_properties( frame );
+       mlt_properties_set_data( properties, "image", new_image, size, mlt_pool_release, NULL );
+       memcpy( new_image, *image, size );
+       mlt_frame_close( nested_frame );
+       *image = new_image;
+
+//     mlt_properties_debug( properties, "frame", stderr );
+//     mlt_properties_debug( mlt_frame_properties( nested_frame ), "nested_frame", stderr );
+
+       return result;
+}
+
+static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       mlt_frame nested_frame = mlt_frame_pop_audio( frame );
+       int result = mlt_frame_get_audio( nested_frame, buffer, format, frequency, channels, samples );
+       int size = *channels * *samples * sizeof( int16_t );
+       int16_t *new_buffer = mlt_pool_alloc( size );
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "audio", new_buffer, size, mlt_pool_release, NULL );
+       memcpy( new_buffer, *buffer, size );
+       *buffer = new_buffer;
+       mlt_frame_close( nested_frame );
+       return result;
+}
+
+static int get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES(this);
+       context cx = mlt_properties_get_data( properties, "context", NULL );
+
+       if ( !cx )
+       {
+               // Allocate and initialize our context
+               cx = mlt_pool_alloc( sizeof( struct context_s ) );
+               mlt_properties_set_data( properties, "context", cx, 0, mlt_pool_release, NULL );
+               cx->this = this;
+               char *profile_name = mlt_properties_get( properties, "profile" );
+               mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( this ) );
+
+               if ( profile_name )
+               {
+                       cx->profile = mlt_profile_init( profile_name );
+                       cx->is_close_profile = 1;
+               }
+               else
+               {
+                       cx->profile = profile;
+                       cx->is_close_profile = 0;
+               }
+
+               // For now, we must conform the nested network's frame rate to the parent network's
+               // framerate.
+               cx->profile->frame_rate_num = profile->frame_rate_num;
+               cx->profile->frame_rate_den = profile->frame_rate_den;
+
+               // We will encapsulate a consumer
+               cx->consumer = mlt_consumer_new( cx->profile );
+               // Do not use _pass_list on real_time so that it defaults to 0 in the absence of
+               // an explicit real_time property.
+               mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( cx->consumer ), "real_time",
+                       mlt_properties_get_int( properties, "real_time" ) );
+               mlt_properties_pass_list( MLT_CONSUMER_PROPERTIES( cx->consumer ), properties,
+                       "buffer, prefill" );
+       
+               // Encapsulate a real producer for the resource
+               cx->producer = mlt_factory_producer( cx->profile, mlt_environment( "MLT_PRODUCER" ),
+                       mlt_properties_get( properties, "resource" ) );
+               mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( cx->producer ),
+                       "out, length" );
+
+               // Since we control the seeking, prevent it from seeking on its own
+               mlt_producer_set_speed( cx->producer, 0 );
+
+               // Connect it all together
+               mlt_consumer_connect( cx->consumer, MLT_PRODUCER_SERVICE( cx->producer ) );
+               mlt_consumer_start( cx->consumer );
+       }
+
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+       if ( frame )
+       {
+               // Our "in" needs to be the same, keep it so
+               mlt_properties_pass_list( MLT_PRODUCER_PROPERTIES( cx->producer ), properties, "in, out" );
+
+               // Seek the producer to the correct place
+               // Calculate our positions
+               double actual_position = mlt_producer_get_speed( this ) * (double)mlt_producer_position( this );
+               mlt_position need_first = floor( actual_position );
+               mlt_producer_seek( cx->producer, need_first );
+               
+               // Get the nested frame
+               mlt_frame nested_frame = mlt_consumer_rt_frame( cx->consumer );
+
+               // Stack the producer and our methods on the nested frame
+               mlt_frame_push_service( *frame, nested_frame );
+               mlt_frame_push_service( *frame, cx );
+               mlt_frame_push_get_image( *frame, get_image );
+               mlt_frame_push_audio( *frame, nested_frame );
+               mlt_frame_push_audio( *frame, get_audio );
+               
+               // Give the returned frame temporal identity
+               mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+               
+               // Put additional references on the frame so both get_image and get_audio
+               // methods can close it.
+               mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( nested_frame ) );
+
+               // Inform the normalizers about our video properties
+               mlt_properties frame_props = MLT_FRAME_PROPERTIES( *frame );
+               mlt_properties_set_double( frame_props, "aspect_ratio", mlt_profile_sar( cx->profile ) );
+               mlt_properties_set_int( frame_props, "width", cx->profile->width );
+               mlt_properties_set_int( frame_props, "height", cx->profile->height );
+               mlt_properties_set_int( frame_props, "real_width", cx->profile->width );
+               mlt_properties_set_int( frame_props, "real_height", cx->profile->height );
+               mlt_properties_set_int( frame_props, "progressive", cx->profile->progressive );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( this );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+       context cx = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( this ), "context", NULL );
+       
+       // Shut down all the encapsulated services
+       if ( cx )
+       {
+               mlt_consumer_stop( cx->consumer );
+               mlt_consumer_close( cx->consumer );
+               mlt_producer_close( cx->producer );
+               if ( cx->is_close_profile )
+                       mlt_profile_close( cx->profile );
+       }
+       
+       this->close = NULL;
+       mlt_producer_close( this );
+       free( this );
+}
+
+mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_producer this = mlt_producer_new( );
+
+       // Encapsulate the real producer
+       mlt_producer real_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), arg );
+
+       if ( this && real_producer )
+       {
+               // Override some producer methods
+               this->close = ( mlt_destructor )producer_close;
+               this->get_frame = get_frame;
+               
+               // Get the properties of this producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+               mlt_properties_set( properties, "resource", arg );
+               mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "out, length" );
+
+               // Done with the producer - will re-open later when we have the profile property
+               mlt_producer_close( real_producer );
+       }
+       else
+       {
+               if ( this )
+                       mlt_producer_close( this );
+               if ( real_producer )
+                       mlt_producer_close( real_producer );
+
+               this = NULL;
+       }
+       return this;
+}
diff --git a/src/modules/core/producer_noise.c b/src/modules/core/producer_noise.c
new file mode 100644 (file)
index 0000000..825f591
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * producer_noise.c -- noise generating producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Random number generator
+*/
+
+static unsigned int seed_x = 521288629;
+static unsigned int seed_y = 362436069;
+
+static inline unsigned int fast_rand( )
+{
+   static unsigned int a = 18000, b = 30903;
+   seed_x = a * ( seed_x & 65535 ) + ( seed_x >> 16 );
+   seed_y = b * ( seed_y & 65535 ) + ( seed_y >> 16 );
+   return ( ( seed_x << 16 ) + ( seed_y & 65535 ) );
+}
+
+// Foward declarations
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer this );
+
+/** Initialise.
+*/
+
+mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create a new producer object
+       mlt_producer this = mlt_producer_new( );
+
+       // Initialise the producer
+       if ( this != NULL )
+       {
+               // Callback registration
+               this->get_frame = producer_get_frame;
+               this->close = ( mlt_destructor )producer_close;
+       }
+
+       return this;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Calculate the size of the image
+       int size = *width * *height * 2;
+
+       // Set the format being returned
+       *format = mlt_image_yuv422;
+
+       // Allocate the image
+       *buffer = mlt_pool_alloc( size );
+
+       // Update the frame
+       mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+       mlt_properties_set_int( properties, "width", *width );
+       mlt_properties_set_int( properties, "height", *height );
+
+       // Before we write to the image, make sure we have one
+       if ( *buffer != NULL )
+       {
+               // Calculate the end of the buffer
+               uint8_t *p = *buffer + *width * *height * 2;
+
+               // Value to hold a random number
+               uint32_t value;
+
+               // Generate random noise
+               while ( p != *buffer )
+               {
+                       value = fast_rand( ) & 0xff;
+                       *( -- p ) = 128;
+                       *( -- p ) = value < 16 ? 16 : value > 240 ? 240 : value;
+               }
+       }
+
+       return 0;
+}
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the frame properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       int size = 0;
+
+       // Correct the returns if necessary
+       *samples = *samples <= 0 ? 1920 : *samples;
+       *channels = *channels <= 0 ? 2 : *channels;
+       *frequency = *frequency <= 0 ? 48000 : *frequency;
+
+       // Calculate the size of the buffer
+       size = *samples * *channels * sizeof( int16_t );
+
+       // Allocate the buffer
+       *buffer = mlt_pool_alloc( size );
+
+       // Make sure we got one and fill it
+       if ( *buffer != NULL )
+       {
+               int16_t *p = *buffer + size / 2;
+               while ( p != *buffer ) 
+                       *( -- p ) = fast_rand( ) & 0x0f00;
+       }
+
+       // Set the buffer for destruction
+       mlt_properties_set_data( properties, "audio", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+       // Check that we created a frame and initialise it
+       if ( *frame != NULL )
+       {
+               // Obtain properties of frame
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Aspect ratio is whatever it needs to be
+               mlt_properties_set_double( properties, "aspect_ratio", 0 );
+
+               // Set producer-specific frame properties
+               mlt_properties_set_int( properties, "progressive", 1 );
+
+               // Update timecode on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+               // Push the get_image method
+               mlt_frame_push_get_image( *frame, producer_get_image );
+
+               // Specify the audio
+               mlt_frame_push_audio( *frame, producer_get_audio );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( this );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+       this->close = NULL;
+       mlt_producer_close( this );
+       free( this );
+}
+
diff --git a/src/modules/core/producer_ppm.c b/src/modules/core/producer_ppm.c
new file mode 100644 (file)
index 0000000..0121579
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * producer_ppm.c -- simple ppm test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct producer_ppm_s *producer_ppm;
+
+struct producer_ppm_s
+{
+       struct mlt_producer_s parent;
+       char *command;
+       FILE *video;
+       FILE *audio;
+       uint64_t expected;
+};
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *command )
+{
+       producer_ppm this = calloc( sizeof( struct producer_ppm_s ), 1 );
+       if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+       {
+               mlt_producer producer = &this->parent;
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               if ( command != NULL )
+               {
+                       mlt_properties_set( properties, "resource", command );
+                       this->command = strdup( command );
+               }
+               else
+               {
+                       mlt_properties_set( properties, "resource", "ppm test" );
+               }
+
+               return producer;
+       }
+       free( this );
+       return NULL;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       if ( mlt_properties_get_int( properties, "has_image" ) )
+       {
+               // Get the RGB image
+               uint8_t *rgb = mlt_properties_get_data( properties, "image", NULL );
+
+               // Get width and height
+               *width = mlt_properties_get_int( properties, "width" );
+               *height = mlt_properties_get_int( properties, "height" );
+
+               // Convert to requested format
+               if ( *format == mlt_image_yuv422 )
+               {
+                       uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 );
+                       mlt_convert_rgb24_to_yuv422( rgb, *width, *height, *width * 3, image );
+                       mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+                       *buffer = image;
+               }
+               else if ( *format == mlt_image_rgb24 )
+               {
+                       *buffer = rgb;
+               }
+       }
+       else
+       {
+               mlt_frame_get_image( this, buffer, format, width, height, writable );
+       }
+
+       return 0;
+}
+
+FILE *producer_ppm_run_video( producer_ppm this )
+{
+       if ( this->video == NULL )
+       {
+               if ( this->command == NULL )
+               {
+                       this->video = popen( "image2raw -k -r 25 -ppm /usr/share/pixmaps/*.png", "r" );
+               }
+               else
+               {
+                       char command[ 1024 ];
+                       float fps = mlt_producer_get_fps( &this->parent );
+                       float position = mlt_producer_position( &this->parent );
+                       sprintf( command, "ffmpeg -i \"%s\" -ss %f -f imagepipe -r %f -img ppm - 2>/dev/null", this->command, position, fps );
+                       this->video = popen( command, "r" );
+               }
+       }
+       return this->video;
+}
+
+FILE *producer_ppm_run_audio( producer_ppm this )
+{
+       if ( this->audio == NULL )
+       {
+               if ( this->command != NULL )
+               {
+                       char command[ 1024 ];
+                       float position = mlt_producer_position( &this->parent );
+                       sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", this->command, position );
+                       this->audio = popen( command, "r" );
+               }
+       }
+       return this->audio;
+}
+
+static void producer_ppm_position( producer_ppm this, uint64_t requested )
+{
+       if ( requested != this->expected )
+       {
+               if ( this->video != NULL )
+                       pclose( this->video );
+               this->video = NULL;
+               if ( this->audio != NULL )
+                       pclose( this->audio );
+               this->audio = NULL;
+       }
+
+       // This is the next frame we expect
+       this->expected = mlt_producer_frame( &this->parent ) + 1;
+
+       // Open the pipe
+       this->video = producer_ppm_run_video( this );
+
+       // Open the audio pipe
+       this->audio = producer_ppm_run_audio( this );
+
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       FILE *pipe = mlt_properties_get_data( properties, "audio.pipe", NULL );
+
+       *frequency = 48000;
+       *channels = 2;
+       *samples = 1920;
+
+       // Size
+       int size = *samples * *channels * 2;
+
+       // Allocate an image
+       *buffer = malloc( size );
+               
+       // Read it
+       if ( pipe != NULL )
+               fread( *buffer, size, 1, pipe );
+       else
+               memset( *buffer, 0, size );
+
+       // Pass the data on the frame properties
+       mlt_properties_set_data( properties, "audio", *buffer, size, free, NULL );
+
+       return 0;
+}
+
+static int read_ppm_header( FILE *video, int *width, int *height )
+{
+       int count = 0;
+       {
+               char temp[ 132 ];
+               fgets( temp, 132, video );
+               fgets( temp, 132, video );
+               count += sscanf( temp, "%d %d", width, height );
+               fgets( temp, 132, video );
+       }
+       return count;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       producer_ppm this = producer->child;
+       int width;
+       int height;
+
+       // Construct a test frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       // Are we at the position expected?
+       producer_ppm_position( this, mlt_producer_frame( producer ) );
+
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+       FILE *video = this->video;
+       FILE *audio = this->audio;
+
+       // Read the video
+       if ( video != NULL && read_ppm_header( video, &width, &height ) == 2 )
+       {
+               // Allocate an image
+               uint8_t *image = mlt_pool_alloc( width * ( height + 1 ) * 3 );
+               
+               // Read it
+               fread( image, width * height * 3, 1, video );
+
+               // Pass the data on the frame properties
+               mlt_properties_set_data( properties, "image", image, width * ( height + 1 ) * 3, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", width );
+               mlt_properties_set_int( properties, "height", height );
+               mlt_properties_set_int( properties, "has_image", 1 );
+               mlt_properties_set_int( properties, "progressive", 1 );
+               mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+               // Push the image callback
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+       else
+       {
+               // Push the image callback
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+
+       // Set the audio pipe
+       mlt_properties_set_data( properties, "audio.pipe", audio, 0, NULL, NULL );
+
+       // Hmm - register audio callback
+       mlt_frame_push_audio( *frame, producer_get_audio );
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       producer_ppm this = parent->child;
+       if ( this->video )
+               pclose( this->video );
+       if ( this->audio )
+               pclose( this->audio );
+       free( this->command );
+       parent->close = NULL;
+       mlt_producer_close( parent );
+       free( this );
+}
diff --git a/src/modules/core/transition_composite.c b/src/modules/core/transition_composite.c
new file mode 100644 (file)
index 0000000..04a518f
--- /dev/null
@@ -0,0 +1,1342 @@
+/*
+ * transition_composite.c -- compose one image over another using alpha channel
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "transition_composite.h"
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+typedef void ( *composite_line_fn )( uint8_t *dest, uint8_t *src, int width_src, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int softness );
+
+/** Geometry struct.
+*/
+
+struct geometry_s
+{
+       struct mlt_geometry_item_s item;
+       int nw; // normalised width
+       int nh; // normalised height
+       int sw; // scaled width, not including consumer scale based upon w/nw
+       int sh; // scaled height, not including consumer scale based upon h/nh
+       int halign; // horizontal alignment: 0=left, 1=center, 2=right
+       int valign; // vertical alignment: 0=top, 1=middle, 2=bottom
+       int x_src;
+       int y_src;
+};
+
+/** Parse the alignment properties into the geometry.
+*/
+
+static int alignment_parse( char* align )
+{
+       int ret = 0;
+       
+       if ( align == NULL );
+       else if ( isdigit( align[ 0 ] ) )
+               ret = atoi( align );
+       else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
+               ret = 1;
+       else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
+               ret = 2;
+
+       return ret;
+}
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( mlt_transition this, struct geometry_s *output, double position )
+{
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       mlt_geometry geometry = mlt_properties_get_data( properties, "geometries", NULL );
+       int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+       int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+       int length = mlt_geometry_get_length( geometry );
+
+       // Allow wrapping
+       if ( !repeat_off && position >= length && length != 0 )
+       {
+               int section = position / length;
+               position -= section * length;
+               if ( !mirror_off && section % 2 == 1 )
+                       position = length - position;
+       }
+
+       // Fetch the key for the position
+       mlt_geometry_fetch( geometry, &output->item, position );
+}
+
+static mlt_geometry transition_parse_keys( mlt_transition this, int normalised_width, int normalised_height )
+{
+       // Loop variable for property interrogation
+       int i = 0;
+
+       // Get the properties of the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // Create an empty geometries object
+       mlt_geometry geometry = mlt_geometry_init( );
+
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+       mlt_position out = mlt_transition_get_out( this );
+       int length = out - in + 1;
+       double cycle = mlt_properties_get_double( properties, "cycle" );
+
+       // Get the new style geometry string
+       char *property = mlt_properties_get( properties, "geometry" );
+
+       // Allow a geometry repeat cycle
+       if ( cycle >= 1 )
+               length = cycle;
+       else if ( cycle > 0 )
+               length *= cycle;
+
+       // Parse the geometry if we have one
+       mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
+
+       // Check if we're using the old style geometry
+       if ( property == NULL )
+       {
+               // DEPRECATED: Multiple keys for geometry information is inefficient and too rigid for 
+               // practical use - while deprecated, it has been slightly extended too - keys can now
+               // be specified out of order, and can be blanked or NULL to simulate removal
+
+               // Structure to use for parsing and inserting
+               struct mlt_geometry_item_s item;
+
+               // Parse the start property
+               item.frame = 0;
+               if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "start" ) ) == 0 )
+                       mlt_geometry_insert( geometry, &item );
+
+               // Parse the keys in between
+               for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+               {
+                       // Get the name of the property
+                       char *name = mlt_properties_get_name( properties, i );
+       
+                       // Check that it's valid
+                       if ( !strncmp( name, "key[", 4 ) )
+                       {
+                               // Get the value of the property
+                               char *value = mlt_properties_get_value( properties, i );
+       
+                               // Determine the frame number
+                               item.frame = atoi( name + 4 );
+       
+                               // Parse and add to the list
+                               if ( mlt_geometry_parse_item( geometry, &item, value ) == 0 )
+                                       mlt_geometry_insert( geometry, &item );
+                               else
+                                       fprintf( stderr, "Invalid Key - skipping %s = %s\n", name, value );
+                       }
+               }
+
+               // Parse the end
+               item.frame = -1;
+               if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "end" ) ) == 0 )
+                       mlt_geometry_insert( geometry, &item );
+       }
+       
+       return geometry;
+}
+
+/** Adjust position according to scaled size and alignment properties.
+*/
+
+static void alignment_calculate( struct geometry_s *geometry )
+{
+       geometry->item.x += ( geometry->item.w - geometry->sw ) * geometry->halign / 2;
+       geometry->item.y += ( geometry->item.h - geometry->sh ) * geometry->valign / 2;
+}
+
+/** Calculate the position for this frame.
+*/
+
+static int position_calculate( mlt_transition this, mlt_position position )
+{
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+
+       // Now do the calcs
+       return position - in;
+}
+
+/** Calculate the field delta for this frame - position between two frames.
+*/
+
+static inline double delta_calculate( mlt_transition this, mlt_frame frame, mlt_position position )
+{
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+       mlt_position out = mlt_transition_get_out( this );
+       double length = out - in + 1;
+
+       // Now do the calcs
+       double x = ( double )( position - in ) / length;
+       double y = ( double )( position + 1 - in ) / length;
+
+       return length * ( y - x ) / 2.0;
+}
+
+static int get_value( mlt_properties properties, const char *preferred, const char *fallback )
+{
+       int value = mlt_properties_get_int( properties, preferred );
+       if ( value == 0 )
+               value = mlt_properties_get_int( properties, fallback );
+       return value;
+}
+
+/** A linear threshold determination function.
+*/
+
+static inline int32_t linearstep( int32_t edge1, int32_t edge2, int32_t a )
+{
+       if ( a < edge1 )
+               return 0;
+
+       if ( a >= edge2 )
+               return 0x10000;
+
+       return ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+}
+
+/** A smoother, non-linear threshold determination function.
+*/
+
+static inline int32_t smoothstep( int32_t edge1, int32_t edge2, uint32_t a )
+{
+       if ( a < edge1 )
+               return 0;
+
+       if ( a >= edge2 )
+               return 0x10000;
+
+       a = ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+
+       return ( ( ( a * a ) >> 16 )  * ( ( 3 << 16 ) - ( 2 * a ) ) ) >> 16;
+}
+
+/** Load the luma map from PGM stream.
+*/
+
+static void luma_read_pgm( FILE *f, uint16_t **map, int *width, int *height )
+{
+       uint8_t *data = NULL;
+       while (1)
+       {
+               char line[128];
+               char comment[128];
+               int i = 2;
+               int maxval;
+               int bpp;
+               uint16_t *p;
+
+               line[127] = '\0';
+
+               // get the magic code
+               if ( fgets( line, 127, f ) == NULL )
+                       break;
+
+               // skip comments
+               while ( sscanf( line, " #%s", comment ) > 0 )
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+               if ( line[0] != 'P' || line[1] != '5' )
+                       break;
+
+               // skip white space and see if a new line must be fetched
+               for ( i = 2; i < 127 && line[i] != '\0' && isspace( line[i] ); i++ );
+               if ( ( line[i] == '\0' || line[i] == '#' ) && fgets( line, 127, f ) == NULL )
+                       break;
+
+               // skip comments
+               while ( sscanf( line, " #%s", comment ) > 0 )
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+               // get the dimensions
+               if ( line[0] == 'P' )
+                       i = sscanf( line, "P5 %d %d %d", width, height, &maxval );
+               else
+                       i = sscanf( line, "%d %d %d", width, height, &maxval );
+
+               // get the height value, if not yet
+               if ( i < 2 )
+               {
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+                       // skip comments
+                       while ( sscanf( line, " #%s", comment ) > 0 )
+                               if ( fgets( line, 127, f ) == NULL )
+                                       break;
+
+                       i = sscanf( line, "%d", height );
+                       if ( i == 0 )
+                               break;
+                       else
+                               i = 2;
+               }
+
+               // get the maximum gray value, if not yet
+               if ( i < 3 )
+               {
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+                       // skip comments
+                       while ( sscanf( line, " #%s", comment ) > 0 )
+                               if ( fgets( line, 127, f ) == NULL )
+                                       break;
+
+                       i = sscanf( line, "%d", &maxval );
+                       if ( i == 0 )
+                               break;
+               }
+
+               // determine if this is one or two bytes per pixel
+               bpp = maxval > 255 ? 2 : 1;
+
+               // allocate temporary storage for the raw data
+               data = mlt_pool_alloc( *width * *height * bpp );
+               if ( data == NULL )
+                       break;
+
+               // read the raw data
+               if ( fread( data, *width * *height * bpp, 1, f ) != 1 )
+                       break;
+
+               // allocate the luma bitmap
+               *map = p = (uint16_t*)mlt_pool_alloc( *width * *height * sizeof( uint16_t ) );
+               if ( *map == NULL )
+                       break;
+
+               // proces the raw data into the luma bitmap
+               for ( i = 0; i < *width * *height * bpp; i += bpp )
+               {
+                       if ( bpp == 1 )
+                               *p++ = data[ i ] << 8;
+                       else
+                               *p++ = ( data[ i ] << 8 ) + data[ i + 1 ];
+               }
+
+               break;
+       }
+
+       if ( data != NULL )
+               mlt_pool_release( data );
+}
+
+/** Generate a luma map from any YUV image.
+*/
+
+static void luma_read_yuv422( uint8_t *image, uint16_t **map, int width, int height )
+{
+       int i;
+       
+       // allocate the luma bitmap
+       uint16_t *p = *map = ( uint16_t* )mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+       if ( *map == NULL )
+               return;
+
+       // proces the image data into the luma bitmap
+       for ( i = 0; i < width * height * 2; i += 2 )
+               *p++ = ( image[ i ] - 16 ) * 299; // 299 = 65535 / 219
+}
+
+static inline int calculate_mix( uint16_t *luma, int j, int soft, int weight, int alpha )
+{
+       return ( ( ( luma == NULL ) ? weight : smoothstep( luma[ j ], luma[ j ] + soft, weight + soft ) ) * alpha ) >> 8;
+}
+
+static inline uint8_t sample_mix( uint8_t dest, uint8_t src, int mix )
+{
+       return ( src * mix + dest * ( ( 1 << 16 ) - mix ) ) >> 16;
+}
+
+/** Composite a source line over a destination line
+*/
+
+static void composite_line_yuv( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+       register int j;
+       register int mix;
+
+       for ( j = 0; j < width; j ++ )
+       {
+               mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ );
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *alpha_a = ( mix >> 8 ) | *alpha_a;
+               alpha_a ++;
+       }
+}
+
+static void composite_line_yuv_or( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+       register int j;
+       register int mix;
+
+       for ( j = 0; j < width; j ++ )
+       {
+               mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ | *alpha_a );
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *alpha_a ++ = mix >> 8;
+       }
+}
+
+static void composite_line_yuv_and( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+       register int j;
+       register int mix;
+
+       for ( j = 0; j < width; j ++ )
+       {
+               mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ & *alpha_a );
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *alpha_a ++ = mix >> 8;
+       }
+}
+
+static void composite_line_yuv_xor( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight, uint16_t *luma, int soft )
+{
+       register int j;
+       register int mix;
+
+       for ( j = 0; j < width; j ++ )
+       {
+               mix = calculate_mix( luma, j, soft, weight, *alpha_b ++ ^ *alpha_a );
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *dest = sample_mix( *dest, *src++, mix );
+               dest++;
+               *alpha_a ++ = mix >> 8;
+       }
+}
+
+/** Composite function.
+*/
+
+static int composite_yuv( uint8_t *p_dest, int width_dest, int height_dest, uint8_t *p_src, int width_src, int height_src, uint8_t *alpha_b, uint8_t *alpha_a, struct geometry_s geometry, int field, uint16_t *p_luma, int32_t softness, composite_line_fn line_fn )
+{
+       int ret = 0;
+       int i;
+       int x_src = -geometry.x_src, y_src = -geometry.y_src;
+       int uneven_x_src = ( x_src % 2 );
+       int32_t weight = ( ( 1 << 16 ) - 1 ) * ( geometry.item.mix / 100 );
+       int step = ( field > -1 ) ? 2 : 1;
+       int bpp = 2;
+       int stride_src = geometry.sw * bpp;
+       int stride_dest = width_dest * bpp;
+       
+       // Adjust to consumer scale
+       int x = rint( geometry.item.x * width_dest / geometry.nw );
+       int y = rint( geometry.item.y * height_dest / geometry.nh );
+       int uneven_x = ( x % 2 );
+
+       // optimization points - no work to do
+       if ( width_src <= 0 || height_src <= 0 || y_src >= height_src || x_src >= width_src )
+               return ret;
+
+       if ( ( x < 0 && -x >= width_src ) || ( y < 0 && -y >= height_src ) )
+               return ret;
+
+       // cropping affects the source width
+       if ( x_src > 0 )
+       {
+               width_src -= x_src;
+               // and it implies cropping
+               if ( width_src > geometry.item.w )
+                       width_src = geometry.item.w;
+       }
+
+       // cropping affects the source height
+       if ( y_src > 0 )
+       {
+               height_src -= y_src;
+               // and it implies cropping
+               if ( height_src > geometry.item.h )
+                       height_src = geometry.item.h;
+       }
+
+       // crop overlay off the left edge of frame
+       if ( x < 0 )
+       {
+               x_src = -x;
+               width_src -= x_src;
+               x = 0;
+       }
+
+       // crop overlay beyond right edge of frame
+       if ( x + width_src > width_dest )
+               width_src = width_dest - x;
+
+       // crop overlay off the top edge of the frame
+       if ( y < 0 )
+       {
+               y_src = -y;
+               height_src -= y_src;
+               y = 0;
+       }
+       
+       // crop overlay below bottom edge of frame
+       if ( y + height_src > height_dest )
+               height_src = height_dest - y;
+
+       // offset pointer into overlay buffer based on cropping
+       p_src += x_src * bpp + y_src * stride_src;
+
+       // offset pointer into frame buffer based upon positive coordinates only!
+       p_dest += ( x < 0 ? 0 : x ) * bpp + ( y < 0 ? 0 : y ) * stride_dest;
+
+       // offset pointer into alpha channel based upon cropping
+       alpha_b += x_src + y_src * stride_src / bpp;
+       alpha_a += x + y * stride_dest / bpp;
+
+       // offset pointer into luma channel based upon cropping
+       if ( p_luma )
+               p_luma += x_src + y_src * stride_src / bpp;
+       
+       // Assuming lower field first
+       // Special care is taken to make sure the b_frame is aligned to the correct field.
+       // field 0 = lower field and y should be odd (y is 0-based).
+       // field 1 = upper field and y should be even.
+       if ( ( field > -1 ) && ( y % 2 == field ) )
+       {
+               if ( ( field == 1 && y < height_dest - 1 ) || ( field == 0 && y == 0 ) )
+                       p_dest += stride_dest;
+               else
+                       p_dest -= stride_dest;
+       }
+
+       // On the second field, use the other lines from b_frame
+       if ( field == 1 )
+       {
+               p_src += stride_src;
+               alpha_b += stride_src / bpp;
+               alpha_a += stride_dest / bpp;
+               height_src--;
+       }
+
+       stride_src *= step;
+       stride_dest *= step;
+       int alpha_b_stride = stride_src / bpp;
+       int alpha_a_stride = stride_dest / bpp;
+
+       // Align chroma of source and destination
+       if ( uneven_x != uneven_x_src )
+       {
+               p_src += 2;
+               width_src -= 2;
+               alpha_b += 1;
+       }
+
+       // now do the compositing only to cropped extents
+       for ( i = 0; i < height_src; i += step )
+       {
+               line_fn( p_dest, p_src, width_src, alpha_b, alpha_a, weight, p_luma, softness );
+
+               p_src += stride_src;
+               p_dest += stride_dest;
+               alpha_b += alpha_b_stride;
+               alpha_a += alpha_a_stride;
+               if ( p_luma )
+                       p_luma += alpha_b_stride;
+       }
+
+       return ret;
+}
+
+
+/** Scale 16bit greyscale luma map using nearest neighbor.
+*/
+
+static inline void
+scale_luma ( uint16_t *dest_buf, int dest_width, int dest_height, const uint16_t *src_buf, int src_width, int src_height, int invert )
+{
+       register int i, j;
+       register int x_step = ( src_width << 16 ) / dest_width;
+       register int y_step = ( src_height << 16 ) / dest_height;
+       register int x, y = 0;
+
+       for ( i = 0; i < dest_height; i++ )
+       {
+               const uint16_t *src = src_buf + ( y >> 16 ) * src_width;
+               x = 0;
+               
+               for ( j = 0; j < dest_width; j++ )
+               {
+                       *dest_buf++ = src[ x >> 16 ] ^ invert;
+                       x += x_step;
+               }
+               y += y_step;
+       }
+}
+
+static uint16_t* get_luma( mlt_transition this, mlt_properties properties, int width, int height )
+{
+       // The cached luma map information
+       int luma_width = mlt_properties_get_int( properties, "_luma.width" );
+       int luma_height = mlt_properties_get_int( properties, "_luma.height" );
+       uint16_t *luma_bitmap = mlt_properties_get_data( properties, "_luma.bitmap", NULL );
+       int invert = mlt_properties_get_int( properties, "luma_invert" );
+       
+       // If the filename property changed, reload the map
+       char *resource = mlt_properties_get( properties, "luma" );
+
+       char temp[ 512 ];
+
+       if ( luma_width == 0 || luma_height == 0 )
+       {
+               luma_width = width;
+               luma_height = height;
+       }
+
+       if ( resource && resource[0] && strchr( resource, '%' ) )
+       {
+               // TODO: Clean up quick and dirty compressed/existence check
+               FILE *test;
+               sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+               test = fopen( temp, "r" );
+               if ( test == NULL )
+                       strcat( temp, ".png" );
+               else
+                       fclose( test );
+               resource = temp;
+       }
+
+       if ( resource && resource[0] )
+       {
+               char *old_luma = mlt_properties_get( properties, "_luma" );
+               int old_invert = mlt_properties_get_int( properties, "_luma_invert" );
+
+               if ( invert != old_invert || ( old_luma && old_luma[0] && strcmp( resource, old_luma ) ) )
+               {
+                       mlt_properties_set_data( properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL );
+                       luma_bitmap = NULL;
+               }
+       }
+       else {
+               char *old_luma = mlt_properties_get( properties, "_luma" );
+               if ( old_luma && old_luma[0] )
+               {
+                       mlt_properties_set_data( properties, "_luma.orig_bitmap", NULL, 0, NULL, NULL );
+                       luma_bitmap = NULL;
+                       mlt_properties_set( properties, "_luma", NULL);
+               }
+       }
+
+       if ( resource && resource[0] && ( luma_bitmap == NULL || luma_width != width || luma_height != height ) )
+       {
+               uint16_t *orig_bitmap = mlt_properties_get_data( properties, "_luma.orig_bitmap", NULL );
+               luma_width = mlt_properties_get_int( properties, "_luma.orig_width" );
+               luma_height = mlt_properties_get_int( properties, "_luma.orig_height" );
+
+               // Load the original luma once
+               if ( orig_bitmap == NULL )
+               {
+                       char *extension = strrchr( resource, '.' );
+                       
+                       // See if it is a PGM
+                       if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 )
+                       {
+                               // Open PGM
+                               FILE *f = fopen( resource, "r" );
+                               if ( f != NULL )
+                               {
+                                       // Load from PGM
+                                       luma_read_pgm( f, &orig_bitmap, &luma_width, &luma_height );
+                                       fclose( f );
+                                       
+                                       // Remember the original size for subsequent scaling
+                                       mlt_properties_set_data( properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+                                       mlt_properties_set_int( properties, "_luma.orig_width", luma_width );
+                                       mlt_properties_set_int( properties, "_luma.orig_height", luma_height );
+                               }
+                       }
+                       else
+                       {
+                               // Get the factory producer service
+                               char *factory = mlt_properties_get( properties, "factory" );
+       
+                               // Create the producer
+                               mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+                               mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+       
+                               // If we have one
+                               if ( producer != NULL )
+                               {
+                                       // Get the producer properties
+                                       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+       
+                                       // Ensure that we loop
+                                       mlt_properties_set( producer_properties, "eof", "loop" );
+       
+                                       // Now pass all producer. properties on the transition down
+                                       mlt_properties_pass( producer_properties, properties, "luma." );
+       
+                                       // We will get the alpha frame from the producer
+                                       mlt_frame luma_frame = NULL;
+       
+                                       // Get the luma frame
+                                       if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 )
+                                       {
+                                               uint8_t *luma_image;
+                                               mlt_image_format luma_format = mlt_image_yuv422;
+       
+                                               // Get image from the luma producer
+                                               mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "none" );
+                                               mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+       
+                                               // Generate the luma map
+                                               if ( luma_image != NULL && luma_format == mlt_image_yuv422 )
+                                                       luma_read_yuv422( luma_image, &orig_bitmap, luma_width, luma_height );
+       
+                                               // Remember the original size for subsequent scaling
+                                               mlt_properties_set_data( properties, "_luma.orig_bitmap", orig_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+                                               mlt_properties_set_int( properties, "_luma.orig_width", luma_width );
+                                               mlt_properties_set_int( properties, "_luma.orig_height", luma_height );
+                                               
+                                               // Cleanup the luma frame
+                                               mlt_frame_close( luma_frame );
+                                       }
+       
+                                       // Cleanup the luma producer
+                                       mlt_producer_close( producer );
+                               }
+                       }
+               }
+               // Scale luma map
+               luma_bitmap = mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+               scale_luma( luma_bitmap, width, height, orig_bitmap, luma_width, luma_height, invert * ( ( 1 << 16 ) - 1 ) );
+
+               // Remember the scaled luma size to prevent unnecessary scaling
+               mlt_properties_set_int( properties, "_luma.width", width );
+               mlt_properties_set_int( properties, "_luma.height", height );
+               mlt_properties_set_data( properties, "_luma.bitmap", luma_bitmap, width * height * 2, mlt_pool_release, NULL );
+               mlt_properties_set( properties, "_luma", resource );
+               mlt_properties_set_int( properties, "_luma_invert", invert );
+       }
+       return luma_bitmap;
+}
+
+/** Get the properly sized image from b_frame.
+*/
+
+static int get_b_frame_image( mlt_transition this, mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry )
+{
+       int ret = 0;
+       mlt_image_format format = mlt_image_yuv422;
+
+       // Get the properties objects
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       uint8_t resize_alpha = mlt_properties_get_int( b_props, "resize_alpha" );
+
+       // Do not scale if we are cropping - the compositing rectangle can crop the b image
+       // TODO: Use the animatable w and h of the crop geometry to scale independently of crop rectangle
+       if ( mlt_properties_get( properties, "crop" ) )
+       {
+               int real_width = get_value( b_props, "real_width", "width" );
+               int real_height = get_value( b_props, "real_height", "height" );
+               double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" );
+               double consumer_ar = mlt_properties_get_double( b_props, "consumer_aspect_ratio" );
+               double background_ar = mlt_properties_get_double( b_props, "output_ratio" );
+               double output_ar = background_ar != 0.0 ? background_ar : consumer_ar;
+               int scaled_width = rint( ( input_ar == 0.0 ? output_ar : input_ar ) / output_ar * real_width );
+               int scaled_height = real_height;
+               geometry->sw = scaled_width;
+               geometry->sh = scaled_height;
+       }
+       // Normalise aspect ratios and scale preserving aspect ratio
+       else if ( mlt_properties_get_int( properties, "aligned" ) && mlt_properties_get_int( properties, "distort" ) == 0 && mlt_properties_get_int( b_props, "distort" ) == 0 && geometry->item.distort == 0 )
+       {
+               // Adjust b_frame pixel aspect
+               int normalised_width = geometry->item.w;
+               int normalised_height = geometry->item.h;
+               int real_width = get_value( b_props, "real_width", "width" );
+               int real_height = get_value( b_props, "real_height", "height" );
+               double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" );
+               double consumer_ar = mlt_properties_get_double( b_props, "consumer_aspect_ratio" );
+               double background_ar = mlt_properties_get_double( b_props, "output_ratio" );
+               double output_ar = background_ar != 0.0 ? background_ar : consumer_ar;
+               int scaled_width = rint( ( input_ar == 0.0 ? output_ar : input_ar ) / output_ar * real_width );
+               int scaled_height = real_height;
+// fprintf(stderr, "%s: scaled %dx%d norm %dx%d real %dx%d output_ar %f => %f\n", __FILE__,
+// scaled_width, scaled_height, normalised_width, normalised_height, real_width, real_height,
+// background_ar, output_ar);
+
+               // Now ensure that our images fit in the normalised frame
+               if ( scaled_width > normalised_width )
+               {
+                       scaled_height = rint( scaled_height * normalised_width / scaled_width );
+                       scaled_width = normalised_width;
+               }
+               if ( scaled_height > normalised_height )
+               {
+                       scaled_width = rint( scaled_width * normalised_height / scaled_height );
+                       scaled_height = normalised_height;
+               }
+
+               // Honour the fill request - this will scale the image to fill width or height while maintaining a/r
+               // ????: Shouln't this be the default behaviour?
+               if ( mlt_properties_get_int( properties, "fill" ) && scaled_width > 0 && scaled_height > 0 )
+               {
+                       if ( scaled_height < normalised_height && scaled_width * normalised_height / scaled_height <= normalised_width )
+                       {
+                               scaled_width = rint( scaled_width * normalised_height / scaled_height );
+                               scaled_height = normalised_height;
+                       }
+                       else if ( scaled_width < normalised_width && scaled_height * normalised_width / scaled_width < normalised_height )
+                       {
+                               scaled_height = rint( scaled_height * normalised_width / scaled_width );
+                               scaled_width = normalised_width;
+                       }
+               }
+
+               // Save the new scaled dimensions
+               geometry->sw = scaled_width;
+               geometry->sh = scaled_height;
+       }
+       else
+       {
+               geometry->sw = geometry->item.w;
+               geometry->sh = geometry->item.h;
+       }
+
+       // We want to ensure that we bypass resize now...
+       if ( resize_alpha == 0 )
+               mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
+
+       // If we're not aligned, we want a non-transparent background
+       if ( mlt_properties_get_int( properties, "aligned" ) == 0 )
+               mlt_properties_set_int( b_props, "resize_alpha", 255 );
+
+       // Take into consideration alignment for optimisation (titles are a special case)
+       if ( !mlt_properties_get_int( properties, "titles" ) &&
+                mlt_properties_get( properties, "crop" ) == NULL )
+               alignment_calculate( geometry );
+
+       // Adjust to consumer scale
+       *width = rint( geometry->sw * *width / geometry->nw );
+       *height = rint( geometry->sh * *height / geometry->nh );
+// fprintf(stderr, "%s: scaled %dx%d norm %dx%d resize %dx%d\n", __FILE__,
+// geometry->sw, geometry->sh, geometry->nw, geometry->nh, *width, *height);
+
+       ret = mlt_frame_get_image( b_frame, image, &format, width, height, 1 );
+
+       // Set the frame back
+       mlt_properties_set_int( b_props, "resize_alpha", resize_alpha );
+
+       return ret && image != NULL;
+}
+
+static void crop_calculate( mlt_transition this, mlt_properties properties, struct geometry_s *result, double position )
+{
+       // Initialize panning info
+       result->x_src = 0;
+       result->y_src = 0;
+       if ( mlt_properties_get( properties, "crop" ) )
+       {
+               mlt_geometry crop = mlt_properties_get_data( properties, "crop_geometry", NULL );
+               if ( !crop )
+               {
+                       crop = mlt_geometry_init();
+                       mlt_position in = mlt_transition_get_in( this );
+                       mlt_position out = mlt_transition_get_out( this );
+                       int length = out - in + 1;
+                       double cycle = mlt_properties_get_double( properties, "cycle" );
+
+                       // Allow a geometry repeat cycle
+                       if ( cycle >= 1 )
+                               length = cycle;
+                       else if ( cycle > 0 )
+                               length *= cycle;
+                       mlt_geometry_parse( crop, mlt_properties_get( properties, "crop" ), length, result->sw, result->sh );
+                       mlt_properties_set_data( properties, "crop_geometry", crop, 0, (mlt_destructor)mlt_geometry_close, NULL );
+               }
+
+               // Repeat processing
+               int length = mlt_geometry_get_length( crop );
+               int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+               int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+               if ( !repeat_off && position >= length && length != 0 )
+               {
+                       int section = position / length;
+                       position -= section * length;
+                       if ( !mirror_off && section % 2 == 1 )
+                               position = length - position;
+               }
+
+               // Compute the pan
+               struct mlt_geometry_item_s crop_item;
+               mlt_geometry_fetch( crop, &crop_item, position );
+               result->x_src = rint( crop_item.x );
+               result->y_src = rint( crop_item.y );
+       }
+}
+
+static mlt_geometry composite_calculate( mlt_transition this, struct geometry_s *result, mlt_frame a_frame, double position )
+{
+       // Get the properties from the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // Get the properties from the frame
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+       
+       // Structures for geometry
+       mlt_geometry start = mlt_properties_get_data( properties, "geometries", NULL );
+
+       // Obtain the normalised width and height from the a_frame
+       int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
+       int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
+
+       char *name = mlt_properties_get( properties, "_unique_id" );
+       char key[ 256 ];
+
+       sprintf( key, "%s.in", name );
+       if ( mlt_properties_get( a_props, key ) )
+       {
+               sscanf( mlt_properties_get( a_props, key ), "%f,%f,%f,%f,%f,%d,%d", &result->item.x, &result->item.y, &result->item.w, &result->item.h, &result->item.mix, &result->nw, &result->nh );
+       }
+       else
+       {
+               // Now parse the geometries
+               if ( start == NULL )
+               {
+                       // Parse the transitions properties
+                       start = transition_parse_keys( this, normalised_width, normalised_height );
+
+                       // Assign to properties to ensure we get destroyed
+                       mlt_properties_set_data( properties, "geometries", start, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+               }
+               else
+               {
+                       int length = mlt_transition_get_out( this ) - mlt_transition_get_in( this ) + 1;
+                       double cycle = mlt_properties_get_double( properties, "cycle" );
+                       if ( cycle > 1 )
+                               length = cycle;
+                       else if ( cycle > 0 )
+                               length *= cycle;
+                       mlt_geometry_refresh( start, mlt_properties_get( properties, "geometry" ), length, normalised_width, normalised_height );
+               }
+
+               // Do the calculation
+               geometry_calculate( this, result, position );
+
+               // Assign normalised info
+               result->nw = normalised_width;
+               result->nh = normalised_height;
+       }
+
+       // Now parse the alignment
+       result->halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
+       result->valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
+
+       crop_calculate( this, properties, result, position );
+
+       return start;
+}
+
+mlt_frame composite_copy_region( mlt_transition this, mlt_frame a_frame, mlt_position frame_position )
+{
+       // Create a frame to return
+       mlt_frame b_frame = mlt_frame_init( MLT_TRANSITION_SERVICE( this ) );
+
+       // Get the properties of the a frame
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+       // Get the properties of the b frame
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+       // Get the position
+       int position = position_calculate( this, frame_position );
+
+       // Get the unique id of the transition
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+       char key[ 256 ];
+
+       // Destination image
+       uint8_t *dest = NULL;
+
+       // Get the image and dimensions
+       uint8_t *image = mlt_properties_get_data( a_props, "image", NULL );
+       int width = mlt_properties_get_int( a_props, "width" );
+       int height = mlt_properties_get_int( a_props, "height" );
+       int format = mlt_properties_get_int( a_props, "format" );
+
+       // Pointers for copy operation
+       uint8_t *p;
+
+       // Coordinates
+       int w = 0;
+       int h = 0;
+       int x = 0;
+       int y = 0;
+
+       int ss = 0;
+       int ds = 0;
+
+       // Will need to know region to copy
+       struct geometry_s result;
+
+       // Calculate the region now
+       composite_calculate( this, &result, a_frame, position );
+
+       // Need to scale down to actual dimensions
+       x = rint( result.item.x * width / result.nw );
+       y = rint( result.item.y * height / result.nh );
+       w = rint( result.item.w * width / result.nw );
+       h = rint( result.item.h * height / result.nh );
+
+       if ( x % 2 )
+       {
+               x --;
+               w ++;
+       }
+
+       // Store the key
+       sprintf( key, "%s.in=%d,%d,%d,%d,%f,%d,%d", name, x, y, w, h, result.item.mix, width, height );
+       mlt_properties_parse( a_props, key );
+       sprintf( key, "%s.out=%d,%d,%d,%d,%f,%d,%d", name, x, y, w, h, result.item.mix, width, height );
+       mlt_properties_parse( a_props, key );
+
+       ds = w * 2;
+       ss = width * 2;
+
+       // Now we need to create a new destination image
+       dest = mlt_pool_alloc( w * h * 2 );
+
+       // Assign to the new frame
+       mlt_properties_set_data( b_props, "image", dest, w * h * 2, mlt_pool_release, NULL );
+       mlt_properties_set_int( b_props, "width", w );
+       mlt_properties_set_int( b_props, "height", h );
+       mlt_properties_set_int( b_props, "format", format );
+
+       if ( y < 0 )
+       {
+               dest += ( ds * -y );
+               h += y;
+               y = 0;
+       }
+
+       if ( y + h > height )
+               h -= ( y + h - height );
+
+       if ( x < 0 )
+       {
+               dest += -x * 2;
+               w += x;
+               x = 0;
+       }
+
+       if ( w > 0 && h > 0 )
+       {
+               // Copy the region of the image
+               p = image + y * ss + x * 2;
+
+               while ( h -- )
+               {
+                       memcpy( dest, p, w * 2 );
+                       dest += ds;
+                       p += ss;
+               }
+       }
+
+       // Assign this position to the b frame
+       mlt_frame_set_position( b_frame, frame_position );
+       mlt_properties_set_int( b_props, "distort", 1 );
+
+       // Return the frame
+       return b_frame;
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the b frame from the stack
+       mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+       // Get the transition from the a frame
+       mlt_transition this = mlt_frame_pop_service( a_frame );
+
+       // Get in and out
+       double position = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( a_frame ) );
+       int out = mlt_frame_pop_service_int( a_frame );
+       int in = mlt_frame_pop_service_int( a_frame );
+
+       // Get the properties from the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // TODO: clean up always_active behaviour
+       if ( mlt_properties_get_int( properties, "always_active" ) )
+       {
+               mlt_events_block( properties, properties );
+               mlt_properties_set_int( properties, "in", in );
+               mlt_properties_set_int( properties, "out", out );
+               mlt_events_unblock( properties, properties );
+       }
+
+       // This compositer is yuv422 only
+       *format = mlt_image_yuv422;
+
+       if ( b_frame != NULL )
+       {
+               // Get the properties of the a frame
+               mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+               // Get the properties of the b frame
+               mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+               // Structures for geometry
+               struct geometry_s result;
+
+               // Calculate the position
+               double delta = delta_calculate( this, a_frame, position );
+
+               // Get the image from the b frame
+               uint8_t *image_b = NULL;
+               int width_b = *width;
+               int height_b = *height;
+       
+               // Vars for alphas
+               uint8_t *alpha_a = NULL;
+               uint8_t *alpha_b = NULL;
+
+               // Composites always need scaling... defaulting to lowest
+               const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
+               if ( rescale == NULL || !strcmp( rescale, "none" ) )
+                       rescale = "nearest";
+               mlt_properties_set( a_props, "rescale.interp", rescale );
+               mlt_properties_set( b_props, "rescale.interp", rescale );
+
+               // Do the calculation
+               // NB: Locks needed here since the properties are being modified
+               mlt_service_lock( MLT_TRANSITION_SERVICE( this ) );
+               composite_calculate( this, &result, a_frame, position );
+               mlt_service_unlock( MLT_TRANSITION_SERVICE( this ) );
+
+               // Since we are the consumer of the b_frame, we must pass along these
+               // consumer properties from the a_frame
+               mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) );
+               mlt_properties_set( b_props, "consumer_deinterlace_method", mlt_properties_get( a_props, "consumer_deinterlace_method" ) );
+               mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+               // TODO: Dangerous/temporary optimisation - if nothing to do, then do nothing
+               if ( mlt_properties_get_int( properties, "no_alpha" ) && 
+                        result.item.x == 0 && result.item.y == 0 && result.item.w == *width && result.item.h == *height && result.item.mix == 100 )
+               {
+                       mlt_frame_get_image( b_frame, image, format, width, height, 1 );
+                       if ( !mlt_frame_is_test_card( a_frame ) )
+                               mlt_frame_replace_image( a_frame, *image, *format, *width, *height );
+                       return 0;
+               }
+
+               if ( a_frame == b_frame )
+               {
+                       double aspect_ratio = mlt_frame_get_aspect_ratio( b_frame );
+                       get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result );
+                       alpha_b = mlt_frame_get_alpha_mask( b_frame );
+                       mlt_properties_set_double( a_props, "aspect_ratio", aspect_ratio );
+               }
+
+               // Get the image from the a frame
+               mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+               alpha_a = mlt_frame_get_alpha_mask( a_frame );
+
+               // Optimisation - no compositing required
+               if ( result.item.mix == 0 || ( result.item.w == 0 && result.item.h == 0 ) )
+                       return 0;
+
+               // Need to keep the width/height of the a_frame on the b_frame for titling
+               if ( mlt_properties_get( a_props, "dest_width" ) == NULL )
+               {
+                       mlt_properties_set_int( a_props, "dest_width", *width );
+                       mlt_properties_set_int( a_props, "dest_height", *height );
+                       mlt_properties_set_int( b_props, "dest_width", *width );
+                       mlt_properties_set_int( b_props, "dest_height", *height );
+               }
+               else
+               {
+                       mlt_properties_set_int( b_props, "dest_width", mlt_properties_get_int( a_props, "dest_width" ) );
+                       mlt_properties_set_int( b_props, "dest_height", mlt_properties_get_int( a_props, "dest_height" ) );
+               }
+
+               // Special case for titling...
+               if ( mlt_properties_get_int( properties, "titles" ) )
+               {
+                       if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL )
+                               mlt_properties_set( b_props, "rescale.interp", "hyper" );
+                       width_b = mlt_properties_get_int( a_props, "dest_width" );
+                       height_b = mlt_properties_get_int( a_props, "dest_height" );
+               }
+
+               if ( *image != image_b && ( image_b != NULL || get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result ) == 0 ) )
+               {
+                       uint8_t *dest = *image;
+                       uint8_t *src = image_b;
+                       int progressive = 
+                                       mlt_properties_get_int( a_props, "consumer_deinterlace" ) ||
+                                       mlt_properties_get_int( properties, "progressive" );
+                       int field;
+                       
+                       int32_t luma_softness = mlt_properties_get_double( properties, "softness" ) * ( 1 << 16 );
+                       uint16_t *luma_bitmap = get_luma( this, properties, width_b, height_b );
+                       char *operator = mlt_properties_get( properties, "operator" );
+
+                       alpha_b = alpha_b == NULL ? mlt_frame_get_alpha_mask( b_frame ) : alpha_b;
+
+                       composite_line_fn line_fn = composite_line_yuv;
+
+                       // Replacement and override
+                       if ( operator != NULL )
+                       {
+                               if ( !strcmp( operator, "or" ) )
+                                       line_fn = composite_line_yuv_or;
+                               if ( !strcmp( operator, "and" ) )
+                                       line_fn = composite_line_yuv_and;
+                               if ( !strcmp( operator, "xor" ) )
+                                       line_fn = composite_line_yuv_xor;
+                       }
+
+                       // Allow the user to completely obliterate the alpha channels from both frames
+                       if ( mlt_properties_get( properties, "alpha_a" ) )
+                               memset( alpha_a, mlt_properties_get_int( properties, "alpha_a" ), *width * *height );
+
+                       if ( mlt_properties_get( properties, "alpha_b" ) )
+                               memset( alpha_b, mlt_properties_get_int( properties, "alpha_b" ), width_b * height_b );
+
+                       for ( field = 0; field < ( progressive ? 1 : 2 ); field++ )
+                       {
+                               // Assume lower field (0) first
+                               double field_position = position + field * delta;
+                               
+                               // Do the calculation if we need to
+                               // NB: Locks needed here since the properties are being modified
+                               mlt_service_lock( MLT_TRANSITION_SERVICE( this ) );
+                               composite_calculate( this, &result, a_frame, field_position );
+                               mlt_service_unlock( MLT_TRANSITION_SERVICE( this ) );
+
+                               if ( mlt_properties_get_int( properties, "titles" ) )
+                               {
+                                       result.item.w = rint( *width * ( result.item.w / result.nw ) );
+                                       result.nw = result.item.w;
+                                       result.item.h = rint( *height * ( result.item.h / result.nh ) );
+                                       result.nh = *height;
+                                       result.sw = width_b;
+                                       result.sh = height_b;
+                               }
+
+                               // Enforce cropping
+                               if ( mlt_properties_get( properties, "crop" ) )
+                               {
+                                       if ( result.x_src == 0 )
+                                               width_b = width_b > result.item.w ? result.item.w : width_b;
+                                       if ( result.y_src == 0 )
+                                               height_b = height_b > result.item.h ? result.item.h : height_b;
+                               }
+                               else
+                               {
+                                       // Otherwise, align
+                                       alignment_calculate( &result );
+                               }
+
+                               // Composite the b_frame on the a_frame
+                               composite_yuv( dest, *width, *height, src, width_b, height_b, alpha_b, alpha_a, result, progressive ? -1 : field, luma_bitmap, luma_softness, line_fn );
+                       }
+               }
+       }
+       else
+       {
+               mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+       }
+
+       return 0;
+}
+
+/** Composition transition processing.
+*/
+
+static mlt_frame composite_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+       // UGH - this is a TODO - find a more reliable means of obtaining in/out for the always_active case
+       if ( mlt_properties_get_int(  MLT_TRANSITION_PROPERTIES( this ), "always_active" ) == 0 )
+       {
+               mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "in" ) );
+               mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( this ), "out" ) );
+               mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( a_frame ), position_calculate( this, mlt_frame_get_position( a_frame ) ) );
+       }
+       else
+       {
+               mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL );
+               mlt_frame_push_service_int( a_frame, mlt_properties_get_int( props, "in" ) );
+               mlt_frame_push_service_int( a_frame, mlt_properties_get_int( props, "out" ) );
+               mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( a_frame ), mlt_properties_get_int( props, "_frame" ) - mlt_properties_get_int( props, "in" ) );
+       }
+       
+       mlt_frame_push_service( a_frame, this );
+       mlt_frame_push_frame( a_frame, b_frame );
+       mlt_frame_push_get_image( a_frame, transition_get_image );
+       return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_composite_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_transition this = calloc( sizeof( struct mlt_transition_s ), 1 );
+       if ( this != NULL && mlt_transition_init( this, NULL ) == 0 )
+       {
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+               
+               this->process = composite_process;
+               
+               // Default starting motion and zoom
+               mlt_properties_set( properties, "start", arg != NULL ? arg : "0,0:100%x100%" );
+               
+               // Default factory
+               mlt_properties_set( properties, "factory", "fezzik" );
+
+               // Use alignment (and hence alpha of b frame)
+               mlt_properties_set_int( properties, "aligned", 1 );
+
+               // Inform apps and framework that this is a video only transition
+               mlt_properties_set_int( properties, "_transition_type", 1 );
+       }
+       return this;
+}
diff --git a/src/modules/core/transition_composite.h b/src/modules/core/transition_composite.h
new file mode 100644 (file)
index 0000000..6523b95
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * transition_composite.h -- compose one image over another using alpha channel
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 _TRANSITION_COMPOSITE_H_
+#define _TRANSITION_COMPOSITE_H_
+
+#include <framework/mlt_transition.h>
+
+extern mlt_transition transition_composite_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+// Courtesy functionality - allows regionalised filtering
+extern mlt_frame composite_copy_region( mlt_transition, mlt_frame, mlt_position );
+
+#endif
diff --git a/src/modules/core/transition_luma.c b/src/modules/core/transition_luma.c
new file mode 100644 (file)
index 0000000..f08d84f
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * transition_luma.c -- a generic dissolve/wipe processor
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * Adapted from Kino Plugin Timfx, which is
+ * Copyright (C) 2002 Timothy M. Shead <tshead@k-3d.com>
+ *
+ * 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 <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+/** Calculate the position for this frame.
+*/
+
+static float position_calculate( mlt_transition this, mlt_frame frame )
+{
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+       mlt_position out = mlt_transition_get_out( this );
+
+       // Get the position of the frame
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+       mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+       // Now do the calcs
+       return ( float )( position - in ) / ( float )( out - in + 1 );
+}
+
+/** Calculate the field delta for this frame - position between two frames.
+*/
+
+static float delta_calculate( mlt_transition this, mlt_frame frame )
+{
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+       mlt_position out = mlt_transition_get_out( this );
+
+       // Get the position of the frame
+       mlt_position position = mlt_frame_get_position( frame );
+
+       // Now do the calcs
+       float x = ( float )( position - in ) / ( float )( out - in + 1 );
+       float y = ( float )( position + 1 - in ) / ( float )( out - in + 1 );
+
+       return ( y - x ) / 2.0;
+}
+
+static inline int dissolve_yuv( mlt_frame this, mlt_frame that, float weight, int width, int height )
+{
+       int ret = 0;
+       int width_src = width, height_src = height;
+       mlt_image_format format = mlt_image_yuv422;
+       uint8_t *p_src, *p_dest;
+       uint8_t *p, *q;
+       uint8_t *limit;
+       uint8_t *alpha_src;
+       uint8_t *alpha_dst;
+
+       int32_t weigh = weight * ( 1 << 16 );
+       int32_t weigh_complement = ( 1 - weight ) * ( 1 << 16 );
+
+       if ( mlt_properties_get( &this->parent, "distort" ) )
+               mlt_properties_set( &that->parent, "distort", mlt_properties_get( &this->parent, "distort" ) );
+       mlt_properties_set_int( &that->parent, "consumer_deinterlace", mlt_properties_get_int( &this->parent, "consumer_deinterlace" ) );
+       mlt_frame_get_image( this, &p_dest, &format, &width, &height, 1 );
+       alpha_dst = mlt_frame_get_alpha_mask( this );
+       mlt_frame_get_image( that, &p_src, &format, &width_src, &height_src, 0 );
+       alpha_src = mlt_frame_get_alpha_mask( that );
+
+       // Pick the lesser of two evils ;-)
+       width_src = width_src > width ? width : width_src;
+       height_src = height_src > height ? height : height_src;
+       
+       p = p_dest;
+       q = alpha_dst;
+       limit = p_dest + height_src * width_src * 2;
+
+       while ( p < limit )
+       {
+               *p_dest++ = ( *p_src++ * weigh + *p++ * weigh_complement ) >> 16;
+               *p_dest++ = ( *p_src++ * weigh + *p++ * weigh_complement ) >> 16;
+               *alpha_dst++ = ( *alpha_src++ * weigh + *q++ * weigh_complement ) >> 16;
+       }
+
+       return ret;
+}
+
+// image processing functions
+
+static inline int32_t smoothstep( int32_t edge1, int32_t edge2, uint32_t a )
+{
+       if ( a < edge1 )
+               return 0;
+
+       if ( a >= edge2 )
+               return 0x10000;
+
+       a = ( ( a - edge1 ) << 16 ) / ( edge2 - edge1 );
+
+       return ( ( ( a * a ) >> 16 )  * ( ( 3 << 16 ) - ( 2 * a ) ) ) >> 16;
+}
+
+/** powerful stuff
+
+    \param field_order -1 = progressive, 0 = lower field first, 1 = top field first
+*/
+static void luma_composite( mlt_frame a_frame, mlt_frame b_frame, int luma_width, int luma_height,
+                                                       uint16_t *luma_bitmap, float pos, float frame_delta, float softness, int field_order,
+                                                       int *width, int *height )
+{
+       int width_src = *width, height_src = *height;
+       int width_dest = *width, height_dest = *height;
+       mlt_image_format format_src = mlt_image_yuv422, format_dest = mlt_image_yuv422;
+       uint8_t *p_src, *p_dest;
+       int i, j;
+       int stride_src;
+       int stride_dest;
+       uint16_t weight = 0;
+
+       format_src = mlt_image_yuv422;
+       format_dest = mlt_image_yuv422;
+
+       if ( mlt_properties_get( &a_frame->parent, "distort" ) )
+               mlt_properties_set( &b_frame->parent, "distort", mlt_properties_get( &a_frame->parent, "distort" ) );
+       mlt_properties_set_int( &b_frame->parent, "consumer_deinterlace", mlt_properties_get_int( &a_frame->parent, "consumer_deinterlace" ) );
+       mlt_frame_get_image( a_frame, &p_dest, &format_dest, &width_dest, &height_dest, 1 );
+       mlt_frame_get_image( b_frame, &p_src, &format_src, &width_src, &height_src, 0 );
+
+       if ( *width == 0 || *height == 0 )
+               return;
+
+       // Pick the lesser of two evils ;-)
+       width_src = width_src > width_dest ? width_dest : width_src;
+       height_src = height_src > height_dest ? height_dest : height_src;
+       
+       stride_src = width_src * 2;
+       stride_dest = width_dest * 2;
+
+       // Offset the position based on which field we're looking at ...
+       int32_t field_pos[ 2 ];
+       field_pos[ 0 ] = ( pos + ( ( field_order == 0 ? 1 : 0 ) * frame_delta * 0.5 ) ) * ( 1 << 16 ) * ( 1.0 + softness );
+       field_pos[ 1 ] = ( pos + ( ( field_order == 0 ? 0 : 1 ) * frame_delta * 0.5 ) ) * ( 1 << 16 ) * ( 1.0 + softness );
+
+       register uint8_t *p;
+       register uint8_t *q;
+       register uint8_t *o;
+       uint16_t  *l;
+
+       uint32_t value;
+
+       int32_t x_diff = ( luma_width << 16 ) / *width;
+       int32_t y_diff = ( luma_height << 16 ) / *height;
+       int32_t x_offset = 0;
+       int32_t y_offset = 0;
+       uint8_t *p_row;
+       uint8_t *q_row;
+
+       int32_t i_softness = softness * ( 1 << 16 );
+
+       int field_count = field_order < 0 ? 1 : 2;
+       int field_stride_src = field_count * stride_src;
+       int field_stride_dest = field_count * stride_dest;
+       int field = 0;
+
+       // composite using luma map
+       while ( field < field_count )
+       {
+               p_row = p_src + field * stride_src;
+               q_row = p_dest + field * stride_dest;
+               y_offset = field << 16;
+               i = field;
+
+               while ( i < height_src )
+               {
+                       p = p_row;
+                       q = q_row;
+                       o = q;
+                       l = luma_bitmap + ( y_offset >> 16 ) * ( luma_width * field_count );
+                       x_offset = 0;
+                       j = width_src;
+
+                       while( j -- )
+                       {
+               weight = l[ x_offset >> 16 ];
+                               value = smoothstep( weight, i_softness + weight, field_pos[ field ] );
+                               *o ++ = ( *p ++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
+                               *o ++ = ( *p ++ * value + *q++ * ( ( 1 << 16 ) - value ) ) >> 16;
+                               x_offset += x_diff;
+                       }
+
+                       y_offset += y_diff;
+                       i += field_count;
+                       p_row += field_stride_src;
+                       q_row += field_stride_dest;
+               }
+
+               field ++;
+       }
+}
+
+/** Load the luma map from PGM stream.
+*/
+
+static void luma_read_pgm( FILE *f, uint16_t **map, int *width, int *height )
+{
+       uint8_t *data = NULL;
+       while (1)
+       {
+               char line[128];
+               char comment[128];
+               int i = 2;
+               int maxval;
+               int bpp;
+               uint16_t *p;
+
+               line[127] = '\0';
+
+               // get the magic code
+               if ( fgets( line, 127, f ) == NULL )
+                       break;
+
+               // skip comments
+               while ( sscanf( line, " #%s", comment ) > 0 )
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+               if ( line[0] != 'P' || line[1] != '5' )
+                       break;
+
+               // skip white space and see if a new line must be fetched
+               for ( i = 2; i < 127 && line[i] != '\0' && isspace( line[i] ); i++ );
+               if ( ( line[i] == '\0' || line[i] == '#' ) && fgets( line, 127, f ) == NULL )
+                       break;
+
+               // skip comments
+               while ( sscanf( line, " #%s", comment ) > 0 )
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+               // get the dimensions
+               if ( line[0] == 'P' )
+                       i = sscanf( line, "P5 %d %d %d", width, height, &maxval );
+               else
+                       i = sscanf( line, "%d %d %d", width, height, &maxval );
+
+               // get the height value, if not yet
+               if ( i < 2 )
+               {
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+                       // skip comments
+                       while ( sscanf( line, " #%s", comment ) > 0 )
+                               if ( fgets( line, 127, f ) == NULL )
+                                       break;
+
+                       i = sscanf( line, "%d", height );
+                       if ( i == 0 )
+                               break;
+                       else
+                               i = 2;
+               }
+
+               // get the maximum gray value, if not yet
+               if ( i < 3 )
+               {
+                       if ( fgets( line, 127, f ) == NULL )
+                               break;
+
+                       // skip comments
+                       while ( sscanf( line, " #%s", comment ) > 0 )
+                               if ( fgets( line, 127, f ) == NULL )
+                                       break;
+
+                       i = sscanf( line, "%d", &maxval );
+                       if ( i == 0 )
+                               break;
+               }
+
+               // determine if this is one or two bytes per pixel
+               bpp = maxval > 255 ? 2 : 1;
+
+               // allocate temporary storage for the raw data
+               data = mlt_pool_alloc( *width * *height * bpp );
+               if ( data == NULL )
+                       break;
+
+               // read the raw data
+               if ( fread( data, *width * *height * bpp, 1, f ) != 1 )
+                       break;
+
+               // allocate the luma bitmap
+               *map = p = (uint16_t*)mlt_pool_alloc( *width * *height * sizeof( uint16_t ) );
+               if ( *map == NULL )
+                       break;
+
+               // proces the raw data into the luma bitmap
+               for ( i = 0; i < *width * *height * bpp; i += bpp )
+               {
+                       if ( bpp == 1 )
+                               *p++ = data[ i ] << 8;
+                       else
+                               *p++ = ( data[ i ] << 8 ) + data[ i+1 ];
+               }
+
+               break;
+       }
+
+       if ( data != NULL )
+               mlt_pool_release( data );
+}
+
+/** Generate a luma map from an RGB image.
+*/
+
+static void luma_read_yuv422( uint8_t *image, uint16_t **map, int width, int height )
+{
+       int i;
+       int size = width * height * 2;
+       
+       // allocate the luma bitmap
+       uint16_t *p = *map = ( uint16_t* )mlt_pool_alloc( width * height * sizeof( uint16_t ) );
+       if ( *map == NULL )
+               return;
+
+       // proces the image data into the luma bitmap
+       for ( i = 0; i < size; i += 2 )
+               *p++ = ( image[ i ] - 16 ) * 299; // 299 = 65535 / 219
+}
+
+/** Generate a luma map from a YUV image.
+*/
+static void luma_read_rgb24( uint8_t *image, uint16_t **map, int width, int height )
+{
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the b frame from the stack
+       mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+       // Get the transition object
+       mlt_transition transition = mlt_frame_pop_service( a_frame );
+
+       // Get the properties of the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
+
+       // Get the properties of the a frame
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+       // Get the properties of the b frame
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+       // This compositer is yuv422 only
+       *format = mlt_image_yuv422;
+
+       // The cached luma map information
+       int luma_width = mlt_properties_get_int( properties, "width" );
+       int luma_height = mlt_properties_get_int( properties, "height" );
+       uint16_t *luma_bitmap = mlt_properties_get_data( properties, "bitmap", NULL );
+       char *current_resource = mlt_properties_get( properties, "_resource" );
+       
+       // If the filename property changed, reload the map
+       char *resource = mlt_properties_get( properties, "resource" );
+
+       // Correct width/height if not specified
+       if ( luma_width == 0 || luma_height == 0 )
+       {
+               luma_width = mlt_properties_get_int( a_props, "width" );
+               luma_height = mlt_properties_get_int( a_props, "height" );
+       }
+               
+       if ( resource != current_resource )
+       {
+               char temp[ 512 ];
+               char *extension = strrchr( resource, '.' );
+
+               if ( strchr( resource, '%' ) )
+               {
+                       FILE *test;
+                       sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+                       test = fopen( temp, "r" );
+                       if ( test == NULL )
+                               strcat( temp, ".png" );
+                       else
+                               fclose( test ); 
+                       resource = temp;
+                       extension = strrchr( resource, '.' );
+               }
+
+               // See if it is a PGM
+               if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 )
+               {
+                       // Open PGM
+                       FILE *f = fopen( resource, "r" );
+                       if ( f != NULL )
+                       {
+                               // Load from PGM
+                               luma_read_pgm( f, &luma_bitmap, &luma_width, &luma_height );
+                               fclose( f );
+
+                               // Set the transition properties
+                               mlt_properties_set_int( properties, "width", luma_width );
+                               mlt_properties_set_int( properties, "height", luma_height );
+                               mlt_properties_set( properties, "_resource", resource );
+                               mlt_properties_set_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+                       }
+               }
+               else if (!*resource) 
+               {
+                   luma_bitmap = NULL;
+                   mlt_properties_set( properties, "_resource", NULL );
+                   mlt_properties_set_data( properties, "bitmap", luma_bitmap, 0, mlt_pool_release, NULL );
+               }
+               else
+               {
+                       // Get the factory producer service
+                       char *factory = mlt_properties_get( properties, "factory" );
+
+                       // Create the producer
+                       mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
+                       mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+
+                       // If we have one
+                       if ( producer != NULL )
+                       {
+                               // Get the producer properties
+                               mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+                               // Ensure that we loop
+                               mlt_properties_set( producer_properties, "eof", "loop" );
+
+                               // Now pass all producer. properties on the transition down
+                               mlt_properties_pass( producer_properties, properties, "producer." );
+
+                               // We will get the alpha frame from the producer
+                               mlt_frame luma_frame = NULL;
+
+                               // Get the luma frame
+                               if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 )
+                               {
+                                       uint8_t *luma_image = NULL;
+                                       mlt_image_format luma_format = mlt_image_yuv422;
+
+                                       // Get image from the luma producer
+                                       mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "nearest" );
+                                       mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+
+                                       // Generate the luma map
+                                       if ( luma_image != NULL && luma_format == mlt_image_yuv422 )
+                                               luma_read_yuv422( luma_image, &luma_bitmap, luma_width, luma_height );
+                                               
+                                       else if ( luma_image != NULL && luma_format == mlt_image_rgb24 )
+                                               luma_read_rgb24( luma_image, &luma_bitmap, luma_width, luma_height );
+                                       
+                                       // Set the transition properties
+                                       mlt_properties_set_int( properties, "width", luma_width );
+                                       mlt_properties_set_int( properties, "height", luma_height );
+                                       mlt_properties_set( properties, "_resource", resource);
+                                       mlt_properties_set_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL );
+
+                                       // Cleanup the luma frame
+                                       mlt_frame_close( luma_frame );
+                               }
+
+                               // Cleanup the luma producer
+                               mlt_producer_close( producer );
+                       }
+               }
+       }
+
+       // Arbitrary composite defaults
+       float mix = position_calculate( transition, a_frame );
+       float frame_delta = delta_calculate( transition, a_frame );
+       
+       float luma_softness = mlt_properties_get_double( properties, "softness" );
+       int progressive = 
+                       mlt_properties_get_int( a_props, "consumer_deinterlace" ) ||
+                       mlt_properties_get_int( properties, "progressive" ) ||
+                       mlt_properties_get_int( b_props, "luma.progressive" );
+       int top_field_first =  mlt_properties_get_int( b_props, "top_field_first" );
+       int reverse = mlt_properties_get_int( properties, "reverse" );
+       int invert = mlt_properties_get_int( properties, "invert" );
+
+       if ( mlt_properties_get( a_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+               mlt_properties_set( a_props, "rescale.interp", "nearest" );
+
+       // Since we are the consumer of the b_frame, we must pass along this
+       // consumer property from the a_frame
+       if ( mlt_properties_get_double( a_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+       if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+       mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+       // Honour the reverse here
+       if ( mix >= 1.0 )
+               mix -= floor( mix );
+
+       mix = reverse || invert ? 1 - mix : mix;
+       frame_delta *= reverse || invert ? -1.0 : 1.0;
+
+       // Ensure we get scaling on the b_frame
+       if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( b_props, "rescale.interp" ), "none" ) )
+               mlt_properties_set( b_props, "rescale.interp", "nearest" );
+
+       if ( mlt_properties_get( properties, "fixed" ) )
+               mix = mlt_properties_get_double( properties, "fixed" );
+
+       if ( luma_width > 0 && luma_height > 0 && luma_bitmap != NULL )
+               // Composite the frames using a luma map
+               luma_composite( !invert ? a_frame : b_frame, !invert ? b_frame : a_frame, luma_width, luma_height, luma_bitmap, mix, frame_delta,
+                       luma_softness, progressive ? -1 : top_field_first, width, height );
+       else
+               // Dissolve the frames using the time offset for mix value
+               dissolve_yuv( a_frame, b_frame, mix, *width, *height );
+
+       // Extract the a_frame image info
+       *width = mlt_properties_get_int( !invert ? a_props : b_props, "width" );
+       *height = mlt_properties_get_int( !invert ? a_props : b_props, "height" );
+       *image = mlt_properties_get_data( !invert ? a_props : b_props, "image", NULL );
+
+       return 0;
+}
+
+
+/** Luma transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+       // Get a unique name to store the frame position
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+
+       // Assign the current position to the name
+       mlt_properties_set_position( MLT_FRAME_PROPERTIES( a_frame ), name, mlt_frame_get_position( a_frame ) );
+
+       // Push the transition on to the frame
+       mlt_frame_push_service( a_frame, transition );
+
+       // Push the b_frame on to the stack
+       mlt_frame_push_frame( a_frame, b_frame );
+
+       // Push the transition method
+       mlt_frame_push_get_image( a_frame, transition_get_image );
+       
+       return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *lumafile )
+{
+       mlt_transition transition = mlt_transition_new( );
+       if ( transition != NULL )
+       {
+               // Set the methods
+               transition->process = transition_process;
+               
+               // Default factory
+               mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "factory", "fezzik" );
+
+               // Set the main property
+               mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "resource", lumafile );
+               
+               // Inform apps and framework that this is a video only transition
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
+
+               return transition;
+       }
+       return NULL;
+}
diff --git a/src/modules/core/transition_mix.c b/src/modules/core/transition_mix.c
new file mode 100644 (file)
index 0000000..b2b3790
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * transition_mix.c -- mix two audio streams
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_transition.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/** Get the audio.
+*/
+
+static int transition_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the b frame from the stack
+       mlt_frame b_frame = mlt_frame_pop_audio( frame );
+
+       // Get the effect
+       mlt_transition effect = mlt_frame_pop_audio( frame );
+
+       // Get the properties of the b frame
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+       if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( effect ), "combine" ) == 0 )
+       {
+               double mix_start = 0.5, mix_end = 0.5;
+               if ( mlt_properties_get( b_props, "audio.previous_mix" ) != NULL )
+                       mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" );
+               if ( mlt_properties_get( b_props, "audio.mix" ) != NULL )
+                       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;
+               }
+
+               mlt_frame_mix_audio( frame, b_frame, mix_start, mix_end, buffer, format, frequency, channels, samples );
+       }
+       else
+       {
+               mlt_frame_combine_audio( frame, b_frame, buffer, format, frequency, channels, samples );
+       }
+
+       return 0;
+}
+
+
+/** Mix transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       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 )
+       {
+               // 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 );
+               int always_active = mlt_properties_get_int(  MLT_TRANSITION_PROPERTIES( this ), "always_active" );
+               mlt_position in = !always_active ? mlt_transition_get_in( this ) : mlt_properties_get_int( props, "in" );
+               mlt_position out = !always_active ? mlt_transition_get_out( this ) : mlt_properties_get_int( props, "out" );
+               int length = mlt_properties_get_int(  MLT_TRANSITION_PROPERTIES( this ), "length" );
+               mlt_position time = !always_active ? mlt_frame_get_position( b_frame ) : mlt_properties_get_int( props, "_frame" );
+               double mix = ( double )( time - in ) / ( double )( out - in + 1 );
+
+               // TODO: Check the logic here - shouldn't we be computing current and next mixing levels in all cases?
+               if ( length == 0 )
+               {
+                       // If there is an end mix level adjust mix to the range
+                       if ( mlt_properties_get( properties, "end" ) != NULL )
+                       {
+                               double start = mlt_properties_get_double( properties, "start" );
+                               double end = mlt_properties_get_double( properties, "end" );
+                               mix = start + ( end - start ) * mix;
+                       }
+                       // A negative means total crossfade (uses position)
+                       else if ( mlt_properties_get_double( properties, "start" ) >= 0 )
+                       {
+                               // Otherwise, start/constructor is a constant mix level
+                       mix = mlt_properties_get_double( properties, "start" );
+                       }
+               
+                       // Finally, set the mix property on the frame
+                       mlt_properties_set_double( b_props, "audio.mix", mix );
+       
+                       // Initialise transition previous mix value to prevent an inadvertant jump from 0
+                       mlt_position last_position = mlt_properties_get_position( properties, "_last_position" );
+                       mlt_position current_position = mlt_frame_get_position( b_frame );
+                       if ( mlt_properties_get( properties, "_previous_mix" ) == NULL
+                            || current_position != last_position + 1 )
+                               mlt_properties_set_double( properties, "_previous_mix", mix );
+                               
+                       // Tell b frame what the previous mix level was
+                       mlt_properties_set_double( b_props, "audio.previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) );
+
+                       // Save the current mix level for the next iteration
+                       mlt_properties_set_double( properties, "_previous_mix", mlt_properties_get_double( b_props, "audio.mix" ) );
+               
+                       mlt_properties_set_double( b_props, "audio.reverse", mlt_properties_get_double( properties, "reverse" ) );
+               }
+               else
+               {
+                       double level = mlt_properties_get_double( properties, "start" );
+                       double mix_start = level;
+                       double mix_end = mix_start;
+                       double mix_increment = 1.0 / length;
+                       if ( time - in < length )
+                       {
+                               mix_start = mix_start * ( ( double )( time - in ) / length );
+                               mix_end = mix_start + mix_increment;
+                       }
+                       else if ( time > out - length )
+                       {
+                               mix_end = mix_start * ( ( double )( out - time - in ) / length );
+                               mix_start = mix_end - mix_increment;
+                       }
+
+                       mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start;
+                       mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end;
+                       mlt_properties_set_double( b_props, "audio.previous_mix", mix_start );
+                       mlt_properties_set_double( b_props, "audio.mix", mix_end );
+               }
+       }
+
+       // Override the get_audio method
+       mlt_frame_push_audio( a_frame, this );
+       mlt_frame_push_audio( a_frame, b_frame );
+       mlt_frame_push_audio( a_frame, transition_get_audio );
+       
+       return a_frame;
+}
+
+/** Constructor for the transition.
+*/
+
+mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_transition this = calloc( sizeof( struct mlt_transition_s ), 1 );
+       if ( this != NULL && mlt_transition_init( this, NULL ) == 0 )
+       {
+               this->process = transition_process;
+               if ( arg != NULL )
+                       mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( this ), "start", atof( arg ) );
+               // Inform apps and framework that this is an audio only transition
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( this ), "_transition_type", 2 );
+       }
+       return this;
+}
+
diff --git a/src/modules/core/transition_region.c b/src/modules/core/transition_region.c
new file mode 100644 (file)
index 0000000..52bcb0f
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * transition_region.c -- region transition
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "transition_region.h"
+#include "transition_composite.h"
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int create_instance( mlt_transition this, char *name, char *value, int count )
+{
+       // Return from this function
+       int error = 0;
+
+       // Duplicate the value
+       char *type = strdup( value );
+
+       // Pointer to filter argument
+       char *arg = type == NULL ? NULL : strchr( type, ':' );
+
+       // New filter being created
+       mlt_filter filter = NULL;
+
+       // Cleanup type and arg
+       if ( arg != NULL )
+               *arg ++ = '\0';
+
+       // Create the filter
+       mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+       filter = mlt_factory_filter( profile, type, arg );
+
+       // If we have a filter, then initialise and store it
+       if ( filter != NULL )
+       {
+               // Properties of this
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+               // String to hold the property name
+               char id[ 256 ];
+
+               // String to hold the passdown key
+               char key[ 256 ];
+
+               // Construct id
+               sprintf( id, "_filter_%d", count );
+
+               // Counstruct key
+               sprintf( key, "%s.", name );
+
+               // Just in case, let's assume that the filter here has a composite
+               //mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "composite.geometry", "0%,0%:100%x100%" );
+               //mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "composite.fill", 1 );
+
+               // Pass all the key properties on the filter down
+               mlt_properties_pass( MLT_FILTER_PROPERTIES( filter ), properties, key );
+
+               // Ensure that filter is assigned
+               mlt_properties_set_data( properties, id, filter, 0, ( mlt_destructor )mlt_filter_close, NULL );
+       }
+       else
+       {
+               // Indicate that an error has occurred
+               error = 1;
+       }
+
+       // Cleanup
+       free( type );
+
+       // Return error condition
+       return error;
+}
+
+static uint8_t *filter_get_alpha_mask( mlt_frame this )
+{
+       uint8_t *alpha = NULL;
+
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the shape frame
+       mlt_frame shape_frame = mlt_properties_get_data( properties, "shape_frame", NULL );
+
+       // Get the width and height of the image
+       int region_width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "width" );
+       int region_height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "height" );
+       uint8_t *image = NULL;
+       mlt_image_format format = mlt_image_yuv422;
+                                       
+       // Get the shape image to trigger alpha creation
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( shape_frame ), "distort", 1 );
+       mlt_frame_get_image( shape_frame, &image, &format, &region_width, &region_height, 0 );
+
+       alpha = mlt_frame_get_alpha_mask( shape_frame );
+
+       // Generate from the Y component of the image if no alpha available
+       if ( alpha == NULL )
+       {
+               int size = region_width * region_height;
+               uint8_t *p = mlt_pool_alloc( size );
+               alpha = p;
+               while ( size -- )
+               {
+                       *p ++ = ( int )( ( ( *image ++ - 16 ) * 299 ) / 255 );
+                       image ++;
+               }
+               mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", alpha, region_width * region_height, mlt_pool_release, NULL );
+       }
+       else
+       {
+               mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "alpha", alpha, region_width * region_height, NULL, NULL );
+       }
+
+       this->get_alpha_mask = NULL;
+
+       return alpha;
+}
+
+/** Do it :-).
+*/
+
+static int transition_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Error we will return
+       int error = 0;
+
+       // We will get the 'b frame' from the frame stack
+       mlt_frame b_frame = mlt_frame_pop_frame( frame );
+
+       // Get the watermark transition object
+       mlt_transition this = mlt_frame_pop_service( frame );
+
+       // Get the properties of the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // Get the composite from the transition
+       mlt_transition composite = mlt_properties_get_data( properties, "composite", NULL );
+
+       // Look for the first filter
+       mlt_filter filter = mlt_properties_get_data( properties, "_filter_0", NULL );
+
+       // Get the unique id of the filter (used to reacquire the producer position)
+       char *name = mlt_properties_get( properties, "_unique_id" );
+
+       // Get the original producer position
+       mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
+
+       // Create a composite if we don't have one
+       if ( composite == NULL )
+       {
+               // Create composite via the factory
+               mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+               composite = mlt_factory_transition( profile, "composite", NULL );
+
+               // If we have one
+               if ( composite != NULL )
+               {
+                       // Get the properties
+                       mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+
+                       // We want to ensure that we don't get a wobble...
+                       //mlt_properties_set_int( composite_properties, "distort", 1 );
+                       mlt_properties_set_int( composite_properties, "progressive", 1 );
+
+                       // Pass all the composite. properties on the transition down
+                       mlt_properties_pass( composite_properties, properties, "composite." );
+
+                       // Register the composite for reuse/destruction
+                       mlt_properties_set_data( properties, "composite", composite, 0, ( mlt_destructor )mlt_transition_close, NULL );
+               }
+       }
+       else
+       {
+               // Pass all current properties down
+               mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
+               mlt_properties_pass( composite_properties, properties, "composite." );
+       }
+
+       // Create filters
+       if ( filter == NULL )
+       {
+               // Loop Variable
+               int i = 0;
+
+               // Number of filters created
+               int count = 0;
+
+               // Loop for all properties
+               for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+               {
+                       // Get the name of this property
+                       char *name = mlt_properties_get_name( properties, i );
+
+                       // If the name does not contain a . and matches filter
+                       if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
+                       {
+                               // Get the filter constructor
+                               char *value = mlt_properties_get_value( properties, i );
+
+                               // Create an instance
+                               if ( create_instance( this, name, value, count ) == 0 )
+                                       count ++;
+                       }
+               }
+       
+               // Look for the first filter again
+               filter = mlt_properties_get_data( properties, "_filter_0", NULL );
+       }
+       else
+       {
+               // Pass all properties down
+               mlt_filter temp = NULL;
+
+               // Loop Variable
+               int i = 0;
+
+               // Number of filters found
+               int count = 0;
+
+               // Loop for all properties
+               for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+               {
+                       // Get the name of this property
+                       char *name = mlt_properties_get_name( properties, i );
+
+                       // If the name does not contain a . and matches filter
+                       if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
+                       {
+                               // Strings to hold the id and pass down key
+                               char id[ 256 ];
+                               char key[ 256 ];
+
+                               // Construct id and key
+                               sprintf( id, "_filter_%d", count );
+                               sprintf( key, "%s.", name );
+
+                               // Get the filter
+                               temp = mlt_properties_get_data( properties, id, NULL );
+
+                               if ( temp != NULL )
+                               {
+                                       mlt_properties_pass( MLT_FILTER_PROPERTIES( temp ), properties, key );
+                                       count ++;
+                               }
+                       }
+               }
+       }
+
+       // Get the image
+       error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       // Only continue if we have both filter and composite
+       if ( composite != NULL )
+       {
+               // Get the resource of this filter (could be a shape [rectangle/circle] or an alpha provider of choice
+               const char *resource =  mlt_properties_get( properties, "resource" );
+
+               // Get the old resource in case it's changed
+               char *old_resource =  mlt_properties_get( properties, "_old_resource" );
+
+               // String to hold the filter to query on
+               char id[ 256 ];
+
+               // Index to hold the count
+               int i = 0;
+
+               // We will get the 'b frame' from the composite only if it's NULL
+               if ( b_frame == NULL )
+               {
+                       // Copy the region
+                       b_frame = composite_copy_region( composite, frame, position );
+
+                       // Ensure a destructor
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), name, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+               }
+
+               // Set the position of the b_frame
+               mlt_frame_set_position( b_frame, position );
+
+               // Make sure the filter is in the correct position
+               while ( filter != NULL )
+               {
+                       // Stack this filter
+                       if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "off" ) == 0 )
+                               mlt_filter_process( filter, b_frame );
+
+                       // Generate the key for the next
+                       sprintf( id, "_filter_%d", ++ i );
+
+                       // Get the next filter
+                       filter = mlt_properties_get_data( properties, id, NULL );
+               }
+
+               // Allow filters to be attached to a region filter
+               filter = mlt_properties_get_data( properties, "_region_filter", NULL );
+               if ( filter != NULL )
+                       mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 );
+
+               // Hmm - this is probably going to go wrong....
+               mlt_frame_set_position( frame, position );
+
+               // Get the b frame and process with composite if successful
+               mlt_transition_process( composite, frame, b_frame );
+
+               // If we have a shape producer copy the alpha mask from the shape frame to the b_frame
+               if ( strcmp( resource, "rectangle" ) != 0 )
+               {
+                       // Get the producer from the transition
+                       mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+                       // If We have no producer then create one
+                       if ( producer == NULL || ( old_resource != NULL && strcmp( resource, old_resource ) ) )
+                       {
+                               // Get the factory producer service
+                               char *factory = mlt_properties_get( properties, "factory" );
+
+                               // Store the old resource
+                               mlt_properties_set( properties, "_old_resource", resource );
+
+                               // Special case circle resource
+                               if ( strcmp( resource, "circle" ) == 0 )
+                                       resource = "pixbuf:<svg width='100' height='100'><circle cx='50' cy='50' r='50' fill='black'/></svg>";
+
+                               // Create the producer
+                               mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( this ) );
+                               producer = mlt_factory_producer( profile, factory, resource );
+
+                               // If we have one
+                               if ( producer != NULL )
+                               {
+                                       // Get the producer properties
+                                       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+                                       // Ensure that we loop
+                                       mlt_properties_set( producer_properties, "eof", "loop" );
+
+                                       // Now pass all producer. properties on the transition down
+                                       mlt_properties_pass( producer_properties, properties, "producer." );
+
+                                       // Register the producer for reuse/destruction
+                                       mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+                               }
+                       }
+
+                       // Now use the shape producer
+                       if ( producer != NULL )
+                       {
+                               // We will get the alpha frame from the producer
+                               mlt_frame shape_frame = NULL;
+
+                               // Make sure the producer is in the correct position
+                               mlt_producer_seek( producer, position );
+
+                               // Get the shape frame
+                               if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &shape_frame, 0 ) == 0 )
+                               {
+                                       // Ensure that the shape frame will be closed
+                                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( b_frame ), "shape_frame", shape_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+
+                                       // Specify the callback for evaluation
+                                       b_frame->get_alpha_mask = filter_get_alpha_mask;
+                               }
+                       }
+               }
+
+               // Get the image
+               error = mlt_frame_get_image( frame, image, format, width, height, 0 );
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
+
+       // Get a unique name to store the frame position
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
+
+       // Assign the current position to the name
+       mlt_properties_set_position( properties, name, mlt_frame_get_position( a_frame ) );
+
+       // Push the transition on to the frame
+       mlt_frame_push_service( a_frame, this );
+
+       // Push the b_frame on to the stack
+       mlt_frame_push_frame( a_frame, b_frame );
+
+       // Push the transition method
+       mlt_frame_push_get_image( a_frame, transition_get_image );
+
+       // Return the frame
+       return a_frame;
+}
+
+/** Constructor for the transition.
+*/
+
+mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create a new transition
+       mlt_transition this = mlt_transition_new( );
+
+       // Further initialisation
+       if ( this != NULL )
+       {
+               // Get the properties from the transition
+               mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+               // Assign the transition process method
+               this->process = transition_process;
+
+               // Default factory
+               mlt_properties_set( properties, "factory", "fezzik" );
+               
+               // Resource defines the shape of the region
+               mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg );
+
+               // Inform apps and framework that this is a video only transition
+               mlt_properties_set_int( properties, "_transition_type", 1 );
+       }
+
+       // Return the transition
+       return this;
+}
+
diff --git a/src/modules/core/transition_region.h b/src/modules/core/transition_region.h
new file mode 100644 (file)
index 0000000..f08ad0e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * transition_region.h -- region transition
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _TRANSITION_REGION_H_
+#define _TRANSITION_REGION_H_
+
+#include <framework/mlt_transition.h>
+
+extern mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#endif
diff --git a/src/modules/data_fx.properties b/src/modules/data_fx.properties
new file mode 100644 (file)
index 0000000..f7dc2e9
--- /dev/null
@@ -0,0 +1,250 @@
+# This properties file describes the fx available to the data_send and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
+#
+# ETV Location
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,80:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:100;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;24=0%,0%:100%x100%:0;49=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:0;12=-100%,0%:100%x100%:0;37=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;37=0%,0%:100%x100%:0;61=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+exclusive=region
+.description=Exclusive
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=590,160:80x25
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=File Shot
+.filter[1].producer.font=San 18
+.filter[1].composite.geometry=0%,0%:100%x100%:15;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Special
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Ticker - provided for reference
+.filter[1].composite.geometry=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].producer.font=San 24
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.markup
+.properties.1=filter[2].producer.markup
+.properties.align=filter[1].composite.valign
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.properties.length[2]=filter[2].composite.out
+.period=2
+.composite.geometry=0,410:720x90
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbb00
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[0].composite.luma=%luma18.pgm
+.filter[0].composite.out=25
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 32
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.markup=
+.filter[2].producer.font=San 32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
diff --git a/src/modules/dgraft/Makefile b/src/modules/dgraft/Makefile
new file mode 100644 (file)
index 0000000..7e17749
--- /dev/null
@@ -0,0 +1,33 @@
+include ../../../config.mak
+
+TARGET = ../libmltdgraft$(LIBSUF)
+
+OBJS = factory.o \
+          filter_telecide.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/dgraft/factory.c b/src/modules/dgraft/factory.c
new file mode 100644 (file)
index 0000000..e0f2825
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2008 Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "telecide", filter_telecide_init );
+}
diff --git a/src/modules/dgraft/filter_telecide.c b/src/modules/dgraft/filter_telecide.c
new file mode 100644 (file)
index 0000000..9bb1149
--- /dev/null
@@ -0,0 +1,1230 @@
+/*
+ * filter_telecide.c -- Donald Graft's Inverse Telecine Filter
+ * Copyright (C) 2003 Donald A. Graft
+ * Copyright (C) 2008 Dan Dennedy <dan@dennedy.org>
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+//#define DEBUG_PATTERN_GUIDANCE
+
+#define MAX_CYCLE 6
+#define BLKSIZE 24
+#define BLKSIZE_TIMES2 (2 * BLKSIZE)
+#define GUIDE_32 1
+#define GUIDE_22 2
+#define GUIDE_32322 3
+#define AHEAD 0
+#define BEHIND 1
+#define POST_METRICS 1
+#define POST_FULL 2
+#define POST_FULL_MAP 3
+#define POST_FULL_NOMATCH 4
+#define POST_FULL_NOMATCH_MAP 5
+#define CACHE_SIZE 100000
+#define P 0
+#define C 1
+#define N 2
+#define PBLOCK 3
+#define CBLOCK 4
+
+#define NO_BACK 0
+#define BACK_ON_COMBED 1
+#define ALWAYS_BACK 2
+
+struct CACHE_ENTRY
+{
+       unsigned int frame;
+       unsigned int metrics[5];
+       unsigned int chosen;
+};
+
+struct PREDICTION
+{
+       unsigned int metric;
+       unsigned int phase;
+       unsigned int predicted;
+       unsigned int predicted_metric;
+};
+
+struct context_s {
+       int is_configured;
+       mlt_properties image_cache;
+       int out;
+       
+       int tff, chroma, blend, hints, show, debug;
+       float dthresh, gthresh, vthresh, vthresh_saved, bthresh;
+       int y0, y1, nt, guide, post, back, back_saved;
+       int pitch, dpitch, pitchover2, pitchtimes4;
+       int w, h, wover2, hover2, hplus1over2, hminus2;
+       int xblocks, yblocks;
+#ifdef WINDOWED_MATCH
+       unsigned int *matchc, *matchp, highest_matchc, highest_matchp;
+#endif
+       unsigned int *sumc, *sump, highest_sumc, highest_sump;
+       int vmetric;
+       unsigned int *overrides, *overrides_p;
+       int film, override, inpattern, found;
+       int force;
+
+       // Used by field matching.
+       unsigned char *fprp, *fcrp, *fcrp_saved, *fnrp;
+//     unsigned char *fprpU, *fcrpU, *fcrp_savedU, *fnrpU;
+//     unsigned char *fprpV, *fcrpV, *fcrp_savedV, *fnrpV;
+       unsigned char *dstp, *finalp;
+//     unsigned char *dstpU, *dstpV;
+       int chosen;
+       unsigned int p, c, pblock, cblock, lowest, predicted, predicted_metric;
+       unsigned int np, nc, npblock, ncblock, nframe;
+       float mismatch;
+       int pframe, x, y;
+       unsigned char *crp, *prp;
+       unsigned char *crpU, *prpU;
+       unsigned char *crpV, *prpV;
+       int hard;
+       char status[80];
+
+       // Metrics cache.
+       struct CACHE_ENTRY *cache;
+
+       // Pattern guidance data.
+       int cycle;
+       struct PREDICTION pred[MAX_CYCLE+1];
+};
+typedef struct context_s *context;
+
+
+static inline
+void BitBlt(uint8_t* dstp, int dst_pitch, const uint8_t* srcp,
+            int src_pitch, int row_size, int height)
+{
+       uint32_t y;
+       for(y=0;y<height;y++)
+       {
+               memcpy(dstp,srcp,row_size);
+               dstp+=dst_pitch;
+               srcp+=src_pitch;
+       }
+}
+
+static void Show(context cx, int frame, mlt_properties properties)
+{
+       char use;
+       char buf[512];
+
+       if (cx->chosen == P) use = 'p';
+       else if (cx->chosen == C) use = 'c';
+       else use = 'n';
+       snprintf(buf, sizeof(buf), "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
+       if ( cx->post )
+               snprintf(buf, sizeof(buf), "%sTelecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", buf, frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
+       if ( cx->guide )
+               snprintf(buf, sizeof(buf), "%spattern mismatch=%0.2f%%\n", buf, cx->mismatch);
+       snprintf(buf, sizeof(buf), "%sTelecide: frame %d: [%s %c]%s %s\n", buf, frame, cx->found ? "forcing" : "using", use,
+               cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
+               cx->guide ? cx->status : "");
+       mlt_properties_set( properties, "meta.attr.telecide.markup", buf );
+}
+
+static void Debug(context cx, int frame)
+{
+       char use;
+
+       if (cx->chosen == P) use = 'p';
+       else if (cx->chosen == C) use = 'c';
+       else use = 'n';
+       fprintf(stderr, "Telecide: frame %d: matches: %d %d %d\n", frame, cx->p, cx->c, cx->np);
+       if ( cx->post )
+               fprintf(stderr, "Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]\n", frame, cx->pblock, cx->cblock, cx->npblock, cx->vmetric);
+       if ( cx->guide )
+               fprintf(stderr, "pattern mismatch=%0.2f%%\n", cx->mismatch); 
+       fprintf(stderr, "Telecide: frame %d: [%s %c]%s %s\n", frame, cx->found ? "forcing" : "using", use,
+               cx->post ? (cx->film ? " [progressive]" : " [interlaced]") : "",
+               cx->guide ? cx->status : "");
+}
+
+static void WriteHints(int film, int inpattern, mlt_properties frame_properties)
+{
+       mlt_properties_set_int( frame_properties, "telecide.progressive", film);
+       mlt_properties_set_int( frame_properties, "telecide.in_pattern", inpattern);
+}
+
+static void PutChosen(context cx, int frame, unsigned int chosen)
+{
+       int f = frame % CACHE_SIZE;
+       if (frame < 0 || frame > cx->out || cx->cache[f].frame != frame)
+               return;
+       cx->cache[f].chosen = chosen;
+}
+
+static void CacheInsert(context cx, int frame, unsigned int p, unsigned int pblock,
+                                        unsigned int c, unsigned int cblock)
+{
+       int f = frame % CACHE_SIZE;
+       if (frame < 0 || frame > cx->out)
+               fprintf( stderr, "%s: internal error: invalid frame %d for CacheInsert", __FUNCTION__, frame);
+       cx->cache[f].frame = frame;
+       cx->cache[f].metrics[P] = p;
+       if (f) cx->cache[f-1].metrics[N] = p;
+       cx->cache[f].metrics[C] = c;
+       cx->cache[f].metrics[PBLOCK] = pblock;
+       cx->cache[f].metrics[CBLOCK] = cblock;
+       cx->cache[f].chosen = 0xff;
+}
+
+static int CacheQuery(context cx, int frame, unsigned int *p, unsigned int *pblock,
+                                       unsigned int *c, unsigned int *cblock)
+{
+       int f;
+
+       f = frame % CACHE_SIZE;
+       if (frame < 0 || frame > cx->out)
+               fprintf( stderr, "%s: internal error: invalid frame %d for CacheQuery", __FUNCTION__, frame);
+       if (cx->cache[f].frame != frame)
+       {
+               return 0;
+       }
+       *p = cx->cache[f].metrics[P];
+       *c = cx->cache[f].metrics[C];
+       *pblock = cx->cache[f].metrics[PBLOCK];
+       *cblock = cx->cache[f].metrics[CBLOCK];
+       return 1;
+}
+
+static int PredictHardYUY2(context cx, int frame, unsigned int *predicted, unsigned int *predicted_metric)
+{
+       // Look for pattern in the actual delivered matches of the previous cycle of frames.
+       // If a pattern is found, use that to predict the current match.
+       if ( cx->guide == GUIDE_22 )
+       {
+               if (cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen == 0xff)
+                       return 0;
+               switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen << 4) +
+                               (cx->cache[(frame- cx->cycle+1)%CACHE_SIZE].chosen))
+               {
+               case 0x11:
+                       *predicted = C;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+                       break;
+               case 0x22:
+                       *predicted = N;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+                       break;
+               default: return 0;
+               }
+       }
+       else if ( cx->guide == GUIDE_32 )
+       {
+               if (cx->cache[(frame-cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
+                       cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen == 0xff)
+                       return 0;
+
+               switch ((cx->cache[(frame-cx->cycle)%CACHE_SIZE  ].chosen << 16) +
+                               (cx->cache[(frame-cx->cycle+1)%CACHE_SIZE].chosen << 12) +
+                               (cx->cache[(frame-cx->cycle+2)%CACHE_SIZE].chosen <<  8) +
+                               (cx->cache[(frame-cx->cycle+3)%CACHE_SIZE].chosen <<  4) +
+                               (cx->cache[(frame-cx->cycle+4)%CACHE_SIZE].chosen))
+               {
+               case 0x11122:
+               case 0x11221:
+               case 0x12211:
+               case 0x12221: 
+               case 0x21122: 
+               case 0x11222: 
+                       *predicted = C;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+                       break;
+               case 0x22111:
+               case 0x21112:
+               case 0x22112: 
+               case 0x22211: 
+                       *predicted = N;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+                       break;
+               default: return 0;
+               }
+       }
+       else if ( cx->guide == GUIDE_32322 )
+       {
+               if (cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen == 0xff ||
+                       cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen == 0xff)
+                       return 0;
+
+               switch ((cx->cache[(frame- cx->cycle)%CACHE_SIZE  ].chosen << 20) +
+                               (cx->cache[(frame- cx->cycle +1)%CACHE_SIZE].chosen << 16) +
+                               (cx->cache[(frame- cx->cycle +2)%CACHE_SIZE].chosen << 12) +
+                               (cx->cache[(frame- cx->cycle +3)%CACHE_SIZE].chosen <<  8) +
+                               (cx->cache[(frame- cx->cycle +4)%CACHE_SIZE].chosen <<  4) +
+                               (cx->cache[(frame- cx->cycle +5)%CACHE_SIZE].chosen))
+               {
+               case 0x111122:
+               case 0x111221:
+               case 0x112211:
+               case 0x122111:
+               case 0x111222: 
+               case 0x112221:
+               case 0x122211:
+               case 0x222111: 
+                       *predicted = C;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C];
+                       break;
+               case 0x221111:
+               case 0x211112:
+
+               case 0x221112: 
+               case 0x211122: 
+                       *predicted = N;
+                       *predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N];
+                       break;
+               default: return 0;
+               }
+       }
+#ifdef DEBUG_PATTERN_GUIDANCE
+       fprintf( stderr, "%s: pos=%d HARD: predicted=%d\n", __FUNCTION__, frame, *predicted);
+#endif
+       return 1;
+}
+
+static struct PREDICTION *PredictSoftYUY2(context cx, int frame )
+{
+       // Use heuristics to look forward for a match.
+       int i, j, y, c, n, phase;
+       unsigned int metric;
+
+       cx->pred[0].metric = 0xffffffff;
+       if (frame < 0 || frame > cx->out - cx->cycle) return cx->pred;
+
+       // Look at the next cycle of frames.
+       for (y = frame + 1; y <= frame + cx->cycle; y++)
+       {
+               // Look for a frame where the current and next match values are
+               // very close. Those are candidates to predict the phase, because
+               // that condition should occur only once per cycle. Store the candidate
+               // phases and predictions in a list sorted by goodness. The list will
+               // be used by the caller to try the phases in order.
+               c = cx->cache[y%CACHE_SIZE].metrics[C]; 
+               n = cx->cache[y%CACHE_SIZE].metrics[N];
+               if (c == 0) c = 1;
+               metric = (100 * abs (c - n)) / c;
+               phase = y % cx->cycle;
+               if (metric < 5)
+               {
+                       // Place the new candidate phase in sorted order in the list.
+                       // Find the insertion point.
+                       i = 0;
+                       while (metric > cx->pred[i].metric) i++;
+                       // Find the end-of-list marker.
+                       j = 0;
+                       while (cx->pred[j].metric != 0xffffffff) j++;
+                       // Shift all items below the insertion point down by one to make
+                       // room for the insertion.
+                       j++;
+                       for (; j > i; j--)
+                       {
+                               cx->pred[j].metric = cx->pred[j-1].metric;
+                               cx->pred[j].phase = cx->pred[j-1].phase;
+                               cx->pred[j].predicted = cx->pred[j-1].predicted;
+                               cx->pred[j].predicted_metric = cx->pred[j-1].predicted_metric;
+                       }
+                       // Insert the new candidate data.
+                       cx->pred[j].metric = metric;
+                       cx->pred[j].phase = phase;
+                       if ( cx->guide == GUIDE_32 )
+                       {
+                               switch ((frame % cx->cycle) - phase)
+                               {
+                               case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case -3: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case  0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               }
+                       }
+                       else if ( cx->guide == GUIDE_32322 )
+                       {
+                               switch ((frame % cx->cycle) - phase)
+                               {
+                               case -5: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case -4: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case -3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case -2: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case -1: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case  0: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case +1: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case +2: cx->pred[j].predicted = N; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[N]; break; 
+                               case +3: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case +4: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               case +5: cx->pred[j].predicted = C; cx->pred[j].predicted_metric = cx->cache[frame%CACHE_SIZE].metrics[C]; break; 
+                               }
+                       }
+               }
+#ifdef DEBUG_PATTERN_GUIDANCE
+               fprintf( stderr, "%s: pos=%d metric=%d phase=%d\n", __FUNCTION__, frame, metric, phase);
+#endif
+       }
+       return cx->pred;
+}
+
+static
+void CalculateMetrics(context cx, int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV,
+                                       unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV)
+{
+       int x, y, p, c, tmp1, tmp2, skip;
+       int vc;
+    unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2;
+       unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4;
+       unsigned char *a0, *a2, *b0, *b2, *b4;
+       unsigned int diff, index;
+#      define T 4
+
+       /* Clear the block sums. */
+       for (y = 0; y < cx->yblocks; y++)
+       {
+               for (x = 0; x < cx->xblocks; x++)
+               {
+#ifdef WINDOWED_MATCH
+                       matchp[y*xblocks+x] = 0;
+                       matchc[y*xblocks+x] = 0;
+#endif
+                       cx->sump[y * cx->xblocks + x] = 0;
+                       cx->sumc[y * cx->xblocks + x] = 0;
+               }
+       }
+
+       /* Find the best field match. Subsample the frames for speed. */
+       currbot0  = fcrp + cx->pitch;
+       currbot2  = fcrp + 3 * cx->pitch;
+       currtop0 = fcrp;
+       currtop2 = fcrp + 2 * cx->pitch;
+       currtop4 = fcrp + 4 * cx->pitch;
+       prevbot0  = fprp + cx->pitch;
+       prevbot2  = fprp + 3 * cx->pitch;
+       prevtop0 = fprp;
+       prevtop2 = fprp + 2 * cx->pitch;
+       prevtop4 = fprp + 4 * cx->pitch;
+       if ( cx->tff )
+       {
+               a0 = prevbot0;
+               a2 = prevbot2;
+               b0 = currtop0;
+               b2 = currtop2;
+               b4 = currtop4;
+       }
+       else
+       {
+               a0 = currbot0;
+               a2 = currbot2;
+               b0 = prevtop0;
+               b2 = prevtop2;
+               b4 = prevtop4;
+       }
+       p = c = 0;
+
+       // Calculate the field match and film/video metrics.
+//     if (vi.IsYV12()) skip = 1;
+//     else 
+       skip = 1 + ( !cx->chroma );
+       for (y = 0, index = 0; y < cx->h - 4; y+=4)
+       {
+               /* Exclusion band. Good for ignoring subtitles. */
+               if (cx->y0 == cx->y1 || y < cx->y0 || y > cx->y1)
+               {
+                       for (x = 0; x < cx->w;)
+                       {
+//                             if (vi.IsYV12())
+//                                     index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
+//                             else
+                                       index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2;
+
+                               // Test combination with current frame.
+                               tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
+//                             diff = abs((long)currtop0[x] - (tmp1 >> 1));
+                               diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
+                               if (diff > cx->nt)
+                               {
+                                       c += diff;
+#ifdef WINDOWED_MATCH
+                                       matchc[index] += diff;
+#endif
+                               }
+
+                               tmp1 = currbot0[x] + T;
+                               tmp2 = currbot0[x] - T;
+                               vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
+                                        (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
+                               if (vc)
+                               {
+                                       cx->sumc[index]++;
+                               }
+
+                               // Test combination with previous frame.
+                               tmp1 = ((long)a0[x] + (long)a2[x]);
+                               diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
+                               if (diff > cx->nt)
+                               {
+                                       p += diff;
+#ifdef WINDOWED_MATCH
+                                       matchp[index] += diff;
+#endif
+                               }
+
+                               tmp1 = a0[x] + T;
+                               tmp2 = a0[x] - T;
+                               vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
+                                        (tmp2 > b0[x] && tmp2 > b2[x]);
+                               if (vc)
+                               {
+                                       cx->sump[index]++;
+                               }
+
+                               x += skip;
+                               if (!(x&3)) x += 4;
+                       }
+               }
+               currbot0 += cx->pitchtimes4;
+               currbot2 += cx->pitchtimes4;
+               currtop0 += cx->pitchtimes4;
+               currtop2 += cx->pitchtimes4;
+               currtop4 += cx->pitchtimes4;
+               a0               += cx->pitchtimes4;
+               a2               += cx->pitchtimes4;
+               b0               += cx->pitchtimes4;
+               b2               += cx->pitchtimes4;
+               b4               += cx->pitchtimes4;
+       }
+
+//     if (vi.IsYV12() && chroma == true)
+//     {
+//             int z;
+//
+//             for (z = 0; z < 2; z++)
+//             {
+//                     // Do the same for the U plane.
+//                     if (z == 0)
+//                     {
+//                             currbot0  = fcrpU + pitchover2;
+//                             currbot2  = fcrpU + 3 * pitchover2;
+//                             currtop0 = fcrpU;
+//                             currtop2 = fcrpU + 2 * pitchover2;
+//                             currtop4 = fcrpU + 4 * pitchover2;
+//                             prevbot0  = fprpU + pitchover2;
+//                             prevbot2  = fprpU + 3 * pitchover2;
+//                             prevtop0 = fprpU;
+//                             prevtop2 = fprpU + 2 * pitchover2;
+//                             prevtop4 = fprpU + 4 * pitchover2;
+//                     }
+//                     else
+//                     {
+//                             currbot0  = fcrpV + pitchover2;
+//                             currbot2  = fcrpV + 3 * pitchover2;
+//                             currtop0 = fcrpV;
+//                             currtop2 = fcrpV + 2 * pitchover2;
+//                             currtop4 = fcrpV + 4 * pitchover2;
+//                             prevbot0  = fprpV + pitchover2;
+//                             prevbot2  = fprpV + 3 * pitchover2;
+//                             prevtop0 = fprpV;
+//                             prevtop2 = fprpV + 2 * pitchover2;
+//                             prevtop4 = fprpV + 4 * pitchover2;
+//                     }
+//                     if (tff == true)
+//                     {
+//                             a0 = prevbot0;
+//                             a2 = prevbot2;
+//                             b0 = currtop0;
+//                             b2 = currtop2;
+//                             b4 = currtop4;
+//                     }
+//                     else
+//                     {
+//                             a0 = currbot0;
+//                             a2 = currbot2;
+//                             b0 = prevtop0;
+//                             b2 = prevtop2;
+//                             b4 = prevtop4;
+//                     }
+//
+//                     for (y = 0, index = 0; y < hover2 - 4; y+=4)
+//                     {
+//                             /* Exclusion band. Good for ignoring subtitles. */
+//                             if (y0 == y1 || y < y0/2 || y > y1/2)
+//                             {
+//                                     for (x = 0; x < wover2;)
+//                                     {
+//                                             if (vi.IsYV12())
+//                                                     index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
+//                                             else
+//                                                     index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2;
+//
+//                                             // Test combination with current frame.
+//                                             tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
+//                                             diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
+//                                             if (diff > nt)
+//                                             {
+//                                                     c += diff;
+//#ifdef WINDOWED_MATCH
+//                                                     matchc[index] += diff;
+//#endif
+//                                             }
+//
+//                                             tmp1 = currbot0[x] + T;
+//                                             tmp2 = currbot0[x] - T;
+//                                             vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
+//                                                      (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
+//                                             if (vc)
+//                                             {
+//                                                     sumc[index]++;
+//                                             }
+//
+//                                             // Test combination with previous frame.
+//                                             tmp1 = ((long)a0[x] + (long)a2[x]);
+//                                             diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
+//                                             if (diff > nt)
+//                                             {
+//                                                     p += diff;
+//#ifdef WINDOWED_MATCH
+//                                                     matchp[index] += diff;
+//#endif
+//                                             }
+//
+//                                             tmp1 = a0[x] + T;
+//                                             tmp2 = a0[x] - T;
+//                                             vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
+//                                                      (tmp2 > b0[x] && tmp2 > b2[x]);
+//                                             if (vc)
+//                                             {
+//                                                     sump[index]++;
+//                                             }
+//
+//                                             x ++;
+//                                             if (!(x&3)) x += 4;
+//                                     }
+//                             }
+//                             currbot0 += 4*pitchover2;
+//                             currbot2 += 4*pitchover2;
+//                             currtop0 += 4*pitchover2;
+//                             currtop2 += 4*pitchover2;
+//                             currtop4 += 4*pitchover2;
+//                             a0               += 4*pitchover2;
+//                             a2               += 4*pitchover2;
+//                             b0               += 4*pitchover2;
+//                             b2               += 4*pitchover2;
+//                             b4               += 4*pitchover2;
+//                     }
+//             }
+//     }
+//
+//     // Now find the blocks that have the greatest differences.
+//#ifdef WINDOWED_MATCH
+//     highest_matchp = 0;
+//     for (y = 0; y < yblocks; y++)
+//     {
+//             for (x = 0; x < xblocks; x++)
+//             {
+//if (frame == 45 && matchp[y * xblocks + x] > 2500)
+//{
+//     sprintf(buf, "%d/%d = %d\n", x, y, matchp[y * xblocks + x]);
+//     OutputDebugString(buf);
+//}
+//                     if (matchp[y * xblocks + x] > highest_matchp)
+//                     {
+//                             highest_matchp = matchp[y * xblocks + x];
+//                     }
+//             }
+//     }
+//     highest_matchc = 0;
+//     for (y = 0; y < yblocks; y++)
+//     {
+//             for (x = 0; x < xblocks; x++)
+//             {
+//if (frame == 44 && matchc[y * xblocks + x] > 2500)
+//{
+//     sprintf(buf, "%d/%d = %d\n", x, y, matchc[y * xblocks + x]);
+//     OutputDebugString(buf);
+//}
+//                     if (matchc[y * xblocks + x] > highest_matchc)
+//                     {
+//                             highest_matchc = matchc[y * xblocks + x];
+//                     }
+//             }
+//     }
+//#endif
+       if ( cx->post )
+       {
+               cx->highest_sump = 0;
+               for (y = 0; y < cx->yblocks; y++)
+               {
+                       for (x = 0; x < cx->xblocks; x++)
+                       {
+                               if (cx->sump[y * cx->xblocks + x] > cx->highest_sump)
+                               {
+                                       cx->highest_sump = cx->sump[y * cx->xblocks + x];
+                               }
+                       }
+               }
+               cx->highest_sumc = 0;
+               for (y = 0; y < cx->yblocks; y++)
+               {
+                       for (x = 0; x < cx->xblocks; x++)
+                       {
+                               if (cx->sumc[y * cx->xblocks + x] > cx->highest_sumc)
+                               {
+                                       cx->highest_sumc = cx->sumc[y * cx->xblocks + x];
+                               }
+                       }
+               }
+       }
+#ifdef WINDOWED_MATCH
+       CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc);
+#else
+       CacheInsert( cx, frame, p, cx->highest_sump, c, cx->highest_sumc);
+#endif
+}
+
+
+/** Process the image.
+*/
+
+static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_service( frame );
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       mlt_properties frame_properties = mlt_frame_properties( frame );
+       context cx = mlt_properties_get_data( properties, "context", NULL );
+       mlt_service producer = mlt_service_producer( mlt_filter_service( filter ) );
+       cx->out = producer? mlt_producer_get_playtime( MLT_PRODUCER( producer ) ) : 999999;
+
+       if ( ! cx->is_configured )
+       {
+               cx->back = mlt_properties_get_int( properties, "back" );
+               cx->chroma = mlt_properties_get_int( properties, "chroma" );
+               cx->guide = mlt_properties_get_int( properties, "guide" );
+               cx->gthresh = mlt_properties_get_double( properties, "gthresh" );
+               cx->post = mlt_properties_get_int( properties, "post" );
+               cx->vthresh = mlt_properties_get_double( properties, "vthresh" );
+               cx->bthresh = mlt_properties_get_double( properties, "bthresh" );
+               cx->dthresh = mlt_properties_get_double( properties, "dthresh" );
+               cx->blend = mlt_properties_get_int( properties, "blend" );
+               cx->nt = mlt_properties_get_int( properties, "nt" );
+               cx->y0 = mlt_properties_get_int( properties, "y0" );
+               cx->y1 = mlt_properties_get_int( properties, "y1" );
+               cx->hints = mlt_properties_get_int( properties, "hints" );
+               cx->debug = mlt_properties_get_int( properties, "debug" );
+               cx->show = mlt_properties_get_int( properties, "show" );
+       }
+
+       // Get the image
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       if ( ! cx->sump )
+       {
+               int guide = mlt_properties_get_int( properties, "guide" );
+               cx->cycle = 0;
+               if ( guide == GUIDE_32 )
+               {
+                       // 24fps to 30 fps telecine.
+                       cx->cycle = 5;
+               }
+               else if ( guide == GUIDE_22 )
+               {
+                       // PAL guidance (expect the current match to be continued).
+                       cx->cycle = 2;
+               }
+               else if ( guide == GUIDE_32322 )
+               {
+                       // 25fps to 30 fps telecine.
+                       cx->cycle = 6;
+               }
+
+               cx->xblocks = (*width+BLKSIZE-1) / BLKSIZE;
+               cx->yblocks = (*height+BLKSIZE-1) / BLKSIZE;
+               cx->sump = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
+               cx->sumc = (unsigned int *) mlt_pool_alloc( cx->xblocks * cx->yblocks * sizeof(unsigned int) );
+               mlt_properties_set_data( properties, "sump", cx->sump, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "sumc", cx->sumc, cx->xblocks * cx->yblocks * sizeof(unsigned int), (mlt_destructor)mlt_pool_release, NULL );
+               cx->tff = mlt_properties_get_int( frame_properties, "top_field_first" );
+       //      fprintf(stderr, "%s: TOP FIELD FIRST %d\n", __FUNCTION__, cx->tff );
+       }
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // Put the current image into the image cache, keyed on position
+               size_t image_size = (*width * *height) << 1;
+               mlt_position pos = mlt_frame_get_position( frame );
+               uint8_t *image_copy = mlt_pool_alloc( image_size );
+               memcpy( image_copy, *image, image_size );
+               char key[20];
+               sprintf( key, "%d", pos );
+               mlt_properties_set_data( cx->image_cache, key, image_copy, image_size, (mlt_destructor)mlt_pool_release, NULL );
+               
+               // Only if we have enough frame images cached
+               if ( pos > 1 && pos > cx->cycle + 1 )
+               {
+                       pos -= cx->cycle + 1;
+                       // Get the current frame image
+                       sprintf( key, "%d", pos );
+                       cx->fcrp = mlt_properties_get_data( cx->image_cache, key, NULL );
+                       if (!cx->fcrp) return error;
+                        
+                       // Get the previous frame image
+                       cx->pframe = pos == 0 ? 0 : pos - 1;
+                       sprintf( key, "%d", cx->pframe );
+                       cx->fprp = mlt_properties_get_data( cx->image_cache, key, NULL );
+                       if (!cx->fprp) return error;
+                       
+                       // Get the next frame image
+                       cx->nframe = pos > cx->out ? cx->out : pos + 1;
+                       sprintf( key, "%d", cx->nframe );
+                       cx->fnrp = mlt_properties_get_data( cx->image_cache, key, NULL );
+                       if (!cx->fnrp) return error;
+                       
+                       cx->pitch = *width << 1;
+                       cx->pitchover2 = cx->pitch >> 1;
+                       cx->pitchtimes4 = cx->pitch << 2;
+                       cx->w = *width << 1;
+                       cx->h = *height;
+                       if ((cx->w/2) & 1)
+                               fprintf( stderr, "%s: width must be a multiple of 2\n", __FUNCTION__ );
+                       if (cx->h & 1)
+                               fprintf( stderr, "%s: height must be a multiple of 2\n", __FUNCTION__ );
+                       cx->wover2 = cx->w/2;
+                       cx->hover2 = cx->h/2;
+                       cx->hplus1over2 = (cx->h+1)/2;
+                       cx->hminus2 = cx->h - 2;
+                       cx->dpitch = cx->pitch;
+                       
+                       // Ensure that the metrics for the frames
+                       // after the current frame are in the cache. They will be used for
+                       // pattern guidance.
+                       if ( cx->guide )
+                       {
+                               for ( cx->y = pos + 1; (cx->y <= pos + cx->cycle + 1) && (cx->y <= cx->out); cx->y++ )
+                               {
+                                       if ( ! CacheQuery( cx, cx->y, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
+                                       {
+                                               sprintf( key, "%d", cx->y );
+                                               cx->crp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
+                                               sprintf( key, "%d", cx->y ? cx->y - 1 : 1 );
+                                               cx->prp = (unsigned char *) mlt_properties_get_data( cx->image_cache, key, NULL );
+                                               CalculateMetrics( cx, cx->y, cx->crp, NULL, NULL, cx->prp, NULL, NULL );                                        }
+                               }
+                       }
+                       
+                       // Check for manual overrides of the field matching.
+                       cx->found = 0;
+                       cx->film = 1;
+                       cx->override = 0;
+                       cx->inpattern = 0;
+                       cx->vthresh = cx->vthresh;
+                       cx->back = cx->back_saved;
+                       
+                       // Get the metrics for the current-previous (p), current-current (c), and current-next (n) match candidates.
+                       if ( ! CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock ) )
+                       {
+                               CalculateMetrics( cx, pos, cx->fcrp, NULL, NULL, cx->fprp, NULL, NULL );
+                               CacheQuery( cx, pos, &cx->p, &cx->pblock, &cx->c, &cx->cblock );
+                       }                               
+                       if ( ! CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock ) )
+                       {
+                               CalculateMetrics( cx, cx->nframe, cx->fnrp, NULL, NULL, cx->fcrp, NULL, NULL );
+                               CacheQuery( cx, cx->nframe, &cx->np, &cx->npblock, &cx->nc, &cx->ncblock );
+                       }                               
+                       
+                       // Determine the best candidate match.
+                       if ( !cx->found )
+                       {
+                               cx->lowest = cx->c;
+                               cx->chosen = C;
+                               if ( cx->back == ALWAYS_BACK && cx->p < cx->lowest )
+                               {
+                                       cx->lowest = cx->p;
+                                       cx->chosen = P;
+                               }
+                               if ( cx->np < cx->lowest )
+                               {
+                                       cx->lowest = cx->np;
+                                       cx->chosen = N;
+                               }
+                       }
+                       if ((pos == 0 && cx->chosen == P) || (pos == cx->out && cx->chosen == N))
+                       {
+                               cx->chosen = C;
+                               cx->lowest = cx->c;
+                       }
+
+                       // See if we can apply pattern guidance.
+                       cx->mismatch = 100.0;
+                       if ( cx->guide )
+                       {
+                               cx->hard = 0;
+                               if ( pos >= cx->cycle && PredictHardYUY2( cx, pos, &cx->predicted, &cx->predicted_metric) )
+                               {
+                                       cx->inpattern = 1;
+                                       cx->mismatch = 0.0;
+                                       cx->hard = 1;
+                                       if ( cx->chosen != cx->predicted )
+                                       {
+                                               // The chosen frame doesn't match the prediction.
+                                               if ( cx->predicted_metric == 0 )
+                                                       cx->mismatch = 0.0;
+                                               else
+                                                       cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest ) ) / cx->predicted_metric;
+                                               if ( cx->mismatch < cx->gthresh )
+                                               {
+                                                       // It's close enough, so use the predicted one.
+                                                       if ( !cx->found )
+                                                       {
+                                                               cx->chosen = cx->predicted;
+                                                               cx->override = 1;
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       cx->hard = 0;
+                                                       cx->inpattern = 0;
+                                               }
+                                       }
+                               }
+               
+                               if ( !cx->hard && cx->guide != GUIDE_22 )
+                               {
+                                       int i;
+                                       struct PREDICTION *pred = PredictSoftYUY2( cx, pos );
+               
+                                       if ( ( pos <= cx->out - cx->cycle) && ( pred[0].metric != 0xffffffff ) )
+                                       {
+                                               // Apply pattern guidance.
+                                               // If the predicted match metric is within defined percentage of the
+                                               // best calculated one, then override the calculated match with the
+                                               // predicted match.
+                                               i = 0;
+                                               while ( pred[i].metric != 0xffffffff )
+                                               {
+                                                       cx->predicted = pred[i].predicted;
+                                                       cx->predicted_metric = pred[i].predicted_metric;
+#ifdef DEBUG_PATTERN_GUIDANCE
+                                                       fprintf(stderr, "%s: pos=%d predicted=%d\n", __FUNCTION__, pos, cx->predicted);
+#endif
+                                                       if ( cx->chosen != cx->predicted )
+                                                       {
+                                                               // The chosen frame doesn't match the prediction.
+                                                               if ( cx->predicted_metric == 0 )
+                                                                       cx->mismatch = 0.0;
+                                                               else
+                                                                       cx->mismatch = (100.0 * abs( cx->predicted_metric - cx->lowest )) / cx->predicted_metric;
+                                                               if ( (int) cx->mismatch <= cx->gthresh )
+                                                               {
+                                                                       // It's close enough, so use the predicted one.
+                                                                       if ( !cx->found )
+                                                                       {
+                                                                               cx->chosen = cx->predicted;
+                                                                               cx->override = 1;
+                                                                       }
+                                                                       cx->inpattern = 1;
+                                                                       break;
+                                                               }
+                                                               else
+                                                               {
+                                                                       // Looks like we're not in a predictable pattern.
+                                                                       cx->inpattern = 0;
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               cx->inpattern = 1;
+                                                               cx->mismatch = 0.0;
+                                                               break;
+                                                       }
+                                                       i++;
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Check the match for progressive versus interlaced.
+                       if ( cx->post )
+                       {
+                               if (cx->chosen == P) cx->vmetric = cx->pblock;
+                               else if (cx->chosen == C) cx->vmetric = cx->cblock;
+                               else if (cx->chosen == N) cx->vmetric = cx->npblock;
+               
+                               if ( !cx->found && cx->back == BACK_ON_COMBED && cx->vmetric > cx->bthresh && cx->p < cx->lowest )
+                               {
+                                       // Backward match.
+                                       cx->vmetric = cx->pblock;
+                                       cx->chosen = P;
+                                       cx->inpattern = 0;
+                                       cx->mismatch = 100;
+                               }
+                               if ( cx->vmetric > cx->vthresh )
+                               {
+                                       // After field matching and pattern guidance the frame is still combed.
+                                       cx->film = 0;
+                                       if ( !cx->found && ( cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP ) )
+                                       {
+                                               cx->chosen = C;
+                                               cx->vmetric = cx->cblock;
+                                               cx->inpattern = 0;
+                                               cx->mismatch = 100;
+                                       }
+                               }
+                       }
+                       cx->vthresh = cx->vthresh_saved;
+               
+                       // Setup strings for debug info.
+                       if ( cx->inpattern && !cx->override ) strcpy( cx->status, "[in-pattern]" );
+                       else if ( cx->inpattern && cx->override ) strcpy( cx->status, "[in-pattern*]" );
+                       else strcpy( cx->status, "[out-of-pattern]" );
+       
+                       // Assemble and output the reconstructed frame according to the final match.
+                       cx->dstp = *image;
+                       if ( cx->chosen == N )
+                       {
+                               // The best match was with the next frame.
+                               if ( cx->tff )
+                               {
+                                       BitBlt( cx->dstp, 2 * cx->dpitch, cx->fnrp, 2 * cx->pitch, cx->w, cx->hover2 );
+                                       BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
+                               }
+                               else
+                               {
+                                       BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+                                       BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fnrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
+                               }
+                       }
+                       else if ( cx->chosen == C )
+                       {
+                               // The best match was with the current frame.
+                               BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+                               BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
+                       }
+                       else if ( ! cx->tff )
+                       {
+                               // The best match was with the previous frame.
+                               BitBlt( cx->dstp, 2 * cx->dpitch, cx->fprp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+                               BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fcrp + cx->pitch, 2 * cx->pitch, cx->w, cx->hover2 );
+                       }
+                       else
+                       {
+                               // The best match was with the previous frame.
+                               BitBlt( cx->dstp, 2 * cx->dpitch, cx->fcrp, 2 * cx->pitch, cx->w, cx->hplus1over2 );
+                               BitBlt( cx->dstp + cx->dpitch, 2 * cx->dpitch, cx->fprp + cx->pitch, 2 * cx->pitch,     cx->w, cx->hover2 );
+                       }
+                       if ( cx->guide )
+                               PutChosen( cx, pos, cx->chosen );
+
+                       if ( !cx->post || cx->post == POST_METRICS )
+                       {
+                               if ( cx->force == '+') cx->film = 0;
+                               else if ( cx->force == '-' ) cx->film = 1;
+                       }
+                       else if ((cx->force == '+') ||
+                               ((cx->post == POST_FULL || cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH || cx->post == POST_FULL_NOMATCH_MAP)
+                                        && (cx->film == 0 && cx->force != '-')))
+                       {
+                               unsigned char *dstpp, *dstpn;
+                               int v1, v2;
+               
+                               if ( cx->blend )
+                               {
+                                       // Do first and last lines.
+                                       uint8_t *final = mlt_pool_alloc( image_size );
+                                       cx->finalp = final;
+                                       mlt_properties_set_data( frame_properties, "image", final, image_size, (mlt_destructor)mlt_pool_release, NULL );
+                                       dstpn = cx->dstp + cx->dpitch;
+                                       for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+                                       {
+                                               cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpn[cx->x]) >> 1);
+                                       }
+                                       cx->finalp = final + (cx->h-1)*cx->dpitch;
+                                       cx->dstp = *image + (cx->h-1)*cx->dpitch;
+                                       dstpp = cx->dstp - cx->dpitch;
+                                       for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+                                       {
+                                               cx->finalp[cx->x] = (((int)cx->dstp[cx->x] + (int)dstpp[cx->x]) >> 1);
+                                       }
+                                       // Now do the rest.
+                                       cx->dstp = *image + cx->dpitch;
+                                       dstpp = cx->dstp - cx->dpitch;
+                                       dstpn = cx->dstp + cx->dpitch;
+                                       cx->finalp = final + cx->dpitch;
+                                       for ( cx->y = 1; cx->y < cx->h - 1; cx->y++ )
+                                       {
+                                               for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+                                               {
+                                                       v1 = (int) cx->dstp[cx->x] - cx->dthresh;
+                                                       if ( v1 < 0 )
+                                                               v1 = 0; 
+                                                       v2 = (int) cx->dstp[cx->x] + cx->dthresh;
+                                                       if (v2 > 235) v2 = 235; 
+                                                       if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
+                                                       {
+                                                               if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
+                                                               {
+                                                                       if (cx->x & 1) cx->finalp[cx->x] = 128;
+                                                                       else cx->finalp[cx->x] = 235;
+                                                               }
+                                                               else
+                                                                       cx->finalp[cx->x] = ((int)dstpp[cx->x] + (int)dstpn[cx->x] + (int)cx->dstp[cx->x] + (int)cx->dstp[cx->x]) >> 2;
+                                                       }
+                                                       else cx->finalp[cx->x] = cx->dstp[cx->x];
+                                               }
+                                               cx->finalp += cx->dpitch;
+                                               cx->dstp += cx->dpitch;
+                                               dstpp += cx->dpitch;
+                                               dstpn += cx->dpitch;
+                                       }
+               
+               
+                                       if (cx->show ) Show( cx, pos, frame_properties);
+                                       if (cx->debug) Debug(cx, pos);
+                                       if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
+                                       goto final;
+                               }
+               
+                               // Interpolate mode.
+                               cx->dstp = *image + cx->dpitch;
+                               dstpp = cx->dstp - cx->dpitch;
+                               dstpn = cx->dstp + cx->dpitch;
+                               for ( cx->y = 1; cx->y < cx->h - 1; cx->y+=2 )
+                               {
+                                       for ( cx->x = 0; cx->x < cx->w; cx->x++ )
+                                       {
+                                               v1 = (int) cx->dstp[cx->x] - cx->dthresh;
+                                               if (v1 < 0) v1 = 0; 
+                                               v2 = (int) cx->dstp[cx->x] + cx->dthresh;
+                                               if (v2 > 235) v2 = 235; 
+                                               if ((v1 > dstpp[cx->x] && v1 > dstpn[cx->x]) || (v2 < dstpp[cx->x] && v2 < dstpn[cx->x]))
+                                               {
+                                                       if ( cx->post == POST_FULL_MAP || cx->post == POST_FULL_NOMATCH_MAP )
+                                                       {
+                                                               if (cx->x & 1) cx->dstp[cx->x] = 128;
+                                                               else cx->dstp[cx->x] = 235;
+                                                       }
+                                                       else
+                                                               cx->dstp[cx->x] = (dstpp[cx->x] + dstpn[cx->x]) >> 1;
+                                               }
+                                       }
+                                       cx->dstp += 2 * cx->dpitch;
+                                       dstpp += 2 * cx->dpitch;
+                                       dstpn += 2 * cx->dpitch;
+                               }
+                       }
+                       if (cx->show ) Show( cx, pos, frame_properties);
+                       if (cx->debug) Debug(cx, pos);
+                       if (cx->hints) WriteHints(cx->film, cx->inpattern, frame_properties);
+
+final:                 
+                       // Flush frame at tail of period from the cache
+                       sprintf( key, "%d", pos - 1 );
+                       mlt_properties_set_data( cx->image_cache, key, NULL, 0, NULL, NULL );
+               }
+               else
+               {
+                       // Signal the first {cycle} frames as invalid
+                       mlt_properties_set_int( frame_properties, "garbage", 1 );
+               }
+       }
+       else if ( error == 0 && *format == mlt_image_yuv420p )
+       {
+               fprintf(stderr,"%s: %d pos %d\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) );
+       }
+
+       return error;
+}
+
+/** Process the frame object.
+*/
+
+static mlt_frame process( mlt_filter this, mlt_frame frame )
+{
+       // Push the filter on to the stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the frame filter
+       mlt_frame_push_get_image( frame, get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = process;
+
+               // Allocate the context and set up for garbage collection               
+               context cx = (context) mlt_pool_alloc( sizeof(struct context_s) );
+               memset( cx, 0, sizeof( struct context_s ) );
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               mlt_properties_set_data( properties, "context", cx, sizeof(struct context_s), (mlt_destructor)mlt_pool_release, NULL );
+
+               // Allocate the metrics cache and set up for garbage collection
+               cx->cache = (struct CACHE_ENTRY *) mlt_pool_alloc(CACHE_SIZE * sizeof(struct CACHE_ENTRY ));
+               mlt_properties_set_data( properties, "cache", cx->cache, CACHE_SIZE * sizeof(struct CACHE_ENTRY), (mlt_destructor)mlt_pool_release, NULL );
+               int i;
+               for (i = 0; i < CACHE_SIZE; i++)
+               {
+                       cx->cache[i].frame = 0xffffffff;
+                       cx->cache[i].chosen = 0xff;
+               }
+               
+               // Allocate the image cache and set up for garbage collection
+               cx->image_cache = mlt_properties_new();
+               mlt_properties_set_data( properties, "image_cache", cx->image_cache, 0, (mlt_destructor)mlt_properties_close, NULL );
+               
+               // Initialize the parameter defaults
+               mlt_properties_set_int( properties, "guide", 0 );
+               mlt_properties_set_int( properties, "back", 0 );
+               mlt_properties_set_int( properties, "chroma", 0 );
+               mlt_properties_set_int( properties, "post", POST_FULL );
+               mlt_properties_set_double( properties, "gthresh", 10.0 );
+               mlt_properties_set_double( properties, "vthresh", 50.0 );
+               mlt_properties_set_double( properties, "bthresh", 50.0 );
+               mlt_properties_set_double( properties, "dthresh", 7.0 );
+               mlt_properties_set_int( properties, "blend", 0 );
+               mlt_properties_set_int( properties, "nt", 10 );
+               mlt_properties_set_int( properties, "y0", 0 );
+               mlt_properties_set_int( properties, "y1", 0 );
+               mlt_properties_set_int( properties, "hints", 1 );
+       }
+       return this;
+}
+
diff --git a/src/modules/dgraft/gpl b/src/modules/dgraft/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/dv/Makefile b/src/modules/dv/Makefile
new file mode 100644 (file)
index 0000000..8c5ef1b
--- /dev/null
@@ -0,0 +1,36 @@
+include ../../../config.mak
+
+TARGET = ../libmltdv$(LIBSUF)
+
+OBJS = factory.o \
+          producer_libdv.o \
+          consumer_libdv.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags libdv`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs libdv`
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/dv/configure b/src/modules/dv/configure
new file mode 100755 (executable)
index 0000000..6712ea6
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       pkg-config libdv 2> /dev/null
+       disable_libdv=$?
+
+       if [ "$disable_libdv" != "0" ]
+       then
+               echo "- libdv not found: disabling"
+               touch ../disable-dv
+               exit 0
+       fi
+
+fi
+
diff --git a/src/modules/dv/consumer_libdv.c b/src/modules/dv/consumer_libdv.c
new file mode 100644 (file)
index 0000000..2ee9b20
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * consumer_libdv.c -- a DV encoder based on libdv
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// mlt Header files
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+
+// System header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+// libdv header files
+#include <libdv/dv.h>
+
+#define FRAME_SIZE_525_60      10 * 150 * 80
+#define FRAME_SIZE_625_50      12 * 150 * 80
+
+// Forward references.
+static int consumer_start( mlt_consumer this );
+static int consumer_stop( mlt_consumer this );
+static int consumer_is_stopped( mlt_consumer this );
+static int consumer_encode_video( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame );
+static void consumer_encode_audio( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame );
+static void consumer_output( mlt_consumer this, uint8_t *dv_frame, int size, mlt_frame frame );
+static void *consumer_thread( void *arg );
+static void consumer_close( mlt_consumer this );
+
+/** Initialise the dv consumer.
+*/
+
+mlt_consumer consumer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Allocate the consumer
+       mlt_consumer this = calloc( 1, sizeof( struct mlt_consumer_s ) );
+
+       // If memory allocated and initialises without error
+       if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+       {
+               // Get properties from the consumer
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+               // Assign close callback
+               this->close = consumer_close;
+
+               // Interpret the argument
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "target", arg );
+
+               // Set the encode and output handling method
+               mlt_properties_set_data( properties, "video", consumer_encode_video, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "audio", consumer_encode_audio, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "output", consumer_output, 0, NULL, NULL );
+
+               // Terminate at end of the stream by default
+               mlt_properties_set_int( properties, "terminate_on_pause", 1 );
+
+               // Set up start/stop/terminated callbacks
+               this->start = consumer_start;
+               this->stop = consumer_stop;
+               this->is_stopped = consumer_is_stopped;
+       }
+       else
+       {
+               // Clean up in case of init failure
+               free( this );
+               this = NULL;
+       }
+
+       // Return this
+       return this;
+}
+
+/** Start the consumer.
+*/
+
+static int consumer_start( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check that we're not already running
+       if ( !mlt_properties_get_int( properties, "running" ) )
+       {
+               // Allocate a thread
+               pthread_t *thread = calloc( 1, sizeof( pthread_t ) );
+
+               // Assign the thread to properties
+               mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL );
+
+               // Set the running state
+               mlt_properties_set_int( properties, "running", 1 );
+
+               // Create the thread
+               pthread_create( thread, NULL, consumer_thread, this );
+       }
+       return 0;
+}
+
+/** Stop the consumer.
+*/
+
+static int consumer_stop( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Check that we're running
+       if ( mlt_properties_get_int( properties, "running" ) )
+       {
+               // Get the thread
+               pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL );
+
+               // Stop the thread
+               mlt_properties_set_int( properties, "running", 0 );
+
+               // Wait for termination
+               pthread_join( *thread, NULL );
+
+               // Close the output file :-) - this is obtuse - doesn't matter if output file
+               // exists or not - the destructor will kick in if it does
+               mlt_properties_set_data( properties, "output_file", NULL, 0, NULL, NULL );
+       }
+
+       return 0;
+}
+
+/** Determine if the consumer is stopped.
+*/
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+       return !mlt_properties_get_int( properties, "running" );
+}
+
+/** Get or create a new libdv encoder.
+*/
+
+static dv_encoder_t *libdv_get_encoder( mlt_consumer this, mlt_frame frame )
+{
+       // Get the properties of the consumer
+       mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Obtain the dv_encoder
+       dv_encoder_t *encoder = mlt_properties_get_data( this_properties, "dv_encoder", NULL );
+
+       // Construct one if we don't have one
+       if ( encoder == NULL )
+       {
+               // Get the fps of the consumer (for now - should be from frame)
+               double fps = mlt_properties_get_double( this_properties, "fps" );
+
+               // Create the encoder
+               encoder = dv_encoder_new( 0, 0, 0 );
+
+               // Encoder settings
+               encoder->isPAL = fps == 25;
+               encoder->is16x9 = 0;
+               encoder->vlc_encode_passes = 1;
+               encoder->static_qno = 0;
+               encoder->force_dct = DV_DCT_AUTO;
+
+               // Store the encoder on the properties
+               mlt_properties_set_data( this_properties, "dv_encoder", encoder, 0, ( mlt_destructor )dv_encoder_free, NULL );
+       }
+
+       // Return the encoder
+       return encoder;
+}
+
+
+/** The libdv encode video method.
+*/
+
+static int consumer_encode_video( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame )
+{
+       // Obtain the dv_encoder
+       dv_encoder_t *encoder = libdv_get_encoder( this, frame );
+
+       // Get the properties of the consumer
+       mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // This will hold the size of the dv frame
+       int size = 0;
+
+       // Is the image rendered
+       int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" );
+
+       // Get width and height
+       int width = mlt_properties_get_int( this_properties, "width" );
+       int height = mlt_properties_get_int( this_properties, "height" );
+
+       // If we get an encoder, then encode the image
+       if ( rendered && encoder != NULL )
+       {
+               // Specify desired image properties
+               mlt_image_format fmt = mlt_image_yuv422;
+               uint8_t *image = NULL;
+
+               // Get the image
+               mlt_events_fire( this_properties, "consumer-frame-show", frame, NULL );
+               mlt_frame_get_image( frame, &image, &fmt, &width, &height, 0 );
+
+               // Check that we get what we expected
+               if ( fmt != mlt_image_yuv422 || 
+                        width != mlt_properties_get_int( this_properties, "width" ) ||
+                        height != mlt_properties_get_int( this_properties, "height" ) ||
+                        image == NULL )
+               {
+                       // We should try to recover here
+                       fprintf( stderr, "We have a problem houston...\n" );
+               }
+               else
+               {
+                       // Calculate the size of the dv frame
+                       size = height == 576 ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+               }
+
+               // Process the frame
+               if ( size != 0 )
+               {
+                       // Encode the image
+                       dv_encode_full_frame( encoder, &image, e_dv_color_yuv, dv_frame );
+               }
+       }
+       else if ( encoder != NULL )
+       {
+               // Calculate the size of the dv frame (duplicate of previous)
+               size = height == 576 ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+       }
+       
+       return size;
+}
+
+/** The libdv encode audio method.
+*/
+
+static void consumer_encode_audio( mlt_consumer this, uint8_t *dv_frame, mlt_frame frame )
+{
+       // Get the properties of the consumer
+       mlt_properties this_properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get the properties of the frame
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the dv_encoder
+       dv_encoder_t *encoder = libdv_get_encoder( this, frame );
+
+       // Only continue if we have an encoder
+       if ( encoder != NULL )
+       {
+               // Get the frame count
+               int count = mlt_properties_get_int( this_properties, "count" );
+
+               // Default audio args
+               mlt_audio_format fmt = mlt_audio_pcm;
+               int channels = 2;
+               int frequency = mlt_properties_get_int( this_properties, "frequency" );
+               int samples = mlt_sample_calculator( mlt_properties_get_double( this_properties, "fps" ), frequency, count );
+               int16_t *pcm = NULL;
+
+               // Get the frame number
+               time_t start = time( NULL );
+               int height = mlt_properties_get_int( this_properties, "height" );
+               int is_pal = height == 576;
+               int is_wide = mlt_properties_get_int( this_properties, "display_aspect_num" ) == 16;
+
+               // Temporary - audio buffer allocation
+               int16_t *audio_buffers[ 4 ];
+               int i = 0;
+               int j = 0;
+               for ( i = 0 ; i < 4; i ++ )
+                       audio_buffers[ i ] = mlt_pool_alloc( 2 * DV_AUDIO_MAX_SAMPLES );
+
+               // Get the audio
+               mlt_frame_get_audio( frame, &pcm, &fmt, &frequency, &channels, &samples );
+
+               // Inform the encoder of the number of audio samples
+               encoder->samples_this_frame = samples;
+
+               // Fill the audio buffers correctly
+               if ( mlt_properties_get_double( frame_properties, "_speed" ) == 1.0 )
+               {
+                       for ( i = 0; i < samples; i ++ )
+                               for ( j = 0; j < channels; j++ )
+                                       audio_buffers[ j ][ i ] = *pcm ++;
+               }
+               else
+               {
+                       for ( j = 0; j < channels; j++ )
+                               memset( audio_buffers[ j ], 0, 2 * DV_AUDIO_MAX_SAMPLES );
+               }
+
+               // Encode audio on frame
+               dv_encode_full_audio( encoder, audio_buffers, channels, frequency, dv_frame );
+
+               // Specify meta data on the frame
+               dv_encode_metadata( dv_frame, is_pal, is_wide, &start, count );
+               dv_encode_timecode( dv_frame, is_pal, count );
+
+               // Update properties
+               mlt_properties_set_int( this_properties, "count", ++ count );
+
+               // Temporary - free audio buffers
+               for ( i = 0 ; i < 4; i ++ )
+                       mlt_pool_release( audio_buffers[ i ] );
+       }
+}
+
+/** The libdv output method.
+*/
+
+static void consumer_output( mlt_consumer this, uint8_t *dv_frame, int size, mlt_frame frame )
+{
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       FILE *output = stdout;
+       char *target = mlt_properties_get( properties, "target" );
+
+       if ( target != NULL )
+       {
+               output = mlt_properties_get_data( properties, "output_file", NULL );
+               if ( output == NULL )
+               {
+                       output = fopen( target, "w" );
+                       if ( output != NULL )
+                               mlt_properties_set_data( properties, "output_file", output, 0, ( mlt_destructor )fclose, 0 );
+               }
+       }
+
+       if ( output != NULL )
+       {
+               fwrite( dv_frame, size, 1, output );
+               fflush( output );
+       }
+       else
+       {
+               fprintf( stderr, "Unable to open %s\n", target );
+       }
+}
+
+/** The main thread - the argument is simply the consumer.
+*/
+
+static void *consumer_thread( void *arg )
+{
+       // Map the argument to the object
+       mlt_consumer this = arg;
+
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get the terminate_on_pause property
+       int top = mlt_properties_get_int( properties, "terminate_on_pause" );
+
+       // Get the handling methods
+       int ( *video )( mlt_consumer, uint8_t *, mlt_frame ) = mlt_properties_get_data( properties, "video", NULL );
+       int ( *audio )( mlt_consumer, uint8_t *, mlt_frame ) = mlt_properties_get_data( properties, "audio", NULL );
+       int ( *output )( mlt_consumer, uint8_t *, int, mlt_frame ) = mlt_properties_get_data( properties, "output", NULL );
+
+       // Allocate a single PAL frame for encoding
+       uint8_t *dv_frame = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+       // Frame and size
+       mlt_frame frame = NULL;
+       int size = 0;
+
+       // Loop while running
+       while( mlt_properties_get_int( properties, "running" ) )
+       {
+               // Get the frame
+               frame = mlt_consumer_rt_frame( this );
+
+               // Check that we have a frame to work with
+               if ( frame != NULL )
+               {
+                       // Terminate on pause
+                       if ( top && mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0 )
+                       {
+                               mlt_frame_close( frame );
+                               break;
+                       }
+
+                       // Obtain the dv_encoder
+                       if ( libdv_get_encoder( this, frame ) != NULL )
+                       {
+                               // Encode the image
+                               size = video( this, dv_frame, frame );
+
+                               // Encode the audio
+                               if ( size > 0 )
+                                       audio( this, dv_frame, frame );
+
+                               // Output the frame
+                               output( this, dv_frame, size, frame );
+
+                               // Close the frame
+                               mlt_frame_close( frame );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Unable to obtain dv encoder.\n" );
+                       }
+               }
+       }
+
+       // Tidy up
+       mlt_pool_release( dv_frame );
+
+       mlt_consumer_stopped( this );
+
+       return NULL;
+}
+
+/** Close the consumer.
+*/
+
+static void consumer_close( mlt_consumer this )
+{
+       // Stop the consumer
+       mlt_consumer_stop( this );
+
+       // Close the parent
+       mlt_consumer_close( this );
+
+       // Free the memory
+       free( this );
+}
diff --git a/src/modules/dv/factory.c b/src/modules/dv/factory.c
new file mode 100644 (file)
index 0000000..305fb9c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "libdv", consumer_libdv_init );
+       MLT_REGISTER( producer_type, "libdv", producer_libdv_init );
+}
diff --git a/src/modules/dv/producer_libdv.c b/src/modules/dv/producer_libdv.c
new file mode 100644 (file)
index 0000000..6fd012c
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * producer_libdv.c -- simple libdv test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_profile.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libdv/dv.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define FRAME_SIZE_525_60      10 * 150 * 80
+#define FRAME_SIZE_625_50      12 * 150 * 80
+
+/** To conserve resources, we maintain a stack of dv decoders.
+*/
+
+static pthread_mutex_t decoder_lock = PTHREAD_MUTEX_INITIALIZER;
+static mlt_properties dv_decoders = NULL;
+
+dv_decoder_t *dv_decoder_alloc( )
+{
+       // We'll return a dv_decoder
+       dv_decoder_t *this = NULL;
+
+       // Lock the mutex
+       pthread_mutex_lock( &decoder_lock );
+
+       // Create the properties if necessary
+       if ( dv_decoders == NULL )
+       {
+               // Create the properties
+               dv_decoders = mlt_properties_new( );
+
+               // Create the stack
+               mlt_properties_set_data( dv_decoders, "stack", mlt_deque_init( ), 0, ( mlt_destructor )mlt_deque_close, NULL );
+
+               // Register the properties for clean up
+               mlt_factory_register_for_clean_up( dv_decoders, ( mlt_destructor )mlt_properties_close );
+       }
+
+       // Now try to obtain a decoder
+       if ( dv_decoders != NULL )
+       {
+               // Obtain the stack
+               mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
+
+               // Pop the top of the stack
+               this = mlt_deque_pop_back( stack );
+
+               // Create a new decoder if none available
+               if ( this == NULL )
+               {
+                       // We'll need a unique property ID for this
+                       char label[ 256 ];
+
+                       // Configure the decoder
+                       this = dv_decoder_new( FALSE, FALSE, FALSE );
+                       this->quality = DV_QUALITY_COLOR | DV_QUALITY_AC_2;
+                       this->audio->arg_audio_emphasis = 2;
+                       dv_set_audio_correction( this, DV_AUDIO_CORRECT_AVERAGE );
+                       dv_set_error_log( this, NULL );
+
+                       // Register it with the properties to ensure clean up
+                       sprintf( label, "%p", this );
+                       mlt_properties_set_data( dv_decoders, label, this, 0, ( mlt_destructor )dv_decoder_free, NULL );
+               }
+       }
+
+       // Unlock the mutex
+       pthread_mutex_unlock( &decoder_lock );
+
+       return this;
+}
+
+void dv_decoder_return( dv_decoder_t *this )
+{
+       // Lock the mutex
+       pthread_mutex_lock( &decoder_lock );
+
+       // Now try to return the decoder
+       if ( dv_decoders != NULL )
+       {
+               // Obtain the stack
+               mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
+
+               // Push it back
+               mlt_deque_push_back( stack, this );
+       }
+
+       // Unlock the mutex
+       pthread_mutex_unlock( &decoder_lock );
+}
+
+
+typedef struct producer_libdv_s *producer_libdv;
+
+struct producer_libdv_s
+{
+       struct mlt_producer_s parent;
+       int fd;
+       int is_pal;
+       uint64_t file_size;
+       int frame_size;
+       long frames_in_file;
+       mlt_producer alternative;
+};
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+static int producer_collect_info( producer_libdv this, mlt_profile profile );
+
+mlt_producer producer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+       producer_libdv this = calloc( sizeof( struct producer_libdv_s ), 1 );
+
+       if ( filename != NULL && this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+       {
+               int destroy = 0;
+               mlt_producer producer = &this->parent;
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+               // Set the resource property (required for all producers)
+               mlt_properties_set( properties, "resource", filename );
+
+               // Register transport implementation with the producer
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Register our get_frame implementation with the producer
+               producer->get_frame = producer_get_frame;
+
+               // If we have mov or dv, then we'll use an alternative producer
+               if ( strchr( filename, '.' ) != NULL && (
+                        strncasecmp( strrchr( filename, '.' ), ".avi", 4 ) == 0 ||
+                        strncasecmp( strrchr( filename, '.' ), ".mov", 4 ) == 0 ) )
+               {
+                       // Load via an alternative mechanism
+                       mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
+                       this->alternative = mlt_factory_producer( profile, "kino", filename );
+
+                       // If it's unavailable, then clean up
+                       if ( this->alternative == NULL )
+                               destroy = 1;
+                       else
+                               mlt_properties_pass( properties, MLT_PRODUCER_PROPERTIES( this->alternative ), "" );
+                       this->is_pal = ( ( int ) mlt_producer_get_fps( producer ) ) == 25;
+               }
+               else
+               {
+                       // Open the file if specified
+                       this->fd = open( filename, O_RDONLY );
+
+                       // Collect info
+                       if ( this->fd == -1 || !producer_collect_info( this, profile ) )
+                               destroy = 1;
+               }
+
+               // If we couldn't open the file, then destroy it now
+               if ( destroy )
+               {
+                       mlt_producer_close( producer );
+                       producer = NULL;
+               }
+
+               // Return the producer
+               return producer;
+       }
+       free( this );
+       return NULL;
+}
+
+static int read_frame( int fd, uint8_t* frame_buf, int *isPAL )
+{
+       int result = read( fd, frame_buf, FRAME_SIZE_525_60 ) == FRAME_SIZE_525_60;
+       if ( result )
+       {
+               *isPAL = ( frame_buf[3] & 0x80 );
+               if ( *isPAL )
+               {
+                       int diff = FRAME_SIZE_625_50 - FRAME_SIZE_525_60;
+                       result = read( fd, frame_buf + FRAME_SIZE_525_60, diff ) == diff;
+               }
+       }
+       
+       return result;
+}
+
+static int producer_collect_info( producer_libdv this, mlt_profile profile )
+{
+       int valid = 0;
+
+       uint8_t *dv_data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+       if ( dv_data != NULL )
+       {
+               // Read the first frame
+               valid = read_frame( this->fd, dv_data, &this->is_pal );
+
+               // If it looks like a valid frame, the get stats
+               if ( valid )
+               {
+                       // Get the properties
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+                       // Get a dv_decoder
+                       dv_decoder_t *dv_decoder = dv_decoder_alloc( );
+
+                       // Determine the file size
+                       struct stat buf;
+                       fstat( this->fd, &buf );
+
+                       // Store the file size
+                       this->file_size = buf.st_size;
+
+                       // Determine the frame size
+                       this->frame_size = this->is_pal ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
+
+                       // Determine the number of frames in the file
+                       this->frames_in_file = this->file_size / this->frame_size;
+
+                       // Calculate default in/out points
+                       int fps = 1000 * ( this->is_pal ? 25 : ( 30000.0 / 1001.0 ) );
+                       if ( ( int )( mlt_profile_fps( profile ) * 1000 ) == fps )
+                       {
+                               if ( this->frames_in_file > 0 )
+                               {
+                                       mlt_properties_set_position( properties, "length", this->frames_in_file );
+                                       mlt_properties_set_position( properties, "in", 0 );
+                                       mlt_properties_set_position( properties, "out", this->frames_in_file - 1 );
+                               }
+                       }
+                       else
+                       {
+                               valid = 0;
+                       }
+
+                       // Parse the header for meta info
+                       dv_parse_header( dv_decoder, dv_data );
+                       mlt_properties_set_double( properties, "aspect_ratio", 
+                               dv_format_wide( dv_decoder ) ? ( this->is_pal ? 118.0/81.0 : 40.0/33.0 ) : ( this->is_pal ? 59.0/54.0 : 10.0/11.0 ) );
+                       mlt_properties_set_double( properties, "source_fps", this->is_pal ? 25 : ( 30000.0 / 1001.0 ) );
+                       mlt_properties_set_int( properties, "meta.media.nb_streams", 2 );
+                       mlt_properties_set_int( properties, "video_index", 0 );
+                       mlt_properties_set( properties, "meta.media.0.stream.type", "video" );
+                       mlt_properties_set( properties, "meta.media.0.codec.name", "dvvideo" );
+                       mlt_properties_set( properties, "meta.media.0.codec.long_name", "DV (Digital Video)" );
+                       mlt_properties_set_int( properties, "audio_index", 1 );
+                       mlt_properties_set( properties, "meta.media.1.stream.type", "audio" );
+                       mlt_properties_set( properties, "meta.media.1.codec.name", "pcm_s16le" );
+                       mlt_properties_set( properties, "meta.media.1.codec.long_name", "signed 16-bit little-endian PCM" );
+
+                       // Return the decoder
+                       dv_decoder_return( dv_decoder );
+               }
+
+               mlt_pool_release( dv_data );
+       }
+
+       return valid;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int pitches[3] = { 0, 0, 0 };
+       uint8_t *pixels[3] = { NULL, NULL, NULL };
+       
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get a dv_decoder
+       dv_decoder_t *decoder = dv_decoder_alloc( );
+
+       // Get the dv data
+       uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
+
+       // Get and set the quality request
+       char *quality = mlt_frame_pop_service( this );
+
+       if ( quality != NULL )
+       {
+               if ( strncmp( quality, "fast", 4 ) == 0 )
+                       decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_DC );
+               else if ( strncmp( quality, "best", 4 ) == 0 )
+                       decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_2 );
+               else
+                       decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_1 );
+       }
+
+       // Parse the header for meta info
+       dv_parse_header( decoder, dv_data );
+       
+       // Assign width and height according to the frame
+       *width = 720;
+       *height = dv_data[ 3 ] & 0x80 ? 576 : 480;
+
+       // Extract an image of the format requested
+       if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+       {
+               // Allocate an image
+               uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 );
+
+               // Pass to properties for clean up
+               mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+
+               // Decode the image
+               pitches[ 0 ] = *width * 2;
+               pixels[ 0 ] = image;
+               dv_decode_full_frame( decoder, dv_data, e_dv_color_yuv, pixels, pitches );
+
+               // Assign result
+               *buffer = image;
+               *format = mlt_image_yuv422;
+       }
+       else
+       {
+               // Allocate an image
+               uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 3 );
+
+               // Pass to properties for clean up
+               mlt_properties_set_data( properties, "image", image, *width * ( *height + 1 ) * 3, ( mlt_destructor )mlt_pool_release, NULL );
+
+               // Decode the frame
+               pitches[ 0 ] = 720 * 3;
+               pixels[ 0 ] = image;
+               dv_decode_full_frame( decoder, dv_data, e_dv_color_rgb, pixels, pitches );
+
+               // Assign result
+               *buffer = image;
+               *format = mlt_image_rgb24;
+       }
+
+       // Return the decoder
+       dv_decoder_return( decoder );
+
+       return 0;
+}
+
+static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       int16_t *p;
+       int i, j;
+       int16_t *audio_channels[ 4 ];
+       
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get a dv_decoder
+       dv_decoder_t *decoder = dv_decoder_alloc( );
+
+       // Get the dv data
+       uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
+
+       // Parse the header for meta info
+       dv_parse_header( decoder, dv_data );
+
+       // Check that we have audio
+       if ( decoder->audio->num_channels > 0 )
+       {
+               // Obtain required values
+               *frequency = decoder->audio->frequency;
+               *samples = decoder->audio->samples_this_frame;
+               *channels = decoder->audio->num_channels;
+
+               // Create a temporary workspace
+               for ( i = 0; i < 4; i++ )
+                       audio_channels[ i ] = mlt_pool_alloc( DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) );
+       
+               // Create a workspace for the result
+               *buffer = mlt_pool_alloc( *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) );
+       
+               // Pass the allocated audio buffer as a property
+               mlt_properties_set_data( properties, "audio", *buffer, *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ), ( mlt_destructor )mlt_pool_release, NULL );
+       
+               // Decode the audio
+               dv_decode_full_audio( decoder, dv_data, audio_channels );
+               
+               // Interleave the audio
+               p = *buffer;
+               for ( i = 0; i < *samples; i++ )
+                       for ( j = 0; j < *channels; j++ )
+                               *p++ = audio_channels[ j ][ i ];
+       
+               // Free the temporary work space
+               for ( i = 0; i < 4; i++ )
+                       mlt_pool_release( audio_channels[ i ] );
+       }
+       else
+       {
+               // No audio available on the frame, so get test audio (silence)
+               mlt_frame_get_audio( this, buffer, format, frequency, channels, samples );
+       }
+
+       // Return the decoder
+       dv_decoder_return( decoder );
+
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Access the private data
+       producer_libdv this = producer->child;
+
+       // Will carry the frame data
+       uint8_t *data = NULL;
+
+       // Obtain the current frame number
+       uint64_t position = mlt_producer_frame( producer );
+       
+       if ( this->alternative == NULL )
+       {
+               // Convert timecode to a file position (ensuring that we're on a frame boundary)
+               uint64_t offset = position * this->frame_size;
+
+               // Allocate space
+               data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+
+               // Create an empty frame
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+               // Seek and fetch
+               if ( this->fd != 0 && 
+                        lseek( this->fd, offset, SEEK_SET ) == offset &&
+                        read_frame( this->fd, data, &this->is_pal ) )
+               {
+                       // Pass the dv data
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL );
+               }
+               else
+               {
+                       mlt_pool_release( data );
+                       data = NULL;
+               }
+       }
+       else
+       {
+               // Seek
+               mlt_producer_seek( this->alternative, position );
+               
+               // Fetch
+               mlt_service_get_frame( MLT_PRODUCER_SERVICE( this->alternative ), frame, 0 );
+
+               // Verify
+               if ( *frame != NULL )
+                       data = mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", NULL );
+       }
+
+       if ( data != NULL )
+       {
+               // Get the frames properties
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+       
+               // Get a dv_decoder
+               dv_decoder_t *dv_decoder = dv_decoder_alloc( );
+
+               mlt_properties_set_int( properties, "test_image", 0 );
+               mlt_properties_set_int( properties, "test_audio", 0 );
+
+               // Update other info on the frame
+               mlt_properties_set_int( properties, "width", 720 );
+               mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 );
+               mlt_properties_set_int( properties, "real_width", 720 );
+               mlt_properties_set_int( properties, "real_height", this->is_pal ? 576 : 480 );
+               mlt_properties_set_int( properties, "top_field_first", !this->is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
+       
+               // Parse the header for meta info
+               dv_parse_header( dv_decoder, data );
+               //mlt_properties_set_int( properties, "progressive", dv_is_progressive( dv_decoder ) );
+               mlt_properties_set_double( properties, "aspect_ratio", 
+                               dv_format_wide( dv_decoder ) ? ( this->is_pal ? 118.0/81.0 : 40.0/33.0 ) : ( this->is_pal ? 59.0/54.0 : 10.0/11.0 ) );
+       
+
+               mlt_properties_set_int( properties, "frequency", dv_decoder->audio->frequency );
+               mlt_properties_set_int( properties, "channels", dv_decoder->audio->num_channels );
+
+               // Register audio callback
+               if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "audio_index" ) > 0 )
+                       mlt_frame_push_audio( *frame, producer_get_audio );
+       
+               if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "video_index" ) > -1 )
+               {
+                       // Push the quality string
+                       mlt_frame_push_service( *frame, mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "quality" ) );
+
+                       // Push the get_image method on to the stack
+                       mlt_frame_push_get_image( *frame, producer_get_image );
+               }
+       
+               // Return the decoder
+               dv_decoder_return( dv_decoder );
+       }
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       // Obtain this
+       producer_libdv this = parent->child;
+
+       // Close the file
+       if ( this->fd > 0 )
+               close( this->fd );
+
+       if ( this->alternative )
+               mlt_producer_close( this->alternative );
+
+       // Close the parent
+       parent->close = NULL;
+       mlt_producer_close( parent );
+
+       // Free the memory
+       free( this );
+}
diff --git a/src/modules/effectv/Makefile b/src/modules/effectv/Makefile
new file mode 100644 (file)
index 0000000..a8f5f62
--- /dev/null
@@ -0,0 +1,35 @@
+include ../../../config.mak
+
+TARGET = ../libmlteffectv$(LIBSUF)
+
+OBJS = factory.o \
+          filter_burn.o \
+          image.o \
+          utils.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/effectv/factory.c b/src/modules/effectv/factory.c
new file mode 100644 (file)
index 0000000..e28d765
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2007 Stephane Fillod
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+
+extern mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "BurningTV", filter_burn_init );
+       MLT_REGISTER( filter_type, "burningtv", filter_burn_init );
+}
diff --git a/src/modules/effectv/filter_burn.c b/src/modules/effectv/filter_burn.c
new file mode 100644 (file)
index 0000000..967e263
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * filter_burn.c -- burning filter
+ * Copyright (C) 2007 Stephane Fillod
+ *
+ * Filter taken from EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * BurningTV - burns incoming objects.
+ * Copyright (C) 2001-2002 FUKUCHI Kentaro
+ *
+ * Fire routine is taken from Frank Jan Sorensen's demo program.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "utils.h"
+
+
+#define MaxColor 120
+#define Decay 15
+#define MAGIC_THRESHOLD "50"
+
+static RGB32 palette[256];
+
+/* FIXME: endianess? */
+static void makePalette(void)
+{
+       int i, r, g, b;
+
+       for(i=0; i<MaxColor; i++) {
+               HSItoRGB(4.6-1.5*i/MaxColor, (double)i/MaxColor, (double)i/MaxColor, &r, &g, &b);
+               palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
+       }
+       for(i=MaxColor; i<256; i++) {
+               if(r<255)r++;if(r<255)r++;if(r<255)r++;
+               if(g<255)g++;
+               if(g<255)g++;
+               if(b<255)b++;
+               if(b<255)b++;
+               palette[i] = ((r<<16)|(g<<8)|b) & 0xfefeff;
+       }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       RGB32 *background;
+       unsigned char *diff;
+       unsigned char *buffer;
+
+       // Get the filter
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // Get the "Burn the foreground" value
+               int burn_foreground = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "foreground" );
+               int y_threshold = image_set_threshold_y(
+                               mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "threshold" ));
+
+               // We'll process pixel by pixel
+               int x = 0;
+               int y = 0;
+               int i;
+
+               int video_width = *width;
+               int video_height = *height;
+               int video_area = video_width * video_height;
+               // We need to create a new frame as this effect modifies the input
+               RGB32 *dest = mlt_pool_alloc( video_area * sizeof(RGB32) );
+               RGB32 *src = (RGB32*)dest;
+
+               unsigned char v, w;
+               RGB32 a, b;
+
+               mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)dest, video_area);
+
+
+               diff = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
+                                               "_diff", NULL );
+               if (diff == NULL)
+               {
+                       diff = mlt_pool_alloc(video_area*sizeof(unsigned char));
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_diff", 
+                                       diff, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
+               }
+
+               buffer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
+                                               "_buffer", NULL );
+               if (buffer == NULL)
+               {
+                       buffer = mlt_pool_alloc(video_area*sizeof(unsigned char));
+                       memset(buffer, 0, video_area*sizeof(unsigned char));
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_buffer", 
+                                       buffer, video_area*sizeof(unsigned char), mlt_pool_release, NULL );
+               }
+
+
+               if (burn_foreground == 1) {
+                       /* to burn the foreground, we need a background */
+                       background = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), 
+                                               "_background", NULL );
+                       if (background == NULL)
+                       {
+                               background = mlt_pool_alloc(video_area*sizeof(RGB32));
+                               image_bgset_y(background, src, video_area, y_threshold);
+                               mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "_background", 
+                                       background, video_area*sizeof(RGB32), mlt_pool_release, NULL );
+                       }
+               }
+
+               if (burn_foreground == 1) {
+                       image_bgsubtract_y(diff, background, src, video_area, y_threshold);
+               } else {
+                       /* default */
+                       image_y_over(diff, src, video_area, y_threshold);
+               }
+       
+               for(x=1; x<video_width-1; x++) {
+                       v = 0;
+                       for(y=0; y<video_height-1; y++) {
+                               w = diff[y*video_width+x];
+                               buffer[y*video_width+x] |= v ^ w;
+                               v = w;
+                       }
+               }
+               for(x=1; x<video_width-1; x++) {
+                       i = video_width + x;
+                       for(y=1; y<video_height; y++) {
+                               v = buffer[i];
+                               if(v<Decay)
+                                       buffer[i-video_width] = 0;
+                               else
+                                       buffer[i-video_width+fastrand()%3-1] = v - (fastrand()&Decay);
+                               i += video_width;
+                       }
+               }
+       
+               i = 1;
+               for(y=0; y<video_height; y++) {
+                       for(x=1; x<video_width-1; x++) {
+                               /* FIXME: endianess? */
+                               a = (src[i] & 0xfefeff) + palette[buffer[i]];
+                               b = a & 0x1010100;
+                               dest[i] = a | (b - (b >> 8));
+                               i++;
+                       }
+                       i += 2;
+               }
+
+               mlt_convert_rgb24a_to_yuv422((uint8_t *)dest, *width, *height, *width * sizeof(RGB32), 
+                               *image, NULL );
+
+               mlt_pool_release(dest);
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the frame filter
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "foreground", "0" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "threshold", MAGIC_THRESHOLD );
+       }
+       if (!palette[128])
+       {
+               makePalette();
+       }
+       return this;
+}
+
diff --git a/src/modules/effectv/gpl b/src/modules/effectv/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/effectv/image.c b/src/modules/effectv/image.c
new file mode 100644 (file)
index 0000000..ce8157f
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * image.c: utilities for image processing.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "utils.h"
+
+
+/*
+ * Collection of background subtraction functions
+ */
+
+/* checks only fake-Y value */
+/* In these function Y value is treated as R*2+G*4+B. */
+
+int image_set_threshold_y(int threshold)
+{
+       int y_threshold = threshold * 7; /* fake-Y value is timed by 7 */
+
+       return y_threshold;
+}
+
+void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+       int i;
+       int R, G, B;
+       const RGB32 *p;
+       short *q;
+
+       p = src;
+       q = (short *)background;
+       for(i=0; i<video_area; i++) {
+               /* FIXME: endianess */
+
+               R = ((*p)&0xff0000)>>(16-1);
+               G = ((*p)&0xff00)>>(8-2);
+               B = (*p)&0xff;
+               *q = (short)(R + G + B);
+               p++;
+               q++;
+       }
+}
+
+void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+       int i;
+       int R, G, B;
+       const RGB32 *p;
+       const short *q;
+       unsigned char *r;
+       int v;
+
+       p = src;
+       q = (const short *)background;
+       r = diff;
+       for(i=0; i<video_area; i++) {
+               /* FIXME: endianess */
+
+               R = ((*p)&0xff0000)>>(16-1);
+               G = ((*p)&0xff00)>>(8-2);
+               B = (*p)&0xff;
+               v = (R + G + B) - (int)(*q);
+               *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24);
+
+               p++;
+               q++;
+               r++;
+       }
+
+/* The origin of subtraction function is;
+ * diff(src, dest) = (abs(src - dest) > threshold) ? 0xff : 0;
+ *
+ * This functions is transformed to;
+ * (threshold > (src - dest) > -threshold) ? 0 : 0xff;
+ *
+ * (v + threshold)>>24 is 0xff when v is less than -threshold.
+ * (v - threshold)>>24 is 0xff when v is less than threshold.
+ * So, ((v + threshold)>>24) | ((threshold - v)>>24) will become 0xff when
+ * abs(src - dest) > threshold.
+ */
+}
+
+/* Background image is refreshed every frame */
+void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold)
+{
+       int i;
+       int R, G, B;
+       const RGB32 *p;
+       short *q;
+       unsigned char *r;
+       int v;
+
+       p = src;
+       q = (short *)background;
+       r = diff;
+       for(i=0; i<video_area; i++) {
+               /* FIXME: endianess */
+
+               R = ((*p)&0xff0000)>>(16-1);
+               G = ((*p)&0xff00)>>(8-2);
+               B = (*p)&0xff;
+               v = (R + G + B) - (int)(*q);
+               *q = (short)(R + G + B);
+               *r = ((v + y_threshold)>>24) | ((y_threshold - v)>>24);
+
+               p++;
+               q++;
+               r++;
+       }
+}
+
+/* checks each RGB value */
+
+/* The range of r, g, b are [0..7] */
+RGB32 image_set_threshold_RGB(int r, int g, int b)
+{
+       unsigned char R, G, B;
+       RGB32 rgb_threshold;
+
+       R = G = B = 0xff;
+       R = R<<r;
+       G = G<<g;
+       B = B<<b;
+       rgb_threshold = (RGB32)(R<<16 | G<<8 | B);
+
+       return rgb_threshold;
+}
+
+void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area)
+{
+       int i;
+       RGB32 *p;
+
+       p = background;
+       for(i=0; i<video_area; i++) {
+               *p++ = (*src++) & 0xfefefe;
+       }
+}
+
+void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold)
+{
+       int i;
+       const RGB32 *p, *q;
+       unsigned a, b;
+       unsigned char *r;
+
+       p = src;
+       q = background;
+       r = diff;
+       for(i=0; i<video_area; i++) {
+               a = (*p++)|0x1010100;
+               b = *q++;
+               a = a - b;
+               b = a & 0x1010100;
+               b = b - (b>>8);
+               b = b ^ 0xffffff;
+               a = a ^ b;
+               a = a & rgb_threshold;
+               *r++ = (0 - a)>>24;
+       }
+}
+
+void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold)
+{
+       int i;
+       const RGB32 *p;
+       RGB32 *q;
+       unsigned a, b;
+       unsigned char *r;
+
+       p = src;
+       q = background;
+       r = diff;
+       for(i=0; i<video_area; i++) {
+               a = *p|0x1010100;
+               b = *q&0xfefefe;
+               *q++ = *p++;
+               a = a - b;
+               b = a & 0x1010100;
+               b = b - (b>>8);
+               b = b ^ 0xffffff;
+               a = a ^ b;
+               a = a & rgb_threshold;
+               *r++ = (0 - a)>>24;
+       }
+}
+
+/* noise filter for subtracted image. */
+void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height)
+{
+       int x, y;
+       const unsigned char *src;
+       unsigned char *dest;
+       unsigned int count;
+       unsigned int sum1, sum2, sum3;
+
+       src = diff;
+       dest = diff2 + width +1;
+       for(y=1; y<height-1; y++) {
+               sum1 = src[0] + src[width] + src[width*2];
+               sum2 = src[1] + src[width+1] + src[width*2+1];
+               src += 2;
+               for(x=1; x<width-1; x++) {
+                       sum3 = src[0] + src[width] + src[width*2];
+                       count = sum1 + sum2 + sum3;
+                       sum1 = sum2;
+                       sum2 = sum3;
+                       *dest++ = (0xff*3 - count)>>24;
+                       src++;
+               }
+               dest += 2;
+       }
+}
+
+/* Y value filters */
+void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold)
+{
+       int i;
+       int R, G, B, v;
+       unsigned char *p = diff;
+
+       for(i = video_area; i>0; i--) {
+               R = ((*src)&0xff0000)>>(16-1);
+               G = ((*src)&0xff00)>>(8-2);
+               B = (*src)&0xff;
+               v = y_threshold - (R + G + B);
+               *p = (unsigned char)(v>>24);
+               src++;
+               p++;
+       }
+}
+
+void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold)
+{
+       int i;
+       int R, G, B, v;
+       unsigned char *p = diff;
+
+       for(i = video_area; i>0; i--) {
+               R = ((*src)&0xff0000)>>(16-1);
+               G = ((*src)&0xff00)>>(8-2);
+               B = (*src)&0xff;
+               v = (R + G + B) - y_threshold;
+               *p = (unsigned char)(v>>24);
+               src++;
+               p++;
+       }
+}
+
+/* tiny edge detection */
+void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold)
+{
+       int x, y;
+       const unsigned char *p;
+       unsigned char *q;
+       int r, g, b;
+       int ar, ag, ab;
+       int w;
+
+       p = (const unsigned char *)src;
+       q = diff2;
+       w = width * sizeof(RGB32);
+
+       for(y=0; y<height - 1; y++) {
+               for(x=0; x<width - 1; x++) {
+                       b = p[0];
+                       g = p[1];
+                       r = p[2];
+                       ab = abs(b - p[4]);
+                       ag = abs(g - p[5]);
+                       ar = abs(r - p[6]);
+                       ab += abs(b - p[w]);
+                       ag += abs(g - p[w+1]);
+                       ar += abs(r - p[w+2]);
+                       b = ab+ag+ar;
+                       if(b > y_threshold) {
+                               *q = 255;
+                       } else {
+                               *q = 0;
+                       }
+                       q++;
+                       p += 4;
+               }
+               p += 4;
+               *q++ = 0;
+       }
+       memset(q, 0, width);
+}
+
+/* horizontal flipping */
+void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height)
+{
+       int x, y;
+
+       src += width - 1;
+       for(y=0; y<height; y++) {
+               for(x=0; x<width; x++) {
+                       *dest++ = *src--;
+               }
+               src += width * 2;
+       }
+}
+
diff --git a/src/modules/effectv/utils.c b/src/modules/effectv/utils.c
new file mode 100644 (file)
index 0000000..902d5f1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * utils.c: utilities
+ *
+ */
+
+#include <math.h>
+#include "utils.h"
+
+/*
+ * HSI color system utilities
+ */
+static int itrunc(double f)
+{
+       int i;
+
+       i=(int)f;
+       if(i<0)i=0;
+       if(i>255)i=255;
+       return i;
+}
+
+void HSItoRGB(double H, double S, double I, int *r, int *g, int *b)
+{
+       double T,Rv,Gv,Bv;
+
+       Rv=1+S*sin(H-2*M_PI/3);
+       Gv=1+S*sin(H);
+       Bv=1+S*sin(H+2*M_PI/3);
+       T=255.999*I/2;
+       *r=itrunc(Rv*T);
+       *g=itrunc(Gv*T);
+       *b=itrunc(Bv*T);
+}
+
+/*
+ * fastrand - fast fake random number generator
+ * Warning: The low-order bits of numbers generated by fastrand()
+ *          are bad as random numbers. For example, fastrand()%4
+ *          generates 1,2,3,0,1,2,3,0...
+ *          You should use high-order bits.
+ */
+#ifdef __DARWIN__
+static
+#endif
+unsigned int fastrand_val;
+
+unsigned int fastrand(void)
+{
+       return (fastrand_val=fastrand_val*1103515245+12345);
+}
+
+void fastsrand(unsigned int seed)
+{
+       fastrand_val = seed;
+}
diff --git a/src/modules/effectv/utils.h b/src/modules/effectv/utils.h
new file mode 100644 (file)
index 0000000..4461898
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * EffecTV - Realtime Digital Video Effector
+ * Copyright (C) 2001-2006 FUKUCHI Kentaro
+ *
+ * utils.h: header file for utils
+ *
+ */
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <inttypes.h>
+
+typedef uint32_t RGB32;
+
+/* DEFINE's by nullset@dookie.net */
+#define RED(n)  ((n>>16) & 0x000000FF)
+#define GREEN(n) ((n>>8) & 0x000000FF)
+#define BLUE(n)  ((n>>0) & 0x000000FF)
+#define RGB(r,g,b) ((0<<24) + (r<<16) + (g <<8) + (b))
+#define INTENSITY(n)   ( ( (RED(n)+GREEN(n)+BLUE(n))/3))
+
+/* utils.c */
+void HSItoRGB(double H, double S, double I, int *r, int *g, int *b);
+
+#ifndef __DARWIN__
+extern unsigned int fastrand_val;
+#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345)
+#endif
+unsigned int fastrand(void);
+void fastsrand(unsigned int);
+
+/* image.c */
+int image_set_threshold_y(int threshold);
+void image_bgset_y(RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+void image_bgsubtract_y(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+void image_bgsubtract_update_y(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, int y_threshold);
+RGB32 image_set_threshold_RGB(int r, int g, int b);
+void image_bgset_RGB(RGB32 *background, const RGB32 *src, int video_area);
+void image_bgsubtract_RGB(unsigned char *diff, const RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold);
+void image_bgsubtract_update_RGB(unsigned char *diff, RGB32 *background, const RGB32 *src, int video_area, RGB32 rgb_threshold);
+void image_diff_filter(unsigned char *diff2, const unsigned char *diff, int width, int height);
+void image_y_over(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold);
+void image_y_under(unsigned char *diff, const RGB32 *src, int video_area, int y_threshold);
+void image_edge(unsigned char *diff2, const RGB32 *src, int width, int height, int y_threshold);
+void image_hflip(const RGB32 *src, RGB32 *dest, int width, int height);
+
+#endif /* __UTILS_H__ */
diff --git a/src/modules/feeds/Makefile b/src/modules/feeds/Makefile
new file mode 100644 (file)
index 0000000..748ce47
--- /dev/null
@@ -0,0 +1,15 @@
+include ../../../config.mak
+
+all:   
+
+depend:
+
+distclean:
+
+clean:
+
+install:       all
+       install -d $(DESTDIR)$(prefix)/share/mlt/feeds/PAL
+       install -d $(DESTDIR)$(prefix)/share/mlt/feeds/NTSC
+       install -m 644 PAL/*.* $(DESTDIR)$(prefix)/share/mlt/feeds/PAL
+       install -m 644 NTSC/*.* $(DESTDIR)$(prefix)/share/mlt/feeds/NTSC
diff --git a/src/modules/feeds/NTSC/data_fx.properties b/src/modules/feeds/NTSC/data_fx.properties
new file mode 100644 (file)
index 0000000..03832aa
--- /dev/null
@@ -0,0 +1,250 @@
+# This properties file describes the fx available to the data_feed and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
+#
+# ETV Location
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,80:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:100;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;24=0%,0%:100%x100%:0;49=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=-100%,0%:100%x100%:0;12=-100%,0%:100%x100%:0;37=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:0;37=0%,0%:100%x100%:0;61=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+exclusive=region
+.description=Exclusive
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=0,115:230x30
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=ETV Exclusive
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=right
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=590,160:80x25
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=File Shot
+.filter[1].producer.font=San 20
+.filter[1].composite.geometry=1%,1%:99%x99%:15;25=1%,1%:99%x99%:100
+.filter[1].composite.titles=0
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Titles
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Special
+.filter[1].producer.font=San 24
+.filter[1].composite.geometry=100%,0%:100%x100%:0;49=100%,0%:100%x100%:0;74=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c010100
+.filter[0].composite.geometry=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Ticker - provided for reference
+.filter[1].composite.geometry=0%,0%:100%x100%:100
+.filter[1].composite.titles=0
+.filter[1].producer.font=San 24
+.filter[1].composite.halign=centre
+.filter[1].composite.titles=1
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.markup
+.properties.1=filter[2].producer.markup
+.properties.align=filter[1].composite.valign
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.properties.length[2]=filter[2].composite.out
+.period=2
+.composite.geometry=0,410:720x90
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbb00
+.filter[0].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[0].composite.titles=1
+.filter[0].composite.luma=%luma18.pgm
+.filter[0].composite.out=25
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=
+.filter[1].producer.font=San 32
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.markup=
+.filter[2].producer.font=San 32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.geometry=0%,0%:100%x100%:10;25=0%,0%:100%x100%:100
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
diff --git a/src/modules/feeds/NTSC/obscure.properties b/src/modules/feeds/NTSC/obscure.properties
new file mode 100644 (file)
index 0000000..7eba160
--- /dev/null
@@ -0,0 +1,26 @@
+# This properties file describes the fx available to the data_feed and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+obscure=region
+.description=Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+.filter[0].start=0,0:100%x100%
+
diff --git a/src/modules/feeds/PAL/border.properties b/src/modules/feeds/PAL/border.properties
new file mode 100644 (file)
index 0000000..e02ab35
--- /dev/null
@@ -0,0 +1,22 @@
+border_left=watermark
+.description=Border Left
+.resource=colour:black
+.reverse=1
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,0:100%x100%;25=2.5%,17.5%:45%x45%
+.composite.halign=c
+.composite.valign=c
+.composite.fill=1
+
+border_right=watermark
+.description=Border Right
+.resource=colour:black
+.reverse=1
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,0:100%x100%;25=52.5%,17.5%:45%x45%
+.composite.halign=c
+.composite.valign=c
+.composite.fill=1
+
diff --git a/src/modules/feeds/PAL/data_fx.properties b/src/modules/feeds/PAL/data_fx.properties
new file mode 100644 (file)
index 0000000..e2e483b
--- /dev/null
@@ -0,0 +1,76 @@
+# This properties file describes the fx available to the data_send and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+.description=Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,70%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+.description=Top Titles
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.period=2
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=5%,5%:90%x20%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[0].composite.geometry=0%,0%:100%x100%:0;5=0%,0%:100%x100%:40
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=0%,0%:100%x100%:0;8=0%,0%:100%x100%:100
+.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+.description=Tickertape
+.properties.markup=filter[1].producer.markup
+.type.markup=text
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0%,93%:100%x7%
+.filter[0]=watermark
+.filter[0].resource=colour:0x000000
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.markup=Shotcut
+.filter[1].composite.geometry=100%,0%:300%x100%:100;-1=-300%,0%:300%x100%:100
+.filter[1].producer.font=San 32
+.filter[1].composite.titles=1
+
diff --git a/src/modules/feeds/PAL/etv.properties b/src/modules/feeds/PAL/etv.properties
new file mode 100644 (file)
index 0000000..07be453
--- /dev/null
@@ -0,0 +1,186 @@
+# This properties file describes the fx available to the data_feed and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+location=region
+.description=Titles
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,80:230x30:0;12=,:x:100
+.composite.luma=%luma01.pgm
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=center
+
+courtesy=region
+.description=Courtesy
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=0,115:230x30:0;12=,:x:100
+.composite.luma=%luma01.pgm
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=centre
+
+exclusive=region
+.description=Exclusive
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.period=2
+.properties.length[0]=composite.out
+.composite.geometry=-230,115:230x30;12=0
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=ETV Exclusive
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.geometry=0,0:95%x100%
+.filter[1].composite.titles=1
+.filter[1].composite.halign=right
+.filter[1].composite.valign=centre
+
+file_shot=region
+.description=Titles
+.period=2
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=composite.out
+.composite.geometry=590,160:80x25:0;12=,:x:100
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=File Shot
+.filter[1].producer.font=Sans
+.filter[1].producer.size=18
+.filter[1].producer.weight=700
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+special=region
+.description=Special
+.period=2
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=filter[0].composite.out
+.properties.length[1]=filter[1].composite.out
+.composite.geometry=465,375:255x35
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.geometry=100%,0%:100%x100%:0;12=0%,0%:x:100
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=Special
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.geometry=100%,0%:100%x100%:0;12=0%,0%:x:100
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+ticker=region
+.description=Tickertape
+.properties.markup=filter[1].producer.text
+.properties.font=filter[1].producer.font
+.properties.size=filter[1].producer.size
+.properties.length[0]=filter[1].composite.out
+.composite.geometry=0,500:722x75
+.filter[0]=watermark
+.filter[0].resource=colour:0x6c0101ff
+.filter[0].composite.titles=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=Ticker - provided for reference
+.filter[1].producer.font=Sans
+.filter[1].producer.size=24
+.filter[1].producer.weight=700
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=centre
+
+super=region
+.description=Transcription
+.properties.0=filter[1].producer.text
+.properties.1=filter[2].producer.text
+.properties.align=filter[1].composite.valign
+.properties.weight=filter[1].producer.weight
+.properties.f0=filter[1].producer.font
+.properties.s0=filter[1].producer.size
+.properties.f1=filter[2].producer.font
+.properties.s1=filter[2].producer.size
+.properties.length[0]=composite.out
+.period=2
+.composite.geometry=0,410:720x90:0;25=,:x:100
+.composite.luma=%luma01.pgm
+.composite.luma_invert=1
+.composite.softness=.3
+.filter[0]=watermark
+.filter[0].resource=colour:0xbbbbbbff
+.filter[0].composite.geometry=0,0:100%:100%:70
+.filter[0].composite.distort=1
+.filter[1]=watermark
+.filter[1].resource=pango:
+.filter[1].producer.text=
+.filter[1].producer.font=Sans
+.filter[1].producer.size=32
+.filter[1].producer.weight=700
+.filter[1].producer.fgcolour=0x6c0101ff
+.filter[1].composite.titles=1
+.filter[1].composite.halign=centre
+.filter[1].composite.valign=top
+.filter[2]=watermark
+.filter[2].resource=pango:
+.filter[2].producer.text=
+.filter[2].producer.font=Sans
+.filter[2].producer.size=32
+.filter[2].producer.fgcolour=0x6c0101ff
+.filter[2].composite.titles=1
+.filter[2].composite.halign=centre
+.filter[2].composite.valign=bottom
+
diff --git a/src/modules/feeds/PAL/example.properties b/src/modules/feeds/PAL/example.properties
new file mode 100644 (file)
index 0000000..0509fff
--- /dev/null
@@ -0,0 +1,12 @@
+greyscale=greyscale
+.description=Greyscale
+
+sepia=sepia
+.description=Sepia
+
+charcoal=charcoal
+.description=Charcoal
+
+invert=invert
+.description=Invert
+
diff --git a/src/modules/feeds/PAL/obscure.properties b/src/modules/feeds/PAL/obscure.properties
new file mode 100644 (file)
index 0000000..3917d9a
--- /dev/null
@@ -0,0 +1,35 @@
+# This properties file describes the fx available to the data_feed and 
+# data_show filters
+#
+# Syntax is as follows:
+#
+#      name=<filter>
+#      name.description=<user defined>
+#      name.properties.<variable>=<full-property>
+#      name.<property>=value
+#      etc
+#
+# Typically, the <filter> is a 'region' and additional filters are 
+# included as properties using the normal region filter syntax.
+#
+
+obscure0=region
+.description=Primary Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+
+obscure1=region
+.description=Secondary Obscure
+.properties.geometry=composite.geometry
+.properties.resource=resource
+.properties.length[0]=composite.out
+.composite.geometry=
+.resource=rectangle
+.composite.refresh=1
+.filter[0]=obscure
+
diff --git a/src/modules/fezzik.dict b/src/modules/fezzik.dict
new file mode 100644 (file)
index 0000000..bdcc0e1
--- /dev/null
@@ -0,0 +1,39 @@
+http://*=avformat
+<?xml*=westley-xml
+*.westley=westley
+*.kdenlive=westley
+*.inigo=inigo_file
+*.asf=avformat
+*.avi=mcdv,avformat,libdv
+*.bmp=pixbuf,qimage,sdl_image
+*.dv=mcdv,avformat,libdv
+*.dif=mcdv,avformat,libdv
+*.exr=qimage
+*.gif=pixbuf,qimage,sdl_image
+*.graphics=westley
+*.jfx=westley
+*.jef=westley
+*.jpg=pixbuf,qimage,sdl_image
+*.jpeg=pixbuf,qimage,sdl_image
+*.kino=westley
+*.mp3=avformat
+*.mov=mcdv,avformat,libdv
+*.mpg=mcmpeg,avformat
+*.mpeg=mcmpeg,avformat
+*.mpl=pango
+*.ogg=avformat,vorbis
+*.pcx=pixbuf,qimage,sdl_image
+*.pgm=pgm,pixbuf,qimage,sdl_image
+*.png=pixbuf,qimage,sdl_image
+*.psd=qimage
+*.story=westley
+*.svg=pixbuf,qimage
+*.tga=pixbuf,qimage,sdl_image
+*.tif=pixbuf,qimage,sdl_image
+*.tiff=pixbuf,qimage,sdl_image
+*.txt=pango
+*.vob=mcmpeg,avformat
+*.wav=avformat
+*.wmv=avformat
+*.xcf=qimage,sdl_image
+*=avformat
diff --git a/src/modules/fezzik.ini b/src/modules/fezzik.ini
new file mode 100644 (file)
index 0000000..bcb2560
--- /dev/null
@@ -0,0 +1,14 @@
+# This file defines the normalisers, their fallbacks and the order they're attached
+#
+# The names on the left are arbitrary, but the order in which they occur is the
+# order in which they're applied.
+# 
+# The names of the services on the right dictate the preference used (if unavailable
+# the second and third are applied as applicable).
+
+crop=crop:1
+deinterlace=deinterlace,avdeinterlace
+rescaler=mcrescale,gtkrescale,rescale,swscale
+resizer=resize
+resampler=resample,soxresample,avresample
+data=data_feed:attr_check
diff --git a/src/modules/fezzik/Makefile b/src/modules/fezzik/Makefile
new file mode 100644 (file)
index 0000000..52b5129
--- /dev/null
@@ -0,0 +1,36 @@
+include ../../../config.mak
+
+TARGET = ../libmltfezzik$(LIBSUF)
+
+OBJS = factory.o \
+          producer_fezzik.o \
+          producer_hold.o 
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+       install -m 644 ../fezzik.dict "$(DESTDIR)$(prefix)/share/mlt"
+       install -m 644 ../fezzik.ini "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/fezzik/factory.c b/src/modules/fezzik/factory.c
new file mode 100644 (file)
index 0000000..87f22b9
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt.h>
+#include <string.h>
+
+extern mlt_producer producer_fezzik_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( producer_type, "fezzik", producer_fezzik_init );
+       MLT_REGISTER( producer_type, "hold", producer_hold_init );
+}
diff --git a/src/modules/fezzik/producer_fezzik.c b/src/modules/fezzik/producer_fezzik.c
new file mode 100644 (file)
index 0000000..816c1bd
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * producer_fezzik.c -- a normalising filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#include <framework/mlt.h>
+
+static mlt_properties dictionary = NULL;
+static mlt_properties normalisers = NULL;
+
+static mlt_producer create_from( mlt_profile profile, char *file, char *services )
+{
+       mlt_producer producer = NULL;
+       char *temp = strdup( services );
+       char *service = temp;
+       do
+       {
+               char *p = strchr( service, ',' );
+               if ( p != NULL )
+                       *p ++ = '\0';
+               producer = mlt_factory_producer( profile, service, file );
+               service = p;
+       }
+       while ( producer == NULL && service != NULL );
+       free( temp );
+       return producer;
+}
+
+static mlt_producer create_producer( mlt_profile profile, char *file )
+{
+       mlt_producer result = NULL;
+
+       // 1st Line - check for service:resource handling
+       if ( strchr( file, ':' ) )
+       {
+               char *temp = strdup( file );
+               char *service = temp;
+               char *resource = strchr( temp, ':' );
+               *resource ++ = '\0';
+               result = mlt_factory_producer( profile, service, resource );
+               free( temp );
+       }
+
+       // 2nd Line preferences
+       if ( result == NULL )
+       {
+               int i = 0;
+               char *lookup = strdup( file );
+               char *p = lookup;
+
+               // We only need to load the dictionary once
+               if ( dictionary == NULL )
+               {
+                       char temp[ 1024 ];
+                       sprintf( temp, "%s/fezzik.dict", mlt_environment( "MLT_DATA" ) );
+                       dictionary = mlt_properties_load( temp );
+                       mlt_factory_register_for_clean_up( dictionary, ( mlt_destructor )mlt_properties_close );
+               }
+
+               // Convert the lookup string to lower case
+               while ( *p )
+               {
+                       *p = tolower( *p );
+                       p ++;
+               }
+
+               // Iterate through the dictionary
+               for ( i = 0; result == NULL && i < mlt_properties_count( dictionary ); i ++ )
+               {
+                       char *name = mlt_properties_get_name( dictionary, i );
+                       if ( fnmatch( name, lookup, 0 ) == 0 )
+                               result = create_from( profile, file, mlt_properties_get_value( dictionary, i ) );
+               }
+
+               free( lookup );
+       }
+
+       // Finally, try just loading as service
+       if ( result == NULL )
+               result = mlt_factory_producer( profile, file, NULL );
+
+       return result;
+}
+
+static void create_filter( mlt_profile profile, mlt_producer producer, char *effect, int *created )
+{
+       // The swscale filter can not handle images with a width > 2048 and the
+       // sdl_image producer does not scale on its own
+       if ( strncmp( effect, "swscale", 7 ) == 0 &&
+            mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "_real_width" ) > 2048 &&
+            strcmp( mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" ), "sdl_image" ) == 0 )
+               return;
+
+       char *id = strdup( effect );
+       char *arg = strchr( id, ':' );
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_filter filter = mlt_factory_filter( profile, id, arg );
+       if ( filter != NULL )
+       {
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_fezzik", 1 );
+               mlt_producer_attach( producer, filter );
+               mlt_filter_close( filter );
+               *created = 1;
+       }
+       free( id );
+}
+
+static void attach_normalisers( mlt_profile profile, mlt_producer producer )
+{
+       // Loop variable
+       int i;
+
+       // Tokeniser
+       mlt_tokeniser tokeniser = mlt_tokeniser_init( );
+
+       // We only need to load the normalising properties once
+       if ( normalisers == NULL )
+       {
+               char temp[ 1024 ];
+               sprintf( temp, "%s/fezzik.ini", mlt_environment( "MLT_DATA" ) );
+               normalisers = mlt_properties_load( temp );
+               mlt_factory_register_for_clean_up( normalisers, ( mlt_destructor )mlt_properties_close );
+       }
+
+       // Apply normalisers
+       for ( i = 0; i < mlt_properties_count( normalisers ); i ++ )
+       {
+               int j = 0;
+               int created = 0;
+               char *value = mlt_properties_get_value( normalisers, i );
+               mlt_tokeniser_parse_new( tokeniser, value, "," );
+               for ( j = 0; !created && j < mlt_tokeniser_count( tokeniser ); j ++ )
+                       create_filter( profile, producer, mlt_tokeniser_get_string( tokeniser, j ), &created );
+       }
+
+       // Close the tokeniser
+       mlt_tokeniser_close( tokeniser );
+}
+
+mlt_producer producer_fezzik_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the producer 
+       mlt_producer producer = NULL;
+       mlt_properties properties = NULL;
+
+       if ( arg != NULL )
+               producer = create_producer( profile, arg );
+
+       if ( producer != NULL )
+               properties = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Attach filters if we have a producer and it isn't already westley'd :-)
+       if ( producer != NULL && mlt_properties_get( properties, "westley" ) == NULL && \
+               mlt_properties_get( properties, "_westley" ) == NULL && \
+               mlt_properties_get( properties, "fezzik_normalised" ) == NULL )
+               attach_normalisers( profile, producer );
+
+       // Now make sure we don't lose our identity
+       if ( properties != NULL )
+               mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
+
+       // Return the producer
+       return producer;
+}
diff --git a/src/modules/fezzik/producer_hold.c b/src/modules/fezzik/producer_hold.c
new file mode 100644 (file)
index 0000000..f30aa0c
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * producer_hold.c -- frame holding producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+// Forward references
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer this );
+
+/** Constructor for the frame holding producer. Basically, all this producer does is
+       provide a producer wrapper for the requested producer, allows the specifcation of
+       the frame required and will then repeatedly obtain that frame for each get_frame
+       and get_image requested.
+*/
+
+mlt_producer producer_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Construct a new holding producer
+       mlt_producer this = mlt_producer_new( );
+
+       // Construct the requested producer via fezzik
+       mlt_producer producer = mlt_factory_producer( profile, "fezzik", arg );
+
+       // Initialise the frame holding capabilities
+       if ( this != NULL && producer != NULL )
+       {
+               // Get the properties of this producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+               // Store the producer
+               mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+               // Set frame, in, out and length for this producer
+               mlt_properties_set_position( properties, "frame", 0 );
+               mlt_properties_set_position( properties, "in", 0 );
+               mlt_properties_set_position( properties, "out", 25 );
+               mlt_properties_set_position( properties, "length", 15000 );
+               mlt_properties_set( properties, "resource", arg );
+               mlt_properties_set( properties, "method", "onefield" );
+
+               // Override the get_frame method
+               this->get_frame = producer_get_frame;
+               this->close = ( mlt_destructor )producer_close;
+       }
+       else
+       {
+               // Clean up (not sure which one failed, can't be bothered to find out, so close both)
+               if ( this )
+                       mlt_producer_close( this );
+               if ( producer )
+                       mlt_producer_close( producer );
+
+               // Make sure we return NULL
+               this = NULL;
+       }
+
+       // Return this producer
+       return this;
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the real frame
+       mlt_frame real_frame = mlt_frame_pop_service( frame );
+
+       // Get the image from the real frame
+       int size = 0;
+       *buffer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", &size );
+       *width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( real_frame ), "width" );
+       *height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( real_frame ), "height" );
+
+       // If this is the first time, get it from the producer
+       if ( *buffer == NULL )
+       {
+               mlt_properties_pass( MLT_FRAME_PROPERTIES( real_frame ), properties, "" );
+
+               // We'll deinterlace on the downstream deinterlacer
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( real_frame ), "consumer_deinterlace", 1 );
+
+               // We want distorted to ensure we don't hit the resize filter twice
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( real_frame ), "distort", 1 );
+
+               // Get the image
+               mlt_frame_get_image( real_frame, buffer, format, width, height, writable );
+       
+               // Make sure we get the size
+               *buffer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", &size );
+       }
+
+       mlt_properties_pass( properties, MLT_FRAME_PROPERTIES( real_frame ), "" );
+
+       // Set the values obtained on the frame
+       if ( *buffer != NULL )
+       {
+               uint8_t *image = mlt_pool_alloc( size );
+               memcpy( image, *buffer, size );
+               *buffer = image;
+               mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+       }
+       else
+       {
+               // Pass the current image as is
+               mlt_properties_set_data( properties, "image", *buffer, size, NULL, NULL );
+       }
+
+       // Make sure that no further scaling is done
+       mlt_properties_set( properties, "rescale.interps", "none" );
+       mlt_properties_set( properties, "scale", "off" );
+
+       // All done
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Get the properties of this producer
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Construct a new frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+       // If we have a frame, then stack the producer itself and the get_image method
+       if ( *frame != NULL )
+       {
+               // Define the real frame
+               mlt_frame real_frame = mlt_properties_get_data( properties, "real_frame", NULL );
+
+               // Obtain real frame if we don't have it
+               if ( real_frame == NULL )
+               {
+                       // Get the producer
+                       mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+
+                       // Get the frame position requested
+                       mlt_position position = mlt_properties_get_position( properties, "frame" );
+
+                       // Seek the producer to the correct place
+                       mlt_producer_seek( producer, position );
+
+                       // Get the real frame
+                       mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &real_frame, index );
+
+                       // Ensure that the real frame gets wiped eventually
+                       mlt_properties_set_data( properties, "real_frame", real_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
+               }
+               else
+               {
+                       // Temporary fix - ensure that we aren't seen as a test frame
+                       int8_t *image = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", NULL );
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "image", image, 0, NULL, NULL );
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 0 );
+               }
+
+               // Stack the real frame and method
+               mlt_frame_push_service( *frame, real_frame );
+               mlt_frame_push_service( *frame, producer_get_image );
+
+               // Ensure that the consumer sees what the real frame has
+               mlt_properties_pass( MLT_FRAME_PROPERTIES( *frame ), MLT_FRAME_PROPERTIES( real_frame ), "" );
+
+               mlt_properties_set( MLT_FRAME_PROPERTIES( real_frame ), "deinterlace_method",
+                       mlt_properties_get( properties, "method" ) );
+       }
+
+       // Move to the next position
+       mlt_producer_prepare_next( this );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer this )
+{
+       this->close = NULL;
+       mlt_producer_close( this );
+       free( this );
+}
+
diff --git a/src/modules/frei0r/Makefile b/src/modules/frei0r/Makefile
new file mode 100644 (file)
index 0000000..2c3762d
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltfrei0r$(LIBSUF)
+
+OBJS = factory.o \
+          producer_frei0r.o \
+          filter_frei0r.o \
+          transition_frei0r.o \
+          frei0r_helper.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/frei0r/configure b/src/modules/frei0r/configure
new file mode 100755 (executable)
index 0000000..73757a4
--- /dev/null
@@ -0,0 +1,14 @@
+#! /bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       echo "#include <frei0r.h> int main(){ f0r_plugin_info_t test; test.name;return 0;}"| gcc $CFLAGS -c -x c -  >/dev/null 2>&1
+
+       if [ "$?" = "1" ]
+       then
+               touch ../disable-frei0r
+               echo "- frei0r plugin disabled. Install frei0r-plugins and make sure frei0r.h is available." 
+       fi
+
+fi
diff --git a/src/modules/frei0r/factory.c b/src/modules/frei0r/factory.c
new file mode 100644 (file)
index 0000000..8e2ab99
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+#include <frei0r.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#define FREI0R_PLUGIN_PATH "/usr/lib/frei0r-1:/usr/local/lib/frei0r-1:/usr/lib64/frei0r-1:/opt/local/lib/frei0r-1"
+
+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 );
+extern int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+extern void producer_close( mlt_producer this );
+extern void transition_close( mlt_transition this );
+extern mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame );
+
+static mlt_properties fill_param_info ( mlt_service_type type, const char *service_name, char *name )
+{
+       char file[ PATH_MAX ];
+       char servicetype[ 1024 ]="";
+       struct stat stat_buff;
+
+       switch ( type ) {
+               case producer_type:
+                       strcpy ( servicetype , "producer" );
+                       break;
+               case filter_type:
+                       strcpy ( servicetype , "filter" );
+                       break;
+               case transition_type:
+                       strcpy ( servicetype , "transition" ) ;
+                       break;
+               default:
+                       strcpy ( servicetype , "" );
+       };
+
+       snprintf( file, PATH_MAX, "%s/frei0r/%s_%s.yml", mlt_environment( "MLT_DATA" ), servicetype, service_name );
+       stat(file,&stat_buff);
+
+       if (S_ISREG(stat_buff.st_mode)){
+               return mlt_properties_parse_yaml( file );
+       }
+
+       void* handle=dlopen(name,RTLD_LAZY);
+       if (!handle) return NULL;
+       void (*plginfo)(f0r_plugin_info_t*)=dlsym(handle,"f0r_get_plugin_info");
+       void (*param_info)(f0r_param_info_t*,int param_index)=dlsym(handle,"f0r_get_param_info");
+       if (!plginfo || !param_info) {
+               dlclose(handle);
+               return NULL;
+       }
+       mlt_properties metadata = mlt_properties_new();
+       f0r_plugin_info_t info;
+       char string[48];
+       int j=0;
+
+       plginfo(&info);
+       snprintf ( string, sizeof(string) , "%d.%d" , info.major_version , info.minor_version );
+       mlt_properties_set ( metadata, "schema_version" , "0.1" );
+       mlt_properties_set ( metadata, "title" , info.name );
+       mlt_properties_set ( metadata, "version", string );
+       mlt_properties_set ( metadata, "identifier" , service_name );
+       mlt_properties_set ( metadata, "description" , info.explanation );
+       mlt_properties_set ( metadata, "creator" , info.author );
+       switch (type){
+               case producer_type:
+                       mlt_properties_set ( metadata, "type" , "producer" );
+                       break;
+               case filter_type:
+                       mlt_properties_set ( metadata, "type" , "filter" );
+                       break;
+               case transition_type:
+                       mlt_properties_set ( metadata, "type" , "transition" );
+                       break;
+               default:
+                       break;
+       }
+
+       mlt_properties parameter = mlt_properties_new ( );
+       mlt_properties_set_data ( metadata , "parameters" , parameter , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+       mlt_properties tags = mlt_properties_new ( );
+       mlt_properties_set_data ( metadata , "tags" , tags , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+       mlt_properties_set ( tags , "0" , "Video" );
+
+       for (j=0;j<info.num_params;j++){
+               snprintf ( string , sizeof(string), "%d" , j );
+               mlt_properties pnum = mlt_properties_new ( );
+               mlt_properties_set_data ( parameter , string , pnum , 0 , ( mlt_destructor )mlt_properties_close, NULL );
+               f0r_param_info_t paraminfo;
+               param_info(&paraminfo,j);
+               mlt_properties_set ( pnum , "identifier" , paraminfo.name );
+               mlt_properties_set ( pnum , "title" , paraminfo.name );
+               mlt_properties_set ( pnum , "description" , paraminfo.explanation);
+               if ( paraminfo.type == F0R_PARAM_DOUBLE ){
+                       mlt_properties_set ( pnum , "type" , "float" );
+                       mlt_properties_set ( pnum , "minimum" , "0" );
+                       mlt_properties_set ( pnum , "maximum" , "1" );
+                       mlt_properties_set ( pnum , "readonly" , "no" );
+                       mlt_properties_set ( pnum , "widget" , "spinner" );
+               }else
+               if ( paraminfo.type == F0R_PARAM_BOOL ){
+                       mlt_properties_set ( pnum , "type" , "boolean" );
+                       mlt_properties_set ( pnum , "minimum" , "0" );
+                       mlt_properties_set ( pnum , "maximum" , "1" );
+                       mlt_properties_set ( pnum , "readonly" , "no" );
+               }else
+               if ( paraminfo.type == F0R_PARAM_COLOR ){
+                       mlt_properties_set ( pnum , "type" , "color" );
+                       mlt_properties_set ( pnum , "readonly" , "no" );
+               }else
+               if ( paraminfo.type == F0R_PARAM_STRING ){
+                       mlt_properties_set ( pnum , "type" , "string" );
+                       mlt_properties_set ( pnum , "readonly" , "no" );
+               }
+       }
+       dlclose(handle);
+       free(name);
+
+       return metadata;
+}
+
+static void * load_lib(  mlt_profile profile, mlt_service_type type , void* handle){
+
+       int i=0;
+       void (*f0r_get_plugin_info)(f0r_plugin_info_t*),
+               *f0r_construct , *f0r_update , *f0r_destruct,
+               (*f0r_get_param_info)(f0r_param_info_t* info, int param_index),
+               (*f0r_init)(void) , *f0r_deinit ,
+               (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index),
+               (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index),
+               (*f0r_update2) (f0r_instance_t instance, double time,   const uint32_t* inframe1,
+                 const uint32_t* inframe2,const uint32_t* inframe3, uint32_t* outframe);
+
+       if ( ( f0r_construct = dlsym(handle, "f0r_construct") ) &&
+                               (f0r_update = dlsym(handle,"f0r_update") ) &&
+                               (f0r_destruct = dlsym(handle,"f0r_destruct") ) &&
+                               (f0r_get_plugin_info = dlsym(handle,"f0r_get_plugin_info") ) &&
+                               (f0r_get_param_info = dlsym(handle,"f0r_get_param_info") ) &&
+                               (f0r_set_param_value= dlsym(handle,"f0r_set_param_value" ) ) &&
+                               (f0r_get_param_value= dlsym(handle,"f0r_get_param_value" ) ) &&
+                               (f0r_init= dlsym(handle,"f0r_init" ) ) &&
+                               (f0r_deinit= dlsym(handle,"f0r_deinit" ) )
+               ){
+
+               f0r_update2=dlsym(handle,"f0r_update2");
+
+               f0r_plugin_info_t info;
+               f0r_get_plugin_info(&info);
+
+               void* ret=NULL;
+               mlt_properties properties=NULL;
+
+               if (type == producer_type && info.plugin_type == F0R_PLUGIN_TYPE_SOURCE ){
+                       mlt_producer this = mlt_producer_new( );
+                       if ( this != NULL )
+                       {
+                               this->get_frame = producer_get_frame;
+                               this->close = ( mlt_destructor )producer_close;
+                               f0r_init();
+                               properties=MLT_PRODUCER_PROPERTIES ( this );
+
+                               for (i=0;i<info.num_params;i++){
+                                       f0r_param_info_t pinfo;
+                                       f0r_get_param_info(&pinfo,i);
+
+                               }
+
+                               ret=this;
+                       }
+               } else if (type == filter_type && info.plugin_type == F0R_PLUGIN_TYPE_FILTER ){
+                       mlt_filter this = mlt_filter_new( );
+                       if ( this != NULL )
+                       {
+                               this->process = filter_process;
+                               this->close = filter_close;
+                               f0r_init();
+                               properties=MLT_FILTER_PROPERTIES ( this );
+
+                               for (i=0;i<info.num_params;i++){
+                                       f0r_param_info_t pinfo;
+                                       f0r_get_param_info(&pinfo,i);
+
+                               }
+
+                               ret=this;
+                       }
+               }else if (type == transition_type && info.plugin_type == F0R_PLUGIN_TYPE_MIXER2){
+                       mlt_transition transition = mlt_transition_new( );
+                       if ( transition != NULL )
+                       {
+                               transition->process = transition_process;
+                               transition->close = transition_close;
+                               properties=MLT_TRANSITION_PROPERTIES( transition );
+                               mlt_properties_set_int(properties, "_transition_type", 1 );
+
+                               ret=transition;
+                       }
+               }
+               mlt_properties_set_data(properties, "_dlclose_handle", handle , sizeof (void*) , NULL , NULL );
+               mlt_properties_set_data(properties, "_dlclose", dlclose , sizeof (void*) , NULL , NULL );
+               mlt_properties_set_data(properties, "f0r_construct", f0r_construct , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_update", f0r_update , sizeof(void*),NULL,NULL);
+               if (f0r_update2)
+                       mlt_properties_set_data(properties, "f0r_update2", f0r_update2 , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_destruct", f0r_destruct , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_get_plugin_info", f0r_get_plugin_info , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_get_param_info", f0r_get_param_info , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_set_param_value", f0r_set_param_value , sizeof(void*),NULL,NULL);
+               mlt_properties_set_data(properties, "f0r_get_param_value", f0r_get_param_value , sizeof(void*),NULL,NULL);
+
+
+               return ret;
+       }else{
+               printf("some was wrong\n");
+               dlerror();
+       }
+       return NULL;
+}
+
+static void * create_frei0r_item ( mlt_profile profile, mlt_service_type type, const char *id, void *arg){
+
+       mlt_tokeniser tokeniser = mlt_tokeniser_init ( );
+       int dircount=mlt_tokeniser_parse_new (
+               tokeniser,
+               getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH,
+                ":"
+       );
+       void* ret=NULL;
+       while (dircount--){
+               char soname[1024]="";
+
+               char *save_firstptr = NULL;
+               char *firstname=strtok_r(strdup(id),".",&save_firstptr);
+
+               firstname=strtok_r(NULL,".",&save_firstptr);
+               sprintf(soname,"%s/%s.so", mlt_tokeniser_get_string( tokeniser , dircount ) , firstname );
+
+               if (firstname){
+
+                       void* handle=dlopen(soname,RTLD_LAZY);
+
+                       if (handle ){
+                               ret=load_lib ( profile , type , handle );
+                       }else{
+                               dlerror();
+                       }
+               }
+       }
+       mlt_tokeniser_close ( tokeniser );
+       return ret;
+}
+
+
+MLT_REPOSITORY
+{
+       int i=0;
+       mlt_tokeniser tokeniser = mlt_tokeniser_init ( );
+       int dircount=mlt_tokeniser_parse_new (
+               tokeniser ,
+       getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH,
+               ":"
+       );
+
+       while (dircount--){
+
+               mlt_properties direntries = mlt_properties_new();
+               char* dirname = mlt_tokeniser_get_string ( tokeniser , dircount ) ;
+               mlt_properties_dir_list(direntries, dirname ,"*.so",1);
+
+               for (i=0;i<mlt_properties_count(direntries);i++){
+                       char* name=mlt_properties_get_value(direntries,i);
+                       char* shortname=name+strlen(dirname)+1;
+                       char fname[1024]="";
+
+                       strcat(fname,dirname);
+                       strcat(fname,shortname);
+
+                       char *save_firstptr = NULL;
+                       char pluginname[1024]="frei0r.";
+                       char* firstname = strtok_r ( shortname , "." , &save_firstptr );
+                       strcat(pluginname,firstname);
+
+                       void* handle=dlopen(strcat(name,".so"),RTLD_LAZY);
+                       if (handle){
+                               void (*plginfo)(f0r_plugin_info_t*)=dlsym(handle,"f0r_get_plugin_info");
+
+                               if (plginfo){
+                                       f0r_plugin_info_t info;
+                                       plginfo(&info);
+                                       if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_SOURCE){
+                                               MLT_REGISTER( producer_type, pluginname, create_frei0r_item );
+                                               MLT_REGISTER_METADATA( producer_type, pluginname, fill_param_info, strdup(name) );
+                                       }
+                                       else if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_FILTER){
+                                               MLT_REGISTER( filter_type, pluginname, create_frei0r_item );
+                                               MLT_REGISTER_METADATA( filter_type, pluginname, fill_param_info, strdup(name) );
+                                       }
+                                       else if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_MIXER2 ){
+                                               MLT_REGISTER( transition_type, pluginname, create_frei0r_item );
+                                               MLT_REGISTER_METADATA( transition_type, pluginname, fill_param_info, strdup(name) );
+                                       }
+                               }
+                               dlclose(handle);
+                       }
+               }
+               mlt_properties_close(direntries);
+       }
+       mlt_tokeniser_close ( tokeniser );
+}
diff --git a/src/modules/frei0r/filter_frei0r.c b/src/modules/frei0r/filter_frei0r.c
new file mode 100644 (file)
index 0000000..a2880f1
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * filter_frei0r.c -- frei0r filter
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt.h>
+
+#include "frei0r_helper.h"
+#include <string.h>
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               mlt_position in = mlt_filter_get_in( filter );
+               mlt_position out = mlt_filter_get_out( filter );
+               mlt_position time = mlt_frame_get_position( this );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               
+               process_frei0r_item( filter_type , position, MLT_FILTER_PROPERTIES ( filter ), this , image, format , width , height , writable );
+               
+       }
+
+       return error;
+}
+
+
+mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+void filter_close( mlt_filter this ){
+       
+       destruct( MLT_FILTER_PROPERTIES ( this ) );
+       
+}
diff --git a/src/modules/frei0r/frei0r_helper.c b/src/modules/frei0r/frei0r_helper.c
new file mode 100644 (file)
index 0000000..e42b06e
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * frei0r_helper.c -- frei0r helper
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 "frei0r_helper.h"
+#include <frei0r.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void parse_color( int color, f0r_param_color_t *fcolor )
+{
+       fcolor->r = ( color >> 24 ) & 0xff;
+       fcolor->r /= 255;
+       fcolor->g = ( color >> 16 ) & 0xff;
+       fcolor->g /= 255;
+       fcolor->b = ( color >>  8 ) & 0xff;
+       fcolor->b /= 255;
+}
+
+int process_frei0r_item( mlt_service_type type,  double position , mlt_properties prop , mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ){
+
+       int i=0;
+       f0r_instance_t ( *f0r_construct ) ( unsigned int , unsigned int ) =  mlt_properties_get_data(  prop , "f0r_construct" ,NULL);
+       void (*f0r_update)(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)=mlt_properties_get_data(  prop , "f0r_update" ,NULL);
+       void (*f0r_destruct)(f0r_instance_t instance)=mlt_properties_get_data(  prop , "f0r_destruct" ,NULL);
+
+       void (*f0r_get_plugin_info)(f0r_plugin_info_t*)=mlt_properties_get_data( prop, "f0r_get_plugin_info" ,NULL);
+       void (*f0r_get_param_info)(f0r_param_info_t* info, int param_index)=mlt_properties_get_data( prop ,  "f0r_get_param_info" ,NULL);
+       void (*f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index)=mlt_properties_get_data(  prop , "f0r_set_param_value" ,NULL);
+       void (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index)=mlt_properties_get_data(  prop , "f0r_get_param_value" ,NULL);
+       void (*f0r_update2) (f0r_instance_t instance, double time,
+                        const uint32_t* inframe1,const uint32_t* inframe2,const uint32_t* inframe3,
+        uint32_t* outframe)=mlt_properties_get_data(  prop , "f0r_update2" ,NULL);
+
+
+       //use as name the width and height
+       f0r_instance_t inst;
+       char ctorname[1024]="";
+       sprintf(ctorname,"ctor-%dx%d",*width,*height);
+
+       void* neu=mlt_properties_get_data( prop , ctorname ,NULL );
+       if (!f0r_construct){
+               //printf("no ctor\n");
+               return -1;
+       }
+       if ( neu == 0 ){
+               inst= f0r_construct(*width,*height);
+               mlt_properties_set_data(  prop  ,  ctorname , inst, sizeof(void*) , f0r_destruct , NULL );;
+       }else{
+               inst=mlt_properties_get_data( prop ,  ctorname , NULL );
+       }
+       if (f0r_get_plugin_info){
+               f0r_plugin_info_t info;
+               f0r_get_plugin_info(&info);
+               for (i=0;i<info.num_params;i++){
+                       f0r_param_info_t pinfo;
+                       f0r_get_param_info(&pinfo,i);
+                       mlt_geometry geom=mlt_geometry_init();
+                       struct mlt_geometry_item_s item;
+                       //set param if found
+
+                       double t=0.0;
+                       f0r_get_param_value(inst,&t,i);
+                       char *val;
+                       if (mlt_properties_get( prop , pinfo.name ) !=NULL ){
+                               switch (pinfo.type) {
+                                       case F0R_PARAM_DOUBLE:
+                                       case F0R_PARAM_BOOL:
+                                               val=mlt_properties_get(prop, pinfo.name );
+                                               mlt_geometry_parse(geom,val,-1,-1,-1);
+                                               mlt_geometry_fetch(geom,&item,position);
+                                               t=item.x;
+                                               f0r_set_param_value(inst,&t,i);
+                                               break;
+                                       case F0R_PARAM_COLOR:
+                                       {
+                                               f0r_param_color_t color;
+                                               parse_color(mlt_properties_get_int(prop , pinfo.name), &color);
+                                               f0r_set_param_value(inst, &color, i);
+                                               break;
+                                       }
+                               }
+                       }
+
+                       mlt_geometry_close(geom);
+               }
+       }
+
+       int video_area = *width * *height;
+       uint32_t *img_a;
+
+       if ( type != producer_type )
+           img_a = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+       uint32_t *img_b = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+
+       if (type==producer_type) {
+               f0r_update ( inst , position , NULL , img_b );
+               mlt_convert_rgb24a_to_yuv422((uint8_t *)img_b , *width, *height, *width * sizeof(uint32_t),*image, NULL);
+       } else if (type==filter_type) {
+               mlt_convert_yuv422_to_rgb24a(*image, (uint8_t *)img_a, video_area);
+               f0r_update ( inst , position , img_a , img_b );
+               mlt_convert_rgb24a_to_yuv422((uint8_t *)img_b , *width, *height, *width * sizeof(uint32_t),
+                                                                                               *image, NULL );
+       } else if (type==transition_type && f0r_update2 ){
+               uint32_t *result = mlt_pool_alloc( video_area * sizeof(uint32_t) );
+
+               mlt_convert_yuv422_to_rgb24a ( image[0] , (uint8_t *)img_a , video_area );
+               mlt_convert_yuv422_to_rgb24a ( image[1] , (uint8_t *)img_b , video_area );
+               f0r_update2 ( inst , position , img_a , img_b , NULL , result );
+
+               uint8_t * image_ptr=mlt_properties_get_data(MLT_FRAME_PROPERTIES(this), "image", NULL );
+               if (image_ptr)
+                   mlt_convert_rgb24a_to_yuv422((uint8_t *)result, *width, *height, *width * sizeof(uint32_t), image_ptr , NULL );
+
+               mlt_pool_release(result);
+       }
+       if ( type != producer_type ) mlt_pool_release(img_a);
+       mlt_pool_release(img_b);
+
+       return 0;
+}
+
+void destruct (mlt_properties prop ) {
+
+       void (*f0r_destruct)(f0r_instance_t instance)=mlt_properties_get_data(  prop , "f0r_destruct" , NULL );
+       void (*f0r_deinit)(void)=mlt_properties_get_data ( prop , "f0r_deinit" , NULL);
+       int i=0;
+
+       if ( f0r_deinit != NULL )
+               f0r_deinit();
+
+       for ( i=0 ; i < mlt_properties_count ( prop ) ; i++ ){
+               if ( strstr ( mlt_properties_get_name ( prop , i ) , "ctor-" ) != NULL ){
+                       void * inst=mlt_properties_get_data( prop , mlt_properties_get_name ( prop , i ) , NULL );
+                       if ( inst != NULL ){
+                               f0r_destruct( (f0r_instance_t) inst);
+                       }
+               }
+       }
+       void (*dlclose)(void*)=mlt_properties_get_data ( prop , "_dlclose" , NULL);
+       void *handle=mlt_properties_get_data ( prop , "_dlclose_handle" , NULL);
+
+       if (handle && dlclose ){
+               dlclose ( handle );
+       }
+
+}
diff --git a/src/modules/frei0r/frei0r_helper.h b/src/modules/frei0r/frei0r_helper.h
new file mode 100644 (file)
index 0000000..aee4a5d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * frei0r_helper.h -- frei0r helper
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt.h>
+
+int process_frei0r_item( mlt_service_type type,  double position , mlt_properties prop , mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable );
+void destruct (mlt_properties prop );
diff --git a/src/modules/frei0r/producer_frei0r.c b/src/modules/frei0r/producer_frei0r.c
new file mode 100644 (file)
index 0000000..ae1a26d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * producer_frei0r.c -- frei0r producer
+ * Copyright (c) 2009 Jean-Baptiste Mardelle <jb@kdenlive.org>
+ *
+ * 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 <framework/mlt.h>
+
+#include "frei0r_helper.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer for this frame
+       mlt_producer producer = mlt_properties_get_data( properties, "producer_frei0r", NULL );
+
+       // Obtain properties of producer
+       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Allocate the image
+       int size = *width * ( *height + 1 ) * 2;
+
+       // Allocate the image
+       *buffer = mlt_pool_alloc( size );
+
+       // Update the frame
+       mlt_properties_set_data( properties, "image", *buffer, size, mlt_pool_release, NULL );
+       mlt_properties_set_int( properties, "width", *width );
+       mlt_properties_set_int( properties, "height", *height );
+
+       *format = mlt_image_yuv422;
+       if ( *buffer != NULL )
+       {
+               mlt_position in = mlt_producer_get_in( producer );
+               mlt_position out = mlt_producer_get_out( producer );
+               mlt_position time = mlt_frame_get_position( frame );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               process_frei0r_item( producer_type , position, producer_props, frame , buffer, format , width , height , writable );
+       }
+
+    return 0;
+}
+
+int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       if ( *frame != NULL )
+       {
+               // Obtain properties of frame and producer
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Obtain properties of producer
+               mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+               // Set the producer on the frame properties
+               mlt_properties_set_data( properties, "producer_frei0r", producer, 0, NULL, NULL );
+
+               // Update timecode on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+               // Set producer-specific frame properties
+               mlt_properties_set_int( properties, "progressive", 1 );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+
+               // Push the get_image method
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+void producer_close( mlt_producer producer )
+{
+       producer->close = NULL;
+       mlt_producer_close( producer );
+       free( producer );
+}
diff --git a/src/modules/frei0r/transition_frei0r.c b/src/modules/frei0r/transition_frei0r.c
new file mode 100644 (file)
index 0000000..4fb6846
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * transition_frei0r.c -- frei0r transition
+ * Copyright (c) 2008 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt.h>
+#include "frei0r_helper.h"
+#include <string.h>
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ){
+       
+       if (*format!=mlt_image_yuv422 ){
+               return -1;
+       }
+       
+       mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+       mlt_transition transition = mlt_frame_pop_service( a_frame );
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+       int invert = mlt_properties_get_int( properties, "invert" );
+
+       if ( mlt_properties_get( a_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+               mlt_properties_set( a_props, "rescale.interp", "nearest" );
+
+       // set consumer_aspect_ratio for a and b frame
+       if ( mlt_properties_get_double( a_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+       if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+       mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
+
+       if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL || !strcmp( mlt_properties_get( b_props, "rescale.interp" ), "none" ) )
+                mlt_properties_set( b_props, "rescale.interp", "nearest" );
+       
+       uint8_t *images[]={NULL,NULL,NULL};
+
+       mlt_frame_get_image( a_frame, &images[0], format, width, height, 1 );
+       mlt_frame_get_image( b_frame, &images[1], format, width, height, 1 );
+       
+       mlt_position in = mlt_transition_get_in( transition );
+       mlt_position out = mlt_transition_get_out( transition );
+
+       // Get the position of the frame
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+       mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( a_frame ), name );
+
+       float pos=( float )( position - in ) / ( float )( out - in + 1 );
+       
+       process_frei0r_item( transition_type , pos , properties, !invert ? a_frame : b_frame , images , format, width,height, writable );
+       
+       *width = mlt_properties_get_int( !invert ? a_props : b_props, "width" );
+        *height = mlt_properties_get_int( !invert ? a_props : b_props, "height" );
+       *image = mlt_properties_get_data( !invert ? a_props : b_props , "image", NULL );
+       return 0;
+}
+
+mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+       mlt_properties_set_position( MLT_FRAME_PROPERTIES( a_frame ), name, mlt_frame_get_position( a_frame ) );
+       mlt_frame_push_service( a_frame, transition );
+       mlt_frame_push_frame( a_frame, b_frame );
+       mlt_frame_push_get_image( a_frame, transition_get_image );
+       return a_frame;
+}
+
+void transition_close( mlt_transition this ){
+       destruct ( MLT_TRANSITION_PROPERTIES ( this ) );
+}
diff --git a/src/modules/gtk2/Makefile b/src/modules/gtk2/Makefile
new file mode 100644 (file)
index 0000000..de5fa2f
--- /dev/null
@@ -0,0 +1,62 @@
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltgtk2$(LIBSUF)
+
+OBJS = factory.o
+
+CFLAGS += -I../..
+LDFLAGS += -L../../framework -lmlt
+
+ifdef USE_GTK2
+OBJS += consumer_gtk2.o
+CFLAGS += `pkg-config gtk+-2.0 --cflags`
+LDFLAGS += `pkg-config gtk+-2.0 --libs`
+endif
+
+ifdef USE_PIXBUF
+OBJS += producer_pixbuf.o pixops.o filter_rescale.o
+CFLAGS += `pkg-config gdk-pixbuf-2.0 --cflags`
+LDFLAGS += `pkg-config gdk-pixbuf-2.0 --libs`
+endif
+
+ifdef MMX_FLAGS
+ifndef ARCH_X86_64
+ASM_OBJS = have_mmx.o scale_line_22_yuv_mmx.o
+endif
+endif
+
+ifdef USE_PANGO
+OBJS += producer_pango.o
+CFLAGS += `pkg-config pangoft2 --cflags`
+LDFLAGS += `pkg-config pangoft2 --libs`
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS) $(ASM_OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(ASM_OBJS) $(LDFLAGS)
+
+have_mmx.o:
+       $(CC) -o $@ -c have_mmx.S
+
+scale_line_22_yuv_mmx.o: scale_line_22_yuv_mmx.S
+       $(CC) -o $@ -c scale_line_22_yuv_mmx.S
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(ASM_OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/gtk2/configure b/src/modules/gtk2/configure
new file mode 100755 (executable)
index 0000000..299da4b
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       pkg-config gtk+-2.0 2> /dev/null
+       disable_gtk2=$?
+
+       pkg-config gdk-pixbuf-2.0 2> /dev/null
+       disable_pixbuf=$?
+
+       pkg-config pangoft2 2> /dev/null
+       disable_pango=$?
+
+       if [ "$disable_gtk2" != "0" -a "$disable_pixbuf" != 0 -a "$disable_pango" != "0" ]
+       then
+               echo "- GTK2 components not found: disabling"
+               touch ../disable-gtk2
+               exit 0
+       fi
+
+       [ "$disable_gtk2" != "0" ] && echo "- gtk2 not found: gtk2 preview disabled"
+       [ "$disable_pixbuf" != "0" ] && echo "- pixbuf not found: pixbuf loader and rescaler disabled"
+       [ "$disable_pango" != "0" ] && echo "- pango not found: pango titler disabled"
+
+       echo > config.h
+       [ "$disable_gtk2" = "0" ] && echo "#define USE_GTK2" >> config.h
+       [ "$disable_pixbuf" = "0" ] && echo "#define USE_PIXBUF" >> config.h
+       [ "$disable_pango" = "0" ] && echo "#define USE_PANGO" >> config.h
+
+       echo > config.mak
+       [ "$disable_gtk2" = "0" ] && echo "USE_GTK2=1" >> config.mak
+       [ "$disable_pixbuf" = "0" ] && echo "USE_PIXBUF=1" >> config.mak
+       [ "$disable_pango" = "0" ] && echo "USE_PANGO=1" >> config.mak
+
+       exit 0
+fi
+
diff --git a/src/modules/gtk2/consumer_gtk2.c b/src/modules/gtk2/consumer_gtk2.c
new file mode 100644 (file)
index 0000000..dc1619e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * consumer_gtk2.c -- A consumer for GTK2 apps
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_factory.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+mlt_consumer consumer_gtk2_preview_init( mlt_profile profile, GtkWidget *widget )
+{
+       // Create an sdl preview consumer
+       mlt_consumer consumer = NULL;
+
+       // This is a nasty little hack which is required by SDL
+       if ( widget != NULL )
+       {
+        Window xwin = GDK_WINDOW_XWINDOW( widget->window );
+        char windowhack[ 32 ];
+        sprintf( windowhack, "%ld", xwin );
+        setenv( "SDL_WINDOWID", windowhack, 1 );
+       }
+
+       // Create an sdl preview consumer
+       consumer = mlt_factory_consumer( profile, "sdl_preview", NULL );
+
+       // Now assign the lock/unlock callbacks
+       if ( consumer != NULL )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+               mlt_properties_set_int( properties, "app_locked", 1 );
+               mlt_properties_set_data( properties, "app_lock", gdk_threads_enter, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "app_unlock", gdk_threads_leave, 0, NULL, NULL );
+       }
+
+       return consumer;
+}
diff --git a/src/modules/gtk2/factory.c b/src/modules/gtk2/factory.c
new file mode 100644 (file)
index 0000000..34d62e2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 "config.h"
+#include <string.h>
+#include <framework/mlt.h>
+#include <gdk/gdk.h>
+
+#ifdef USE_PIXBUF
+extern mlt_producer producer_pixbuf_init( char *filename );
+extern mlt_filter filter_rescale_init( mlt_profile profile, char *arg );
+#endif
+
+#ifdef USE_GTK2
+extern mlt_consumer consumer_gtk2_preview_init( mlt_profile profile, void *widget );
+#endif
+
+#ifdef USE_PANGO
+extern mlt_producer producer_pango_init( const char *filename );
+#endif
+
+static void initialise( )
+{
+       static int init = 0;
+       if ( init == 0 )
+       {
+               init = 1;
+               g_type_init( );
+       }
+}
+
+void *create_service( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       initialise( );
+
+#ifdef USE_PIXBUF
+       if ( !strcmp( id, "pixbuf" ) )
+               return producer_pixbuf_init( arg );
+#endif
+
+#ifdef USE_PANGO
+       if ( !strcmp( id, "pango" ) )
+               return producer_pango_init( arg );
+#endif
+
+#ifdef USE_PIXBUF
+       if ( !strcmp( id, "gtkrescale" ) )
+               return filter_rescale_init( profile, arg );
+#endif
+
+#ifdef USE_GTK2
+       if ( !strcmp( id, "gtk2_preview" ) )
+               return consumer_gtk2_preview_init( profile, arg );
+#endif
+
+       return NULL;
+}
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "gtk2_preview", create_service );
+       MLT_REGISTER( filter_type, "gtkrescale", create_service );
+       MLT_REGISTER( producer_type, "pango", create_service );
+       MLT_REGISTER( producer_type, "pixbuf", create_service );
+}
diff --git a/src/modules/gtk2/filter_rescale.c b/src/modules/gtk2/filter_rescale.c
new file mode 100644 (file)
index 0000000..e3b6d17
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * filter_rescale.c -- scale the producer video frame size to match the consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 "pixops.h"
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format iformat, mlt_image_format oformat, int iwidth, int iheight, int owidth, int oheight )
+{
+       // Get the properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Get the requested interpolation method
+       char *interps = mlt_properties_get( properties, "rescale.interp" );
+
+       // Convert to the GTK flag
+       int interp = PIXOPS_INTERP_BILINEAR;
+
+       if ( strcmp( interps, "nearest" ) == 0 )
+               interp = PIXOPS_INTERP_NEAREST;
+       else if ( strcmp( interps, "tiles" ) == 0 )
+               interp = PIXOPS_INTERP_TILES;
+       else if ( strcmp( interps, "hyper" ) == 0 )
+               interp = PIXOPS_INTERP_HYPER;
+
+       // Carry out the rescaling
+       if ( iformat == mlt_image_yuv422 && oformat == mlt_image_yuv422 )
+       {
+               // Create the output image
+               uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+               // Calculate strides
+               int istride = iwidth * 2;
+               int ostride = owidth * 2;
+
+               yuv422_scale_simple( output, owidth, oheight, ostride, *image, iwidth, iheight, istride, interp );
+               
+               // Now update the frame
+               mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", owidth );
+               mlt_properties_set_int( properties, "height", oheight );
+
+               // Return the output
+               *image = output;
+       }
+       else if ( iformat == mlt_image_rgb24 || iformat == mlt_image_rgb24a )
+       {
+               int bpp = (iformat == mlt_image_rgb24a ? 4 : 3 );
+                       
+               // Create the yuv image
+               uint8_t *output = mlt_pool_alloc( owidth * ( oheight + 1 ) * 2 );
+
+               if ( strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) )
+               {
+                       GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( *image, GDK_COLORSPACE_RGB,
+                               ( iformat == mlt_image_rgb24a ), 8, iwidth, iheight,
+                               iwidth * bpp, NULL, NULL );
+
+                       GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf, owidth, oheight, interp );
+                       g_object_unref( pixbuf );
+                       
+                       // Extract YUV422 and alpha
+                       if ( bpp == 4 )
+                       {
+                               // Allocate the alpha mask
+                               uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+                               // Convert the image and extract alpha
+                               mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( scaled ), owidth, oheight, gdk_pixbuf_get_rowstride( scaled ), output, alpha );
+
+                               mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+                       }
+                       else
+                       {
+                               // No alpha to extract
+                               mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( scaled ), owidth, oheight, gdk_pixbuf_get_rowstride( scaled ), output );
+                       }
+                       g_object_unref( scaled );
+               }
+               else
+               {
+                       // Extract YUV422 and alpha
+                       if ( bpp == 4 )
+                       {
+                               // Allocate the alpha mask
+                               uint8_t *alpha = mlt_pool_alloc( owidth * ( oheight + 1 ) );
+
+                               // Convert the image and extract alpha
+                               mlt_convert_rgb24a_to_yuv422( *image, owidth, oheight, owidth * 4, output, alpha );
+
+                               mlt_properties_set_data( properties, "alpha", alpha, owidth * ( oheight + 1 ), ( mlt_destructor )mlt_pool_release, NULL );
+                       }
+                       else
+                       {
+                               // No alpha to extract
+                               mlt_convert_rgb24_to_yuv422( *image, owidth, oheight, owidth * 3, output );
+                       }
+               }
+
+               // Now update the frame
+               mlt_properties_set_data( properties, "image", output, owidth * ( oheight + 1 ) * 2, ( mlt_destructor )mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "width", owidth );
+               mlt_properties_set_int( properties, "height", oheight );
+
+               *image = output;
+       }
+
+       return 0;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_rescale_init( mlt_profile profile, char *arg )
+{
+       // Create a new scaler
+       mlt_filter this = mlt_factory_filter( profile, "rescale", arg );
+
+       // If successful, then initialise it
+       if ( this != NULL )
+       {
+               // Get the properties
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Set the inerpolation
+               mlt_properties_set( properties, "interpolation", arg == NULL ? "bilinear" : arg );
+
+               // Set the method
+               mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL );
+       }
+
+       return this;
+}
+
diff --git a/src/modules/gtk2/have_mmx.S b/src/modules/gtk2/have_mmx.S
new file mode 100644 (file)
index 0000000..4f8f5d8
--- /dev/null
@@ -0,0 +1,53 @@
+       .file   "have_mmx.S"
+       .version        "01.01"
+gcc2_compiled.:
+.text
+       .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)      
+
+.globl pixops_have_mmx
+       .type    pixops_have_mmx,@function
+pixops_have_mmx:
+
+#else
+
+.globl _pixops_have_mmx
+_pixops_have_mmx:
+
+#endif
+       
+       push    %ebx
+
+# Check if bit 21 in flags word is writeable
+
+       pushfl  
+       popl    %eax
+       movl    %eax,%ebx
+       xorl    $0x00200000, %eax
+       pushl   %eax
+       popfl
+       pushfl
+       popl    %eax
+
+       cmpl    %eax, %ebx
+
+       je .notfound
+
+# OK, we have CPUID
+
+       movl    $1, %eax
+       cpuid
+       
+       test    $0x00800000, %edx
+       jz      .notfound
+
+       movl    $1, %eax
+       jmp     .out
+
+.notfound:
+       movl    $0, %eax
+.out:  
+       popl    %ebx
+       ret
+
diff --git a/src/modules/gtk2/pixops.c b/src/modules/gtk2/pixops.c
new file mode 100644 (file)
index 0000000..05b8292
--- /dev/null
@@ -0,0 +1,769 @@
+/* GdkPixbuf library - Scaling and compositing functions
+ *
+ * Original:
+ * Copyright (C) 2000 Red Hat, Inc
+ * Author: Owen Taylor <otaylor@redhat.com>
+ *
+ * Modification for MLT:
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 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 <math.h>
+#include <glib.h>
+#include <stdio.h>
+
+#include "pixops.h"
+
+#define SUBSAMPLE_BITS 4
+#define SUBSAMPLE (1 << SUBSAMPLE_BITS)
+#define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1)
+#define SCALE_SHIFT 16
+
+typedef struct _PixopsFilter PixopsFilter;
+typedef struct _PixopsFilterDimension PixopsFilterDimension;
+
+struct _PixopsFilterDimension
+{
+       int n;
+       double offset;
+       double *weights;
+};
+
+struct _PixopsFilter
+{
+       PixopsFilterDimension x;
+       PixopsFilterDimension y;
+       double overall_alpha;
+};
+
+typedef guchar *( *PixopsLineFunc ) ( int *weights, int n_x, int n_y,
+                                      guchar *dest, int dest_x, guchar *dest_end,
+                                      guchar **src,
+                                      int x_init, int x_step, int src_width );
+
+typedef void ( *PixopsPixelFunc ) ( guchar *dest, guint y1, guint cr, guint y2, guint cb );
+
+
+/* mmx function declarations */
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+guchar *pixops_scale_line_22_yuv_mmx ( guint32 weights[ 16 ][ 8 ], guchar *p, guchar *q1, guchar *q2, int x_step, guchar *p_stop, int x_init, int destx );
+int pixops_have_mmx ( void );
+#endif
+
+static inline int
+get_check_shift ( int check_size )
+{
+       int check_shift = 0;
+       g_return_val_if_fail ( check_size >= 0, 4 );
+
+       while ( !( check_size & 1 ) )
+       {
+               check_shift++;
+               check_size >>= 1;
+       }
+
+       return check_shift;
+}
+
+static inline void
+pixops_scale_nearest ( guchar *dest_buf,
+                       int render_x0,
+                       int render_y0,
+                       int render_x1,
+                       int render_y1,
+                       int dest_rowstride,
+                       const guchar *src_buf,
+                       int src_width,
+                       int src_height,
+                       int src_rowstride,
+                       double scale_x,
+                       double scale_y )
+{
+       register int i, j;
+       register int x_step = ( 1 << SCALE_SHIFT ) / scale_x;
+       register int y_step = ( 1 << SCALE_SHIFT ) / scale_y;
+       register int x, x_scaled;
+
+       for ( i = 0; i < ( render_y1 - render_y0 ); i++ )
+       {
+               const guchar *src = src_buf + ( ( ( i + render_y0 ) * y_step + ( y_step >> 1 ) ) >> SCALE_SHIFT ) * src_rowstride;
+               guchar *dest = dest_buf + i * dest_rowstride;
+               x = render_x0 * x_step + ( x_step >> 1 );
+
+               for ( j = 0; j < ( render_x1 - render_x0 ); j++ )
+               {
+                       x_scaled = x >> SCALE_SHIFT;
+                       *dest++ = src[ x_scaled << 1 ];
+                       *dest++ = src[ ( ( x_scaled >> 1 ) << 2 ) + ( ( j & 1 ) << 1 ) + 1 ];
+                       x += x_step;
+               }
+       }
+}
+
+
+static inline guchar *
+scale_line ( int *weights, int n_x, int n_y,
+             guchar *dest, int dest_x, guchar *dest_end,
+             guchar **src,
+             int x_init, int x_step, int src_width )
+{
+       register int x = x_init;
+       register int i, j, x_scaled, y_index, uv_index;
+
+       while ( dest < dest_end )
+       {
+               unsigned int y = 0, uv = 0;
+               int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * n_x * n_y;
+
+               x_scaled = x >> SCALE_SHIFT;
+               y_index = x_scaled << 1;
+               uv_index = ( ( x_scaled >> 1 ) << 2 ) + ( ( dest_x & 1 ) << 1 ) + 1;
+
+               for ( i = 0; i < n_y; i++ )
+               {
+                       int *line_weights = pixel_weights + n_x * i;
+                       guchar *q = src[ i ];
+
+                       for ( j = 0; j < n_x; j ++ )
+                       {
+                               unsigned int ta = line_weights[ j ];
+
+                               y  += ta * q[ y_index ];
+                               uv += ta * q[ uv_index ];
+                       }
+               }
+
+               *dest++ = ( y  + 0xffff ) >> SCALE_SHIFT;
+               *dest++ = ( uv + 0xffff ) >> SCALE_SHIFT;
+
+               x += x_step;
+               dest_x++;
+       }
+
+       return dest;
+}
+
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+static inline guchar *
+scale_line_22_yuv_mmx_stub ( int *weights, int n_x, int n_y,
+                            guchar *dest, int dest_x, guchar *dest_end,
+                            guchar **src,
+                            int x_init, int x_step, int src_width )
+{
+       guint32 mmx_weights[ 16 ][ 8 ];
+       int j;
+
+       for ( j = 0; j < 16; j++ )
+       {
+               mmx_weights[ j ][ 0 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 );
+               mmx_weights[ j ][ 1 ] = 0x00010001 * ( weights[ 4 * j ] >> 8 );
+               mmx_weights[ j ][ 2 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 );
+               mmx_weights[ j ][ 3 ] = 0x00010001 * ( weights[ 4 * j + 1 ] >> 8 );
+               mmx_weights[ j ][ 4 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 );
+               mmx_weights[ j ][ 5 ] = 0x00010001 * ( weights[ 4 * j + 2 ] >> 8 );
+               mmx_weights[ j ][ 6 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 );
+               mmx_weights[ j ][ 7 ] = 0x00010001 * ( weights[ 4 * j + 3 ] >> 8 );
+       }
+
+       return pixops_scale_line_22_yuv_mmx ( mmx_weights, dest, src[ 0 ], src[ 1 ], x_step, dest_end, x_init, dest_x );
+}
+#endif /* USE_MMX */
+
+static inline guchar *
+scale_line_22_yuv ( int *weights, int n_x, int n_y,
+                   guchar *dest, int dest_x, guchar *dest_end,
+                   guchar **src,
+                   int x_init, int x_step, int src_width )
+{
+       register int x = x_init;
+       register guchar *src0 = src[ 0 ];
+       register guchar *src1 = src[ 1 ];
+       register unsigned int p;
+       register guchar *q0, *q1;
+       register int w1, w2, w3, w4;
+       register int x_scaled, x_aligned, uv_index;
+
+       while ( dest < dest_end )
+       {
+               int *pixel_weights = weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * 4;
+
+               x_scaled = x >> SCALE_SHIFT;
+
+               w1 = pixel_weights[ 0 ];
+               w2 = pixel_weights[ 1 ];
+               w3 = pixel_weights[ 2 ];
+               w4 = pixel_weights[ 3 ];
+
+               /* process Y */
+               q0 = src0 + ( x_scaled << 1 );
+               q1 = src1 + ( x_scaled << 1 );
+               p  = w1 * q0[ 0 ];
+               p += w2 * q0[ 2 ];
+               p += w3 * q1[ 0 ];
+               p += w4 * q1[ 2 ];
+               *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT;
+
+               /* process U/V */
+               x_aligned = ( ( x_scaled >> 1 ) << 2 );
+               uv_index = ( ( dest_x & 1 ) << 1 ) + 1;
+
+               q0 = src0 + x_aligned;
+               q1 = src1 + x_aligned;
+               p  = w1 * q0[ uv_index ];
+               p += w3 * q1[ uv_index ];
+               p += w2 * q0[ uv_index ];
+               p += w4 * q1[ uv_index ];
+
+               x += x_step;
+               dest_x ++;
+
+               *dest++ = ( p + 0x8000 ) >> SCALE_SHIFT;
+       }
+
+       return dest;
+}
+
+
+static inline void
+process_pixel ( int *weights, int n_x, int n_y,
+                guchar *dest, int dest_x, int dest_channels,
+                guchar **src, int src_channels,
+                int x_start, int src_width )
+{
+       register unsigned int y = 0, uv = 0;
+       register int i, j;
+       int uv_index = ( ( dest_x & 1 ) << 1 ) + 1;
+
+       for ( i = 0; i < n_y; i++ )
+       {
+               int *line_weights = weights + n_x * i;
+
+               for ( j = 0; j < n_x; j++ )
+               {
+                       unsigned int ta = 0xff * line_weights[ j ];
+
+                       if ( x_start + j < 0 )
+                       {
+                               y  += ta * src[ i ][ 0 ];
+                               uv += ta * src[ i ][ uv_index ];
+                       }
+                       else if ( x_start + j < src_width )
+                       {
+                               y  += ta * src[ i ][ ( x_start + j ) << 1 ];
+                               uv += ta * src[ i ][ ( ( ( x_start + j ) >> 1 ) << 2) + uv_index ];
+                       }
+                       else
+                       {
+                               y  += ta * src[ i ][ ( src_width - 1 ) << 1 ];
+                               uv += ta * src[ i ][ ( ( ( src_width - 1 ) >> 1 ) << 2) + uv_index ];
+                       }
+               }
+       }
+
+       *dest++ = ( y  + 0xffffff ) >> 24;
+       *dest++ = ( uv + 0xffffff ) >> 24;
+}
+
+
+static inline void
+correct_total ( int *weights,
+                int n_x,
+                int n_y,
+                int total,
+                double overall_alpha )
+{
+       int correction = ( int ) ( 0.5 + 65536 * overall_alpha ) - total;
+       int remaining, c, d, i;
+
+       if ( correction != 0 )
+       {
+               remaining = correction;
+               for ( d = 1, c = correction; c != 0 && remaining != 0; d++, c = correction / d )
+                       for ( i = n_x * n_y - 1; i >= 0 && c != 0 && remaining != 0; i-- )
+                               if ( *( weights + i ) + c >= 0 )
+                               {
+                                       *( weights + i ) += c;
+                                       remaining -= c;
+                                       if ( ( 0 < remaining && remaining < c ) ||
+                                               ( 0 > remaining && remaining > c ) )
+                                               c = remaining;
+                               }
+       }
+}
+
+
+static inline int *
+make_filter_table ( PixopsFilter *filter )
+{
+       int i_offset, j_offset;
+       int n_x = filter->x.n;
+       int n_y = filter->y.n;
+       int *weights = g_new ( int, SUBSAMPLE * SUBSAMPLE * n_x * n_y );
+
+       for ( i_offset = 0; i_offset < SUBSAMPLE; i_offset++ )
+               for ( j_offset = 0; j_offset < SUBSAMPLE; j_offset++ )
+               {
+                       double weight;
+                       int *pixel_weights = weights + ( ( i_offset * SUBSAMPLE ) + j_offset ) * n_x * n_y;
+                       int total = 0;
+                       int i, j;
+
+                       for ( i = 0; i < n_y; i++ )
+                               for ( j = 0; j < n_x; j++ )
+                               {
+                                       weight = filter->x.weights[ ( j_offset * n_x ) + j ] *
+                                                filter->y.weights[ ( i_offset * n_y ) + i ] *
+                                                filter->overall_alpha * 65536 + 0.5;
+
+                                       total += ( int ) weight;
+
+                                       *( pixel_weights + n_x * i + j ) = weight;
+                               }
+
+                       correct_total ( pixel_weights, n_x, n_y, total, filter->overall_alpha );
+               }
+
+       return weights;
+}
+
+
+static inline void
+pixops_process ( guchar *dest_buf,
+                 int render_x0,
+                 int render_y0,
+                 int render_x1,
+                 int render_y1,
+                 int dest_rowstride,
+                 int dest_channels,
+                 gboolean dest_has_alpha,
+                 const guchar *src_buf,
+                 int src_width,
+                 int src_height,
+                 int src_rowstride,
+                 int src_channels,
+                 gboolean src_has_alpha,
+                 double scale_x,
+                 double scale_y,
+                 int check_x,
+                 int check_y,
+                 int check_size,
+                 guint32 color1,
+                 guint32 color2,
+                 PixopsFilter *filter,
+                 PixopsLineFunc line_func )
+{
+       int i, j;
+       int x, y;                       /* X and Y position in source (fixed_point) */
+
+       guchar **line_bufs = g_new ( guchar *, filter->y.n );
+       int *filter_weights = make_filter_table ( filter );
+
+       int x_step = ( 1 << SCALE_SHIFT ) / scale_x; /* X step in source (fixed point) */
+       int y_step = ( 1 << SCALE_SHIFT ) / scale_y; /* Y step in source (fixed point) */
+
+       int check_shift = check_size ? get_check_shift ( check_size ) : 0;
+
+       int scaled_x_offset = floor ( filter->x.offset * ( 1 << SCALE_SHIFT ) );
+
+       /* Compute the index where we run off the end of the source buffer. The furthest
+        * source pixel we access at index i is:
+        *
+        *  ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->x.n - 1
+        *
+        * So, run_end_index is the smallest i for which this pixel is src_width, i.e, for which:
+        *
+        *  (i + render_x0) * x_step >= ((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset
+        *
+        */
+#define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b))    /* Division so that -1/5 = -1 */
+
+       int run_end_x = ( ( ( src_width - filter->x.n + 1 ) << SCALE_SHIFT ) - scaled_x_offset );
+       int run_end_index = MYDIV ( run_end_x + x_step - 1, x_step ) - render_x0;
+       run_end_index = MIN ( run_end_index, render_x1 - render_x0 );
+
+       y = render_y0 * y_step + floor ( filter->y.offset * ( 1 << SCALE_SHIFT ) );
+       for ( i = 0; i < ( render_y1 - render_y0 ); i++ )
+       {
+               int dest_x;
+               int y_start = y >> SCALE_SHIFT;
+               int x_start;
+               int *run_weights = filter_weights +
+                                  ( ( y >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) *
+                                  filter->x.n * filter->y.n * SUBSAMPLE;
+               guchar *new_outbuf;
+               guint32 tcolor1, tcolor2;
+
+               guchar *outbuf = dest_buf + dest_rowstride * i;
+               guchar *outbuf_end = outbuf + dest_channels * ( render_x1 - render_x0 );
+
+               if ( ( ( i + check_y ) >> check_shift ) & 1 )
+               {
+                       tcolor1 = color2;
+                       tcolor2 = color1;
+               }
+               else
+               {
+                       tcolor1 = color1;
+                       tcolor2 = color2;
+               }
+
+               for ( j = 0; j < filter->y.n; j++ )
+               {
+                       if ( y_start < 0 )
+                               line_bufs[ j ] = ( guchar * ) src_buf;
+                       else if ( y_start < src_height )
+                               line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * y_start;
+                       else
+                               line_bufs[ j ] = ( guchar * ) src_buf + src_rowstride * ( src_height - 1 );
+
+                       y_start++;
+               }
+
+               dest_x = check_x;
+               x = render_x0 * x_step + scaled_x_offset;
+               x_start = x >> SCALE_SHIFT;
+
+               while ( x_start < 0 && outbuf < outbuf_end )
+               {
+                       process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ),
+                                       filter->x.n, filter->y.n,
+                                       outbuf, dest_x, dest_channels,
+                                       line_bufs, src_channels,
+                                       x >> SCALE_SHIFT, src_width );
+
+                       x += x_step;
+                       x_start = x >> SCALE_SHIFT;
+                       dest_x++;
+                       outbuf += dest_channels;
+               }
+
+               new_outbuf = ( *line_func ) ( run_weights, filter->x.n, filter->y.n,
+                                             outbuf, dest_x,
+                                             dest_buf + dest_rowstride * i + run_end_index * dest_channels,
+                                             line_bufs,
+                                             x, x_step, src_width );
+
+               dest_x += ( new_outbuf - outbuf ) / dest_channels;
+
+               x = ( dest_x - check_x + render_x0 ) * x_step + scaled_x_offset;
+               outbuf = new_outbuf;
+
+               while ( outbuf < outbuf_end )
+               {
+                       process_pixel ( run_weights + ( ( x >> ( SCALE_SHIFT - SUBSAMPLE_BITS ) ) & SUBSAMPLE_MASK ) * ( filter->x.n * filter->y.n ),
+                                       filter->x.n, filter->y.n,
+                                       outbuf, dest_x, dest_channels,
+                                       line_bufs, src_channels,
+                                       x >> SCALE_SHIFT, src_width );
+
+                       x += x_step;
+                       dest_x++;
+                       outbuf += dest_channels;
+               }
+
+               y += y_step;
+       }
+
+       g_free ( line_bufs );
+       g_free ( filter_weights );
+}
+
+
+/* Compute weights for reconstruction by replication followed by
+ * sampling with a box filter
+ */
+static inline void
+tile_make_weights ( PixopsFilterDimension *dim,
+                    double scale )
+{
+       int n = ceil ( 1 / scale + 1 );
+       double *pixel_weights = g_new ( double, SUBSAMPLE * n );
+       int offset;
+       int i;
+
+       dim->n = n;
+       dim->offset = 0;
+       dim->weights = pixel_weights;
+
+       for ( offset = 0; offset < SUBSAMPLE; offset++ )
+       {
+               double x = ( double ) offset / SUBSAMPLE;
+               double a = x + 1 / scale;
+
+               for ( i = 0; i < n; i++ )
+               {
+                       if ( i < x )
+                       {
+                               if ( i + 1 > x )
+                                       * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale;
+                               else
+                                       *( pixel_weights++ ) = 0;
+                       }
+                       else
+                       {
+                               if ( a > i )
+                                       * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale;
+                               else
+                                       *( pixel_weights++ ) = 0;
+                       }
+               }
+       }
+}
+
+/* Compute weights for a filter that, for minification
+ * is the same as 'tiles', and for magnification, is bilinear
+ * reconstruction followed by a sampling with a delta function.
+ */
+static inline void
+bilinear_magnify_make_weights ( PixopsFilterDimension *dim,
+                                double scale )
+{
+       double * pixel_weights;
+       int n;
+       int offset;
+       int i;
+
+       if ( scale > 1.0 )              /* Linear */
+       {
+               n = 2;
+               dim->offset = 0.5 * ( 1 / scale - 1 );
+       }
+       else                          /* Tile */
+       {
+               n = ceil ( 1.0 + 1.0 / scale );
+               dim->offset = 0.0;
+       }
+
+       dim->n = n;
+       dim->weights = g_new ( double, SUBSAMPLE * n );
+
+       pixel_weights = dim->weights;
+
+       for ( offset = 0; offset < SUBSAMPLE; offset++ )
+       {
+               double x = ( double ) offset / SUBSAMPLE;
+
+               if ( scale > 1.0 )        /* Linear */
+               {
+                       for ( i = 0; i < n; i++ )
+                               *( pixel_weights++ ) = ( ( ( i == 0 ) ? ( 1 - x ) : x ) / scale ) * scale;
+               }
+               else                  /* Tile */
+               {
+                       double a = x + 1 / scale;
+
+                       /*           x
+                        * ---------|--.-|----|--.-|-------  SRC
+                        * ------------|---------|---------  DEST
+                        */
+                       for ( i = 0; i < n; i++ )
+                       {
+                               if ( i < x )
+                               {
+                                       if ( i + 1 > x )
+                                               * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - x ) * scale;
+                                       else
+                                               *( pixel_weights++ ) = 0;
+                               }
+                               else
+                               {
+                                       if ( a > i )
+                                               * ( pixel_weights++ ) = ( MIN ( i + 1, a ) - i ) * scale;
+                                       else
+                                               *( pixel_weights++ ) = 0;
+                               }
+                       }
+               }
+       }
+}
+
+/* Computes the integral from b0 to b1 of
+ *
+ * f(x) = x; 0 <= x < 1
+ * f(x) = 0; otherwise
+ *
+ * We combine two of these to compute the convolution of
+ * a box filter with a triangular spike.
+ */
+static inline double
+linear_box_half ( double b0, double b1 )
+{
+       double a0, a1;
+       double x0, x1;
+
+       a0 = 0.;
+       a1 = 1.;
+
+       if ( a0 < b0 )
+       {
+               if ( a1 > b0 )
+               {
+                       x0 = b0;
+                       x1 = MIN ( a1, b1 );
+               }
+               else
+                       return 0;
+       }
+       else
+       {
+               if ( b1 > a0 )
+               {
+                       x0 = a0;
+                       x1 = MIN ( a1, b1 );
+               }
+               else
+                       return 0;
+       }
+
+       return 0.5 * ( x1 * x1 - x0 * x0 );
+}
+
+/* Compute weights for reconstructing with bilinear
+ * interpolation, then sampling with a box filter
+ */
+static inline void
+bilinear_box_make_weights ( PixopsFilterDimension *dim,
+                            double scale )
+{
+       int n = ceil ( 1 / scale + 2.0 );
+       double *pixel_weights = g_new ( double, SUBSAMPLE * n );
+       double w;
+       int offset, i;
+
+       dim->offset = -1.0;
+       dim->n = n;
+       dim->weights = pixel_weights;
+
+       for ( offset = 0 ; offset < SUBSAMPLE; offset++ )
+       {
+               double x = ( double ) offset / SUBSAMPLE;
+               double a = x + 1 / scale;
+
+               for ( i = 0; i < n; i++ )
+               {
+                       w = linear_box_half ( 0.5 + i - a, 0.5 + i - x );
+                       w += linear_box_half ( 1.5 + x - i, 1.5 + a - i );
+
+                       *( pixel_weights++ ) = w * scale;
+               }
+       }
+}
+
+
+static inline void
+make_weights ( PixopsFilter *filter,
+               PixopsInterpType interp_type,
+               double scale_x,
+               double scale_y )
+{
+       switch ( interp_type )
+       {
+       case PIXOPS_INTERP_NEAREST:
+               g_assert_not_reached ();
+               break;
+
+       case PIXOPS_INTERP_TILES:
+               tile_make_weights ( &filter->x, scale_x );
+               tile_make_weights ( &filter->y, scale_y );
+               break;
+
+       case PIXOPS_INTERP_BILINEAR:
+               bilinear_magnify_make_weights ( &filter->x, scale_x );
+               bilinear_magnify_make_weights ( &filter->y, scale_y );
+               break;
+
+       case PIXOPS_INTERP_HYPER:
+               bilinear_box_make_weights ( &filter->x, scale_x );
+               bilinear_box_make_weights ( &filter->y, scale_y );
+               break;
+       }
+}
+
+
+void
+yuv422_scale ( guchar *dest_buf,
+               int render_x0,
+               int render_y0,
+               int render_x1,
+               int render_y1,
+               int dest_rowstride,
+               int dest_channels,
+               gboolean dest_has_alpha,
+               const guchar *src_buf,
+               int src_width,
+               int src_height,
+               int src_rowstride,
+               int src_channels,
+               gboolean src_has_alpha,
+               double scale_x,
+               double scale_y,
+               PixopsInterpType interp_type )
+{
+       PixopsFilter filter = { { 0, 0, 0}, { 0, 0, 0 }, 0 };
+       PixopsLineFunc line_func;
+
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+       gboolean found_mmx = pixops_have_mmx();
+#endif
+
+       //g_return_if_fail ( !( dest_channels == 3 && dest_has_alpha ) );
+       //g_return_if_fail ( !( src_channels == 3 && src_has_alpha ) );
+       //g_return_if_fail ( !( src_has_alpha && !dest_has_alpha ) );
+
+       if ( scale_x == 0 || scale_y == 0 )
+               return ;
+
+       if ( interp_type == PIXOPS_INTERP_NEAREST )
+       {
+               pixops_scale_nearest ( dest_buf, render_x0, render_y0, render_x1, render_y1,
+                                      dest_rowstride,
+                                      src_buf, src_width, src_height, src_rowstride,
+                                      scale_x, scale_y );
+               return;
+       }
+
+       filter.overall_alpha = 1.0;
+       make_weights ( &filter, interp_type, scale_x, scale_y );
+
+       if ( filter.x.n == 2 && filter.y.n == 2 )
+       {
+#if defined(USE_MMX) && !defined(ARCH_X86_64)
+               if ( found_mmx )
+               {
+                       //fprintf( stderr, "rescale: using mmx\n" );
+                       line_func = scale_line_22_yuv_mmx_stub;
+               }
+               else
+#endif
+
+                       line_func = scale_line_22_yuv;
+       }
+       else
+               line_func = scale_line;
+
+       pixops_process ( dest_buf, render_x0, render_y0, render_x1, render_y1,
+                        dest_rowstride, dest_channels, dest_has_alpha,
+                        src_buf, src_width, src_height, src_rowstride, src_channels,
+                        src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
+                        &filter, line_func );
+
+       g_free ( filter.x.weights );
+       g_free ( filter.y.weights );
+}
+
diff --git a/src/modules/gtk2/pixops.h b/src/modules/gtk2/pixops.h
new file mode 100644 (file)
index 0000000..5b159fa
--- /dev/null
@@ -0,0 +1,72 @@
+/* GdkPixbuf library - Scaling and compositing functions
+ *
+ * Original:
+ * Copyright (C) 2000 Red Hat, Inc
+ * Author: Owen Taylor <otaylor@redhat.com>
+ *
+ * Modification for MLT:
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 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 PIXOPS_H
+#define PIXOPS_H
+
+#include <glib.h>
+
+/* Interpolation modes; must match GdkInterpType */
+typedef enum {
+       PIXOPS_INTERP_NEAREST,
+       PIXOPS_INTERP_TILES,
+       PIXOPS_INTERP_BILINEAR,
+       PIXOPS_INTERP_HYPER
+} PixopsInterpType;
+
+/* Scale src_buf from src_width / src_height by factors scale_x, scale_y
+ * and composite the portion corresponding to
+ * render_x, render_y, render_width, render_height in the new
+ * coordinate system into dest_buf starting at 0, 0
+ */
+void yuv422_scale     (guchar         *dest_buf,
+                      int             render_x0,
+                      int             render_y0,
+                      int             render_x1,
+                      int             render_y1,
+                      int             dest_rowstride,
+                      int             dest_channels,
+                      int             dest_has_alpha,
+                      const guchar   *src_buf,
+                      int             src_width,
+                      int             src_height,
+                      int             src_rowstride,
+                      int             src_channels,
+                      int             src_has_alpha,
+                      double          scale_x,
+                      double          scale_y,
+                      PixopsInterpType   interp_type);
+
+#define yuv422_scale_simple( dest_buf, dest_width, dest_height, dest_rowstride, src_buf, src_width, src_height, src_rowstride, interp_type ) \
+       yuv422_scale( (dest_buf), 0, 0, \
+               (dest_width), (dest_height), \
+               (dest_rowstride), 2, 0, \
+               (src_buf), (src_width), (src_height), \
+               (src_rowstride), 2, 0, \
+               (double) (dest_width) / (src_width), (double) (dest_height) / (src_height), \
+               (PixopsInterpType) interp_type );
+
+#endif
diff --git a/src/modules/gtk2/producer_pango.c b/src/modules/gtk2/producer_pango.c
new file mode 100644 (file)
index 0000000..3398e79
--- /dev/null
@@ -0,0 +1,721 @@
+/*
+ * producer_pango.c -- a pango-based titler
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_geometry.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pangoft2.h>
+#include <freetype/freetype.h>
+#include <iconv.h>
+#include <pthread.h>
+#include <ctype.h>
+
+typedef struct producer_pango_s *producer_pango;
+
+typedef enum
+{
+       pango_align_left = 0,
+       pango_align_center,
+       pango_align_right
+} pango_align;
+
+static pthread_mutex_t pango_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct producer_pango_s
+{
+       struct mlt_producer_s parent;
+       int width;
+       int height;
+       uint8_t *image;
+       uint8_t *alpha;
+       char *fgcolor;
+       char *bgcolor;
+       int   align;
+       int   pad;
+       char *markup;
+       char *text;
+       char *font;
+       int weight;
+};
+
+// special color type used by internal pango routines
+typedef struct
+{
+       uint8_t r, g, b, a;
+} rgba_color;
+
+// Forward declarations
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+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, int pad, int align, int weight, int size );
+
+/** Return nonzero if the two strings are equal, ignoring case, up to
+    the first n characters.
+*/
+int strncaseeq(const char *s1, const char *s2, size_t n)
+{
+       for ( ; n > 0; n--)
+       {
+               if (tolower(*s1++) != tolower(*s2++))
+                       return 0;
+       }
+       return 1;
+}
+
+/** Parse the alignment property.
+*/
+
+static int alignment_parse( char* align )
+{
+       int ret = pango_align_left;
+
+       if ( align == NULL );
+       else if ( isdigit( align[ 0 ] ) )
+               ret = atoi( align );
+       else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
+               ret = pango_align_center;
+       else if ( align[ 0 ] == 'r' )
+               ret = pango_align_right;
+
+       return ret;
+}
+
+static PangoFT2FontMap *fontmap = NULL;
+
+mlt_producer producer_pango_init( const char *filename )
+{
+       producer_pango this = calloc( sizeof( struct producer_pango_s ), 1 );
+       if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+       {
+               mlt_producer producer = &this->parent;
+
+               pthread_mutex_lock( &pango_mutex );
+               if ( fontmap == NULL )
+                       fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new();
+               g_type_init();
+               pthread_mutex_unlock( &pango_mutex );
+
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Get the properties interface
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+
+               // Set the default properties
+               mlt_properties_set( properties, "fgcolour", "0xffffffff" );
+               mlt_properties_set( properties, "bgcolour", "0x00000000" );
+               mlt_properties_set_int( properties, "align", pango_align_left );
+               mlt_properties_set_int( properties, "pad", 0 );
+               mlt_properties_set( properties, "text", "" );
+               mlt_properties_set( properties, "font", "Sans 48" );
+               mlt_properties_set( properties, "encoding", "UTF-8" );
+               mlt_properties_set_int( properties, "weight", PANGO_WEIGHT_NORMAL );
+
+               if ( filename == NULL )
+               {
+                       mlt_properties_set( properties, "markup", "" );
+               }
+               else if ( filename[ 0 ] == '+' || strstr( filename, "/+" ) )
+               {
+                       char *copy = strdup( filename + 1 );
+                       char *markup = copy;
+                       if ( strstr( markup, "/+" ) )
+                               markup = strstr( markup, "/+" ) + 2;
+                       ( *strrchr( markup, '.' ) ) = '\0';
+                       while ( strchr( markup, '~' ) )
+                               ( *strchr( markup, '~' ) ) = '\n';
+                       mlt_properties_set( properties, "resource", filename );
+                       mlt_properties_set( properties, "markup", markup );
+                       free( copy );
+               }
+               else if ( strstr( filename, ".mpl" ) ) 
+               {
+                       int i = 0;
+                       mlt_properties contents = mlt_properties_load( filename );
+                       mlt_geometry key_frames = mlt_geometry_init( );
+                       struct mlt_geometry_item_s item;
+                       mlt_properties_set( properties, "resource", filename );
+                       mlt_properties_set_data( properties, "contents", contents, 0, ( mlt_destructor )mlt_properties_close, NULL );
+                       mlt_properties_set_data( properties, "key_frames", key_frames, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+
+                       // Make sure we have at least one entry
+                       if ( mlt_properties_get( contents, "0" ) == NULL )
+                               mlt_properties_set( contents, "0", "" );
+
+                       for ( i = 0; i < mlt_properties_count( contents ); i ++ )
+                       {
+                               char *name = mlt_properties_get_name( contents, i );
+                               char *value = mlt_properties_get_value( contents, i );
+                               while ( value != NULL && strchr( value, '~' ) )
+                                       ( *strchr( value, '~' ) ) = '\n';
+                               item.frame = atoi( name );
+                               mlt_geometry_insert( key_frames, &item );
+                       }
+               }
+               else
+               {
+                       FILE *f = fopen( filename, "r" );
+                       if ( f != NULL )
+                       {
+                               char line[81];
+                               char *markup = NULL;
+                               size_t size = 0;
+                               line[80] = '\0';
+                               
+                               while ( fgets( line, 80, f ) )
+                               {
+                                       size += strlen( line ) + 1;
+                                       if ( markup )
+                                       {
+                                               markup = realloc( markup, size );
+                                               strcat( markup, line );
+                                       }
+                                       else
+                                       {
+                                               markup = strdup( line );
+                                       }
+                               }
+                               fclose( f );
+
+                               if ( markup[ strlen( markup ) - 1 ] == '\n' ) 
+                                       markup[ strlen( markup ) - 1 ] = '\0';
+
+                               mlt_properties_set( properties, "resource", filename );
+                               mlt_properties_set( properties, "markup", ( markup == NULL ? "" : markup ) );
+                               free( markup );
+                       }
+                       else
+                       {
+                               mlt_properties_set( properties, "markup", "" );
+                       }
+               }
+
+               return producer;
+       }
+       free( this );
+       return NULL;
+}
+
+static void set_string( char **string, const char *value, const char *fallback )
+{
+       if ( value != NULL )
+       {
+               free( *string );
+               *string = strdup( value );
+       }
+       else if ( *string == NULL && fallback != NULL )
+       {
+               *string = strdup( fallback );
+       }
+       else if ( *string != NULL && fallback == NULL )
+       {
+               free( *string );
+               *string = NULL;
+       }
+}
+
+rgba_color parse_color( char *color )
+{
+       rgba_color result = { 0xff, 0xff, 0xff, 0xff };
+
+       if ( !strncmp( color, "0x", 2 ) )
+       {
+               unsigned int temp = 0;
+               sscanf( color + 2, "%x", &temp );
+               result.r = ( temp >> 24 ) & 0xff;
+               result.g = ( temp >> 16 ) & 0xff;
+               result.b = ( temp >> 8 ) & 0xff;
+               result.a = ( temp ) & 0xff;
+       }
+       else if ( !strcmp( color, "red" ) )
+       {
+               result.r = 0xff;
+               result.g = 0x00;
+               result.b = 0x00;
+       }
+       else if ( !strcmp( color, "green" ) )
+       {
+               result.r = 0x00;
+               result.g = 0xff;
+               result.b = 0x00;
+       }
+       else if ( !strcmp( color, "blue" ) )
+       {
+               result.r = 0x00;
+               result.g = 0x00;
+               result.b = 0xff;
+       }
+       else
+       {
+               unsigned int temp = 0;
+               sscanf( color, "%d", &temp );
+               result.r = ( temp >> 24 ) & 0xff;
+               result.g = ( temp >> 16 ) & 0xff;
+               result.b = ( temp >> 8 ) & 0xff;
+               result.a = ( temp ) & 0xff;
+       }
+
+       return result;
+}
+
+/** Convert a string property to UTF-8
+*/
+static int iconv_utf8( mlt_properties properties, const char *prop_name, const char* encoding )
+{
+       char *text = mlt_properties_get( properties, prop_name );
+       int result = -1;
+       
+       iconv_t cd = iconv_open( "UTF-8", encoding );
+       if ( cd != ( iconv_t )-1 )
+       {
+               char *inbuf_p = text;
+               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, &inbuf_p, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 )
+                       mlt_properties_set( properties, prop_name, outbuf );
+               else
+                       mlt_properties_set( properties, prop_name, "" );
+
+               mlt_pool_release( outbuf );
+               iconv_close( cd );
+               result = 0;
+       }
+       return result;
+}
+
+static void refresh_image( mlt_frame frame, int width, int height )
+{
+       // Pixbuf 
+       GdkPixbuf *pixbuf = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "pixbuf", NULL );
+
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer pango for this frame
+       producer_pango this = mlt_properties_get_data( properties, "producer_pango", NULL );
+
+       // Obtain the producer 
+       mlt_producer producer = &this->parent;
+
+       // Obtain the producer properties
+       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Get producer properties
+       char *fg = mlt_properties_get( producer_props, "fgcolour" );
+       char *bg = mlt_properties_get( producer_props, "bgcolour" );
+       int align = alignment_parse( mlt_properties_get( producer_props, "align" ) );
+       int pad = mlt_properties_get_int( producer_props, "pad" );
+       char *markup = mlt_properties_get( producer_props, "markup" );
+       char *text = mlt_properties_get( producer_props, "text" );
+       char *font = mlt_properties_get( producer_props, "font" );
+       char *encoding = mlt_properties_get( producer_props, "encoding" );
+       int weight = mlt_properties_get_int( producer_props, "weight" );
+       int size = mlt_properties_get_int( producer_props, "size" );
+       int property_changed = 0;
+
+       if ( pixbuf == NULL )
+       {
+               // Check for file support
+               int position = mlt_properties_get_position( properties, "pango_position" );
+               mlt_properties contents = mlt_properties_get_data( producer_props, "contents", NULL );
+               mlt_geometry key_frames = mlt_properties_get_data( producer_props, "key_frames", NULL );
+               struct mlt_geometry_item_s item;
+               if ( contents != NULL )
+               {
+                       char temp[ 20 ];
+                       mlt_geometry_prev_key( key_frames, &item, position );
+                       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 ) ) );
+               property_changed = property_changed || ( this->bgcolor == NULL || ( bg && strcmp( bg, this->bgcolor ) ) );
+               property_changed = property_changed || ( pad != this->pad );
+               property_changed = property_changed || ( markup && this->markup && strcmp( markup, this->markup ) );
+               property_changed = property_changed || ( text && this->text && strcmp( text, this->text ) );
+               property_changed = property_changed || ( font && this->font && strcmp( font, this->font ) );
+               property_changed = property_changed || ( weight != this->weight );
+
+               // Save the properties for next comparison
+               this->align = align;
+               this->pad = pad;
+               set_string( &this->fgcolor, fg, "0xffffffff" );
+               set_string( &this->bgcolor, bg, "0x00000000" );
+               set_string( &this->markup, markup, NULL );
+               set_string( &this->text, text, NULL );
+               set_string( &this->font, font, "Sans 48" );
+               this->weight = weight;
+       }
+
+       if ( pixbuf == NULL && property_changed )
+       {
+               rgba_color fgcolor = parse_color( this->fgcolor );
+               rgba_color bgcolor = parse_color( this->bgcolor );
+
+               mlt_pool_release( this->image );
+               mlt_pool_release( this->alpha );
+               this->image = NULL;
+               this->alpha = NULL;
+
+               // Convert from specified encoding to UTF-8
+               if ( encoding != NULL && !strncaseeq( encoding, "utf-8", 5 ) && !strncaseeq( encoding, "utf8", 4 ) )
+               {
+                       if ( markup != NULL && iconv_utf8( producer_props, "markup", encoding ) != -1 )
+                       {
+                               markup = mlt_properties_get( producer_props, "markup" );
+                               set_string( &this->markup, markup, NULL );
+                       }
+                       if ( text != NULL && iconv_utf8( producer_props, "text", encoding ) != -1 )
+                       {
+                               text = mlt_properties_get( producer_props, "text" );
+                               set_string( &this->text, text, NULL );
+                       }
+               }
+               
+               // Render the title
+               pixbuf = pango_get_pixbuf( markup, text, font, fgcolor, bgcolor, pad, align, weight, size );
+
+               if ( pixbuf != NULL )
+               {
+                       // Register this pixbuf for destruction and reuse
+                       mlt_properties_set_data( producer_props, "pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref, NULL );
+                       g_object_ref( pixbuf );
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref, NULL );
+
+                       mlt_properties_set_int( producer_props, "real_width", gdk_pixbuf_get_width( pixbuf ) );
+                       mlt_properties_set_int( producer_props, "real_height", gdk_pixbuf_get_height( pixbuf ) );
+
+                       // Store the width/height of the pixbuf temporarily
+                       this->width = gdk_pixbuf_get_width( pixbuf );
+                       this->height = gdk_pixbuf_get_height( pixbuf );
+               }
+       }
+       else if ( pixbuf == NULL && ( width > 0 && ( this->image == NULL || width != this->width || height != this->height ) ) )
+       {
+               mlt_pool_release( this->image );
+               mlt_pool_release( this->alpha );
+               this->image = NULL;
+               this->alpha = NULL;
+
+               pixbuf = mlt_properties_get_data( producer_props, "pixbuf", NULL );
+       }
+
+       // If we have a pixbuf and a valid width
+       if ( pixbuf && width > 0 )
+       {
+               char *interps = mlt_properties_get( properties, "rescale.interp" );
+               int interp = GDK_INTERP_BILINEAR;
+
+               if ( strcmp( interps, "nearest" ) == 0 )
+                       interp = GDK_INTERP_NEAREST;
+               else if ( strcmp( interps, "tiles" ) == 0 )
+                       interp = GDK_INTERP_TILES;
+               else if ( strcmp( interps, "hyper" ) == 0 )
+                       interp = GDK_INTERP_HYPER;
+
+// fprintf(stderr,"%s: scaling from %dx%d to %dx%d\n", __FILE__, this->width, this->height, width, height);
+
+               // Note - the original pixbuf is already safe and ready for destruction
+               pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp );
+
+               // Store width and height
+               this->width = width;
+               this->height = height;
+
+               // Allocate/define image
+               this->image = mlt_pool_alloc( width * ( height + 1 ) * 2 );
+               this->alpha = mlt_pool_alloc( this->width * this->height );
+
+               // Convert the image
+               mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+                                                                         this->width, this->height,
+                                                                         gdk_pixbuf_get_rowstride( pixbuf ),
+                                                                         this->image, this->alpha );
+
+               // Finished with pixbuf now
+               g_object_unref( pixbuf );
+       }
+
+       // Set width/height
+       mlt_properties_set_int( properties, "width", this->width );
+       mlt_properties_set_int( properties, "height", this->height );
+       mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "real_width" ) );
+       mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "real_height" ) );
+
+       // pass the image data without destructor
+       mlt_properties_set_data( properties, "image", this->image, this->width * ( this->height + 1 ) * 2, NULL, NULL );
+       mlt_properties_set_data( properties, "alpha", this->alpha, this->width * this->height, NULL, NULL );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // We need to know the size of the image to clone it
+       int image_size = 0;
+       int alpha_size = 0;
+
+       // Alpha channel
+       uint8_t *alpha = NULL;
+
+       *width = mlt_properties_get_int( properties, "rescale_width" );
+       *height = mlt_properties_get_int( properties, "rescale_height" );
+
+       // Refresh the image
+       pthread_mutex_lock( &pango_mutex );
+       refresh_image( frame, *width, *height );
+
+       // Get the image
+       *buffer = mlt_properties_get_data( properties, "image", &image_size );
+       alpha = mlt_properties_get_data( properties, "alpha", &alpha_size );
+
+       // Get width and height
+       *width = mlt_properties_get_int( properties, "width" );
+       *height = mlt_properties_get_int( properties, "height" );
+
+       // Always clone here to allow 'animated' text
+       if ( *buffer != NULL )
+       {
+               // Clone the image and the alpha
+               uint8_t *image_copy = mlt_pool_alloc( image_size );
+               uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+               memcpy( image_copy, *buffer, image_size );
+
+               // Copy or default the alpha
+               if ( alpha != NULL )
+                       memcpy( alpha_copy, alpha, alpha_size );
+               else
+                       memset( alpha_copy, 255, alpha_size );
+
+               // Now update properties so we free the copy after
+               mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+               // We're going to pass the copy on
+               *buffer = image_copy;
+       }
+       else
+       {
+               // TODO: Review all cases of invalid images
+               *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+               mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+               *width = 50;
+               *height = 50;
+       }
+
+       pthread_mutex_unlock( &pango_mutex );
+
+       return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Return the alpha mask
+       return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       producer_pango this = producer->child;
+
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       // Obtain properties of frame and producer
+       mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+       // Set the producer on the frame properties
+       mlt_properties_set_data( properties, "producer_pango", this, 0, NULL, NULL );
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+       mlt_properties_set_position( properties, "pango_position", mlt_producer_frame( 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 );
+       mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+       // Set alpha call back
+       ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+       // Stack the get image callback
+       mlt_frame_push_get_image( *frame, producer_get_image );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       producer_pango this = parent->child;
+       mlt_pool_release( this->image );
+       mlt_pool_release( this->alpha );
+       free( this->fgcolor );
+       free( this->bgcolor );
+       free( this->markup );
+       free( this->text );
+       free( this->font );
+       parent->close = NULL;
+       mlt_producer_close( parent );
+       free( this );
+}
+
+static void pango_draw_background( GdkPixbuf *pixbuf, rgba_color bg )
+{
+       int ww = gdk_pixbuf_get_width( pixbuf );
+       int hh = gdk_pixbuf_get_height( pixbuf );
+       uint8_t *p = gdk_pixbuf_get_pixels( pixbuf );
+       int i, j;
+
+       for ( j = 0; j < hh; j++ )
+       {
+               for ( i = 0; i < ww; i++ )
+               {
+                       *p++ = bg.r;
+                       *p++ = bg.g;
+                       *p++ = bg.b;
+                       *p++ = bg.a;
+               }
+       }
+}
+
+static GdkPixbuf *pango_get_pixbuf( const char *markup, const char *text, const char *font, rgba_color fg, rgba_color bg, int pad, int align, int weight, int size )
+{
+       PangoContext *context = pango_ft2_font_map_create_context( fontmap );
+       PangoLayout *layout = pango_layout_new( context );
+       int w, h, x;
+       int i, j;
+       GdkPixbuf *pixbuf = NULL;
+       FT_Bitmap bitmap;
+       uint8_t *src = NULL;
+       uint8_t* dest = NULL;
+       uint8_t *d, *s, a;
+       int stride;
+       PangoFontDescription *desc = pango_font_description_from_string( font );
+
+       pango_ft2_font_map_set_resolution( fontmap, 72, 72 );
+       pango_layout_set_width( layout, -1 ); // set wrapping constraints
+       pango_font_description_set_weight( desc, ( PangoWeight ) weight  );
+       if ( size != 0 )
+               pango_font_description_set_absolute_size( desc, PANGO_SCALE * size );
+       pango_layout_set_font_description( layout, desc );
+//     pango_layout_set_spacing( layout, space );
+       pango_layout_set_alignment( layout, ( PangoAlignment ) align  );
+       if ( markup != NULL && strcmp( markup, "" ) != 0 )
+       {
+               pango_layout_set_markup( layout, markup, strlen( markup ) );
+       }
+       else if ( text != NULL && strcmp( text, "" ) != 0 )
+       {
+               // Replace all ~'s with a line feed (silly convention, but handy)
+               char *copy = strdup( text );
+               while ( strchr( copy, '~' ) )
+                       ( *strchr( copy, '~' ) ) = '\n';
+               pango_layout_set_text( layout, copy, strlen( copy ) );
+               free( copy );
+       }
+       else
+       {
+               // Pango doesn't like empty strings
+               pango_layout_set_text( layout, "  ", 2 );
+       }
+       pango_layout_get_pixel_size( layout, &w, &h );
+
+       // Interpret size property as an absolute pixel height and compensate for
+       // freetype's "interpretation" of our absolute size request. This gives
+       // precise control over compositing and better quality by reducing scaling
+       // artifacts with composite geometries that constrain the dimensions.
+       // If you do not want this, then put the size in the font property or in
+       // the pango markup.
+       if ( size != 0 )
+       {
+               pango_font_description_set_absolute_size( desc, PANGO_SCALE * size * size/h );
+               pango_layout_set_font_description( layout, desc );
+               pango_layout_get_pixel_size( layout, &w, &h );
+       }
+
+       pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad );
+       pango_draw_background( pixbuf, bg );
+
+       stride = gdk_pixbuf_get_rowstride( pixbuf );
+
+       bitmap.width     = w;
+       bitmap.pitch     = 32 * ( ( w + 31 ) / 31 );
+       bitmap.rows      = h;
+       bitmap.buffer    = mlt_pool_alloc( h * bitmap.pitch );
+       bitmap.num_grays = 256;
+       bitmap.pixel_mode = ft_pixel_mode_grays;
+
+       memset( bitmap.buffer, 0, h * bitmap.pitch );
+
+       pango_ft2_render_layout( &bitmap, layout, 0, 0 );
+
+       src = bitmap.buffer;
+       x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad;
+       dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride;
+       j = h;
+
+       while( j -- )
+       {
+               d = dest;
+               s = src;
+               i = w;
+               while( i -- )
+               {
+                       a = *s ++;
+                       *d++ = ( a * fg.r + ( 255 - a ) * bg.r ) >> 8;
+                       *d++ = ( a * fg.g + ( 255 - a ) * bg.g ) >> 8;
+                       *d++ = ( a * fg.b + ( 255 - a ) * bg.b ) >> 8;
+                       *d++ = ( a * fg.a + ( 255 - a ) * bg.a ) >> 8;
+               }
+               dest += stride;
+               src += bitmap.pitch;
+       }
+       mlt_pool_release( bitmap.buffer );
+       pango_font_description_free( desc );
+       g_object_unref( layout );
+       g_object_unref( context );
+
+       return pixbuf;
+}
diff --git a/src/modules/gtk2/producer_pixbuf.c b/src/modules/gtk2/producer_pixbuf.c
new file mode 100644 (file)
index 0000000..70df32c
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_cache.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+typedef struct producer_pixbuf_s *producer_pixbuf;
+
+struct producer_pixbuf_s
+{
+       struct mlt_producer_s parent;
+
+       // File name list
+       mlt_properties filenames;
+       int count;
+       int image_idx;
+
+       int width;
+       int height;
+       uint8_t *image;
+       uint8_t *alpha;
+       mlt_cache_item image_cache;
+       mlt_cache_item alpha_cache;
+       pthread_mutex_t mutex;
+};
+
+static void load_filenames( producer_pixbuf this, mlt_properties producer_properties );
+static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height );
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_pixbuf_init( char *filename )
+{
+       producer_pixbuf this = calloc( sizeof( struct producer_pixbuf_s ), 1 );
+       if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+       {
+               mlt_producer producer = &this->parent;
+
+               // Get the properties interface
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+       
+               // Callback registration
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Set the default properties
+               mlt_properties_set( properties, "resource", filename );
+               mlt_properties_set_int( properties, "ttl", 25 );
+               mlt_properties_set_int( properties, "aspect_ratio", 1 );
+               mlt_properties_set_int( properties, "progressive", 1 );
+
+               // Validate the resource
+               if ( filename )
+                       load_filenames( this, properties );
+               if ( this->count )
+               {
+                       mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+                       if ( frame )
+                       {
+                               mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+                               pthread_mutex_init( &this->mutex, NULL );
+                               mlt_properties_set_data( frame_properties, "producer_pixbuf", this, 0, NULL, NULL );
+                               mlt_frame_set_position( frame, mlt_producer_position( producer ) );
+                               mlt_properties_set_position( frame_properties, "pixbuf_position", mlt_producer_position( producer ) );
+                               refresh_image( this, frame, 0, 0 );
+                               mlt_frame_close( frame );
+                       }
+               }
+               if ( this->width == 0 )
+               {
+                       producer_close( producer );
+                       producer = NULL;
+               }
+               return producer;
+       }
+       free( this );
+       return NULL;
+}
+
+static void load_filenames( producer_pixbuf this, mlt_properties producer_properties )
+{
+       char *filename = mlt_properties_get( producer_properties, "resource" );
+       this->filenames = mlt_properties_new( );
+
+       // Read xml string
+       if ( strstr( filename, "<svg" ) )
+       {
+               // Generate a temporary file for the svg
+               char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
+               int fd = mkstemp( fullname );
+
+               if ( fd > -1 )
+               {
+                       // Write the svg into the temp file
+                       ssize_t remaining_bytes;
+                       char *xml = filename;
+                       
+                       // Strip leading crap
+                       while ( xml[0] != '<' )
+                               xml++;
+                       
+                       remaining_bytes = strlen( xml );
+                       while ( remaining_bytes > 0 )
+                               remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
+                       close( fd );
+
+                       mlt_properties_set( this->filenames, "0", fullname );
+
+                       // Teehe - when the producer closes, delete the temp file and the space allo
+                       mlt_properties_set_data( producer_properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
+               }
+       }
+       // Obtain filenames
+       else if ( strchr( filename, '%' ) != NULL )
+       {
+               // handle picture sequences
+               int i = mlt_properties_get_int( producer_properties, "begin" );
+               int gap = 0;
+               char full[1024];
+               int keyvalue = 0;
+               char key[ 50 ];
+
+               while ( gap < 100 )
+               {
+                       struct stat buf;
+                       snprintf( full, 1023, filename, i ++ );
+                       if ( stat( full, &buf ) == 0 )
+                       {
+                               sprintf( key, "%d", keyvalue ++ );
+                               mlt_properties_set( this->filenames, key, full );
+                               gap = 0;
+                       }
+                       else
+                       {
+                               gap ++;
+                       }
+               }
+       }
+       else if ( strstr( filename, "/.all." ) != NULL )
+       {
+               char wildcard[ 1024 ];
+               char *dir_name = strdup( filename );
+               char *extension = strrchr( dir_name, '.' );
+
+               *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+               sprintf( wildcard, "*%s", extension );
+
+               mlt_properties_dir_list( this->filenames, dir_name, wildcard, 1 );
+
+               free( dir_name );
+       }
+       else
+       {
+               mlt_properties_set( this->filenames, "0", filename );
+       }
+
+       this->count = mlt_properties_count( this->filenames );
+}
+
+static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer
+       mlt_producer producer = &this->parent;
+
+       // Obtain properties of producer
+       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Obtain the cache flag and structure
+       int use_cache = mlt_properties_get_int( producer_props, "cache" );
+       mlt_properties cache = mlt_properties_get_data( producer_props, "_cache", NULL );
+       int update_cache = 0;
+
+       // restore GdkPixbuf
+       pthread_mutex_lock( &this->mutex );
+       mlt_cache_item pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
+       GdkPixbuf *pixbuf = mlt_cache_item_data( pixbuf_cache, NULL );
+       GError *error = NULL;
+
+       // restore scaled image
+       this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
+       this->image = mlt_cache_item_data( this->image_cache, NULL );
+
+       // restore alpha channel
+       this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
+       this->alpha = mlt_cache_item_data( this->alpha_cache, NULL );
+
+       // Check if user wants us to reload the image
+       if ( mlt_properties_get_int( producer_props, "force_reload" ) ) 
+       {
+               pixbuf = NULL;
+               this->image = NULL;
+               mlt_properties_set_int( producer_props, "force_reload", 0 );
+       }
+
+       // Get the time to live for each frame
+       double ttl = mlt_properties_get_int( producer_props, "ttl" );
+
+       // Get the original position of this frame
+       mlt_position position = mlt_properties_get_position( properties, "pixbuf_position" );
+       position += mlt_producer_get_in( producer );
+
+       // Image index
+       int image_idx = ( int )floor( ( double )position / ttl ) % this->count;
+
+       // Key for the cache
+       char image_key[ 10 ];
+       sprintf( image_key, "%d", image_idx );
+
+       pthread_mutex_lock( &g_mutex );
+
+       // Check if the frame is already loaded
+       if ( use_cache )
+       {
+               if ( cache == NULL )
+               {
+                       cache = mlt_properties_new( );
+                       mlt_properties_set_data( producer_props, "_cache", cache, 0, ( mlt_destructor )mlt_properties_close, NULL );
+               }
+
+               mlt_frame cached = mlt_properties_get_data( cache, image_key, NULL );
+
+               if ( cached )
+               {
+                       this->image_idx = image_idx;
+                       mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+                       this->width = mlt_properties_get_int( cached_props, "width" );
+                       this->height = mlt_properties_get_int( cached_props, "height" );
+                       mlt_properties_set_int( producer_props, "_real_width", mlt_properties_get_int( cached_props, "real_width" ) );
+                       mlt_properties_set_int( producer_props, "_real_height", mlt_properties_get_int( cached_props, "real_height" ) );
+                       this->image = mlt_properties_get_data( cached_props, "image", NULL );
+                       this->alpha = mlt_properties_get_data( cached_props, "alpha", NULL );
+
+                       if ( width != 0 && ( width != this->width || height != this->height ) )
+                               this->image = NULL;
+               }
+       }
+
+    // optimization for subsequent iterations on single picture
+       if ( width != 0 && ( image_idx != this->image_idx || width != this->width || height != this->height ) )
+               this->image = NULL;
+       if ( image_idx != this->image_idx )
+               pixbuf = NULL;
+       if ( pixbuf == NULL && ( width == 0 || this->image == NULL ) )
+       {
+               this->image = NULL;
+               this->image_idx = image_idx;
+               pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( this->filenames, image_idx ), &error );
+
+               if ( pixbuf )
+               {
+                       // Register this pixbuf for destruction and reuse
+                       mlt_cache_item_close( pixbuf_cache );
+                       mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref );
+                       pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
+
+                       mlt_events_block( producer_props, NULL );
+                       mlt_properties_set_int( producer_props, "_real_width", gdk_pixbuf_get_width( pixbuf ) );
+                       mlt_properties_set_int( producer_props, "_real_height", gdk_pixbuf_get_height( pixbuf ) );
+                       mlt_events_unblock( producer_props, NULL );
+
+                       // Store the width/height of the pixbuf temporarily
+                       this->width = gdk_pixbuf_get_width( pixbuf );
+                       this->height = gdk_pixbuf_get_height( pixbuf );
+               }
+       }
+
+       // If we have a pixbuf and we need an image
+       if ( pixbuf && width > 0 && this->image == NULL )
+       {
+               char *interps = mlt_properties_get( properties, "rescale.interp" );
+               int interp = GDK_INTERP_BILINEAR;
+
+               if ( strcmp( interps, "nearest" ) == 0 )
+                       interp = GDK_INTERP_NEAREST;
+               else if ( strcmp( interps, "tiles" ) == 0 )
+                       interp = GDK_INTERP_TILES;
+               else if ( strcmp( interps, "hyper" ) == 0 )
+                       interp = GDK_INTERP_HYPER;
+
+               // Note - the original pixbuf is already safe and ready for destruction
+               pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp );
+
+               // Store width and height
+               this->width = width;
+               this->height = height;
+               
+               // Allocate/define image
+               this->image = mlt_pool_alloc( width * ( height + 1 ) * 2 );
+               if ( !use_cache )
+                       mlt_cache_item_close( this->image_cache );
+               mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", this->image, width * ( height + 1 ) * 2, mlt_pool_release );
+               this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
+
+               // Extract YUV422 and alpha
+               if ( gdk_pixbuf_get_has_alpha( pixbuf ) )
+               {
+                       // Allocate the alpha mask
+                       this->alpha = mlt_pool_alloc( this->width * this->height );
+                       if ( !use_cache )
+                               mlt_cache_item_close( this->alpha_cache );
+                       mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", this->alpha, width * height, mlt_pool_release );
+                       this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
+
+                       // Convert the image
+                       mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+                                                                                 this->width, this->height,
+                                                                                 gdk_pixbuf_get_rowstride( pixbuf ),
+                                                                                 this->image, this->alpha );
+               }
+               else
+               { 
+                       // No alpha to extract
+                       mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
+                                                                                this->width, this->height,
+                                                                                gdk_pixbuf_get_rowstride( pixbuf ),
+                                                                                this->image );
+               }
+
+               // Finished with pixbuf now
+               g_object_unref( pixbuf );
+
+               // Ensure we update the cache when we need to
+               update_cache = use_cache;
+       }
+
+       // release references no longer needed
+       mlt_cache_item_close( pixbuf_cache );
+       if ( width == 0 )
+       {
+               pthread_mutex_unlock( &this->mutex );
+               mlt_cache_item_close( this->image_cache );
+               mlt_cache_item_close( this->alpha_cache );
+       }
+
+       // Set width/height of frame
+       mlt_properties_set_int( properties, "width", this->width );
+       mlt_properties_set_int( properties, "height", this->height );
+       mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+       mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+
+       if ( update_cache )
+       {
+               mlt_frame cached = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+               mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+               mlt_properties_set_int( cached_props, "width", this->width );
+               mlt_properties_set_int( cached_props, "height", this->height );
+               mlt_properties_set_int( cached_props, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+               mlt_properties_set_int( cached_props, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+               mlt_properties_set_data( cached_props, "image", this->image, this->width * ( this->height + 1 ) * 2, mlt_pool_release, NULL );
+               mlt_properties_set_data( cached_props, "alpha", this->alpha, this->width * this->height, mlt_pool_release, NULL );
+               mlt_properties_set_data( cache, image_key, cached, 0, ( mlt_destructor )mlt_frame_close, NULL );
+       }
+
+       pthread_mutex_unlock( &g_mutex );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer for this frame
+       producer_pixbuf this = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
+
+       *width = mlt_properties_get_int( properties, "rescale_width" );
+       *height = mlt_properties_get_int( properties, "rescale_height" );
+
+       // Refresh the image
+       refresh_image( this, frame, *width, *height );
+
+       // Get the image size
+       int image_size = this->width * ( this->height + 1 ) * 2;
+       int alpha_size = this->width * this->height;
+
+       // Get width and height (may have changed during the refresh)
+       *width = mlt_properties_get_int( properties, "width" );
+       *height = mlt_properties_get_int( properties, "height" );
+
+       // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
+       // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
+       if ( this->image )
+       {
+               if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+               {
+                       // Clone the image and the alpha
+                       uint8_t *image_copy = mlt_pool_alloc( image_size );
+                       uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+                       memcpy( image_copy, this->image, image_size );
+
+                       // Copy or default the alpha
+                       if ( this->alpha != NULL )
+                               memcpy( alpha_copy, this->alpha, alpha_size );
+                       else
+                               memset( alpha_copy, 255, alpha_size );
+
+                       // Now update properties so we free the copy after
+                       mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+                       mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+                       // We're going to pass the copy on
+                       *buffer = image_copy;
+               }
+               else if ( *format == mlt_image_rgb24a )
+               {
+                       // Clone the image and the alpha
+                       image_size = *width * ( *height + 1 ) * 4;
+                       alpha_size = *width * ( *height + 1 );
+                       uint8_t *image_copy = mlt_pool_alloc( image_size );
+                       uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+                       mlt_convert_yuv422_to_rgb24a( this->image, image_copy, (*width)*(*height) );
+
+                       // Now update properties so we free the copy after
+                       mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+                       mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+                       // We're going to pass the copy on
+                       *buffer = image_copy;
+               }
+
+       }
+       else
+       {
+               // TODO: Review all cases of invalid images
+               *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+               mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+               *width = 50;
+               *height = 50;
+       }
+
+       // Release references and locks
+       pthread_mutex_unlock( &this->mutex );
+       mlt_cache_item_close( this->image_cache );
+       mlt_cache_item_close( this->alpha_cache );
+
+       return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Return the alpha mask
+       return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Get the real structure for this producer
+       producer_pixbuf this = producer->child;
+
+       // Fetch the producers properties
+       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+       if ( this->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
+               load_filenames( this, producer_properties );
+
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       if ( *frame != NULL && this->count > 0 )
+       {
+               // Obtain properties of frame and producer
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Set the producer on the frame properties
+               mlt_properties_set_data( properties, "producer_pixbuf", this, 0, NULL, NULL );
+
+               // Update timecode on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+               // Ensure that we have a way to obtain the position in the get_image
+               mlt_properties_set_position( properties, "pixbuf_position", mlt_producer_position( producer ) );
+
+               // Refresh the image
+               refresh_image( this, *frame, 0, 0 );
+
+               // Set producer-specific frame properties
+               mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
+
+               // Set alpha call back
+               ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+               // Push the get_image method
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       producer_pixbuf this = parent->child;
+       pthread_mutex_destroy( &this->mutex );
+       parent->close = NULL;
+       mlt_producer_close( parent );
+       mlt_properties_close( this->filenames );
+       free( this );
+}
diff --git a/src/modules/gtk2/scale_line_22_yuv_mmx.S b/src/modules/gtk2/scale_line_22_yuv_mmx.S
new file mode 100644 (file)
index 0000000..d78b341
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * scale_line_22_yuv_mmx.S -- scale line in YUY2 format
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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
+ */
+       .file   "scale_line_22_yuv_mmx.S"
+       .version        "01.01"
+
+.extern printf
+
+gcc2_compiled.:
+.data
+MSG: .ascii "scale_line_22_yuv_mmx: %d %d\n"
+
+.text
+       .align 16
+
+#if !defined(__MINGW32__) && !defined(__CYGWIN__)      
+       
+.globl pixops_scale_line_22_yuv_mmx
+       .type    pixops_scale_line_22_yuv_mmx,@function
+pixops_scale_line_22_yuv_mmx:
+       
+#else
+       
+.globl _pixops_scale_line_22_yuv_mmx
+_pixops_scale_line_22_yuv_mmx:
+       
+#endif
+/*
+ * Arguments
+ *             
+ * weights:         8(%ebp)
+ * p (dest):    12(%ebp)       %esi
+ * q1 (src0):   16(%ebp)       
+ * q2 (src1):   20(%ebp)       
+ * xstep:       24(%ebp)       
+ * p_end:       28(%ebp)
+ * xinit:       32(%ebp)
+ * dest_x:      36(%ebp)
+ *
+ */
+
+/*
+ * Function call entry
+ */
+       pushl %ebp
+       movl %esp,%ebp
+       subl $28,%esp
+       pushl %edi
+       pushl %esi
+       pushl %ebx
+/* Locals:
+ * int x                      %ebx
+ * int x_scaled             -24(%ebp)
+ * int dest_x               36(%ebp)
+ */
+
+/*
+ * Setup
+ */
+/* Initialize variables */
+       movl 36(%ebp),%eax # destx
+       movl %eax,36(%ebp)
+       movl 32(%ebp),%ebx # x
+       movl 12(%ebp),%esi # dest
+
+       cmpl 28(%ebp),%esi # dest == dest_end ?
+       jnb  .out
+
+/* For the body of this loop, %mm0, %mm1, %mm2, %mm3 hold the 4 adjoining
+ * points we are interpolating between, as:
+ *
+ *  00VV00Y200UU00Y1
+ */
+
+       pxor %mm4, %mm4
+/*
+ * Load next component values into mm1 (src0) and mm3 (src1)
+ */
+       movl %ebx, %eax          # x_scaled
+       sarl $15, %eax
+       andl $0xfffffffe, %eax
+       movl %eax, %edx          # x_aligned
+       andl $0xfffffffc, %edx
+
+       movl 16(%ebp), %edi      # get src0
+       movl (%edi,%eax), %ecx   # get y
+       andl $0x00ff00ff, %ecx   # mask off y
+       movl (%edi,%edx), %eax   # get uv
+       andl $0xff00ff00, %eax   # mask off uv
+       orl %eax, %ecx           # composite y, uv
+       movd %ecx, %mm1          # move to mmx1
+       punpcklbw %mm4, %mm1
+
+       movl 20(%ebp), %edi      # get src1
+       movl (%edi,%edx), %ecx   # get y
+       andl $0x00ff00ff, %ecx   # mask off y
+       movl (%edi,%edx), %eax   # get uv
+       andl $0xff00ff00, %eax   # mask off uv
+       orl %eax, %ecx           # composite y, uv
+       movd %ecx, %mm3          # move to mmx3
+       punpcklbw %mm4, %mm3
+
+       jmp .newx
+
+       .p2align 4,,7
+.loop:
+
+/* short *pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y
+ *                                             16             4                  0xf            2     2
+ */
+       movl 8(%ebp), %edi       # get weights pointer
+       movl %ebx, %eax
+       andl $0xf000, %eax
+       shrl $7, %eax
+
+/* At this point, %edi holds weights. Load the 4 weights into 
+ * %mm4,%mm5,%mm6,%mm7, multiply and accumulate.
+ */
+       movq (%edi,%eax), %mm4
+       pmullw %mm0, %mm4
+       movq 8(%edi,%eax), %mm5
+       pmullw %mm1, %mm5
+       movq 16(%edi,%eax), %mm6
+       pmullw %mm2,%mm6
+       movq 24(%edi,%eax), %mm7
+       pmullw %mm3,%mm7
+
+       paddw %mm4, %mm5
+       paddw %mm6, %mm7
+       paddw %mm5, %mm7
+
+/* %mm7 holds the accumulated sum. Compute (C + 0x80) / 256
+ */
+       pxor %mm4, %mm4
+       movl $0x80808080, %eax
+       movd %eax, %mm6
+       punpcklbw %mm4, %mm6
+       paddw %mm6, %mm7
+       psrlw $8, %mm7
+
+/* Pack into %eax and store result
+ */
+       packuswb %mm7, %mm7
+       movd %mm7, %eax
+
+       movb %al, (%esi)         # *dest = y
+       
+       movl 36(%ebp), %ecx      # get dest_x
+       andl $1, %ecx            # select u or v
+       sall $1, %ecx            # determine offset
+       addl $1, %ecx            # relative to x_aligned
+       sall $3, %ecx            # offset * 8 bits/byte
+
+       movd %mm7, %eax
+       shrl %cl, %eax
+       movb %al, 1(%esi)        # *dest = uv
+
+       addl $2, %esi            # dest += 2
+       cmpl %esi,28(%ebp)       # if dest == dest_end
+       je   .out                # then exit
+
+       addl $1, 36(%ebp)        # dest_x++
+
+.newx:
+
+       addl 24(%ebp), %ebx      # x += x_step
+/*
+ * Load current component values into mm0 (src0) and mm2 (src1)
+ */
+       movq %mm1, %mm0
+       movq %mm3, %mm2
+
+/*
+ * Load next component values into mm1 (src0) and mm3 (src1)
+ */
+       movl %ebx, %eax          # x_scaled
+       sarl $15, %eax
+       andl $0xfffffffe, %eax
+       movl %eax, %edx          # x_aligned
+       andl $0xfffffffc, %edx
+
+       movl 16(%ebp), %edi      # get src0
+       movl (%edi,%eax), %ecx   # get y
+       andl $0x00ff00ff, %ecx   # mask off y
+       movl (%edi,%edx), %eax   # get uv
+       andl $0xff00ff00, %eax   # mask off uv
+       orl %eax, %ecx           # composite y, uv
+       movd %ecx, %mm1          # move to mmx1
+       punpcklbw %mm4, %mm1
+
+       movl 20(%ebp), %edi      # get src1
+       movl (%edi,%edx), %ecx   # get y
+       andl $0x00ff00ff, %ecx   # mask off y
+       movl (%edi,%edx), %eax   # get uv
+       andl $0xff00ff00, %eax   # mask off uv
+       orl %eax, %ecx           # composite y, uv
+       movd %ecx, %mm3          # move to mmx3
+       punpcklbw %mm4, %mm3
+
+       jmp .loop
+
+.out:
+       movl %esi,%eax
+       emms
+       leal -40(%ebp),%esp
+       popl %ebx
+       popl %esi
+       popl %edi
+       movl %ebp,%esp
+       popl %ebp
+       ret
diff --git a/src/modules/inigo/Makefile b/src/modules/inigo/Makefile
new file mode 100644 (file)
index 0000000..b2e297f
--- /dev/null
@@ -0,0 +1,33 @@
+include ../../../config.mak
+
+TARGET = ../libmltinigo$(LIBSUF)
+
+OBJS = factory.o \
+          producer_inigo.o 
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/inigo/factory.c b/src/modules/inigo/factory.c
new file mode 100644 (file)
index 0000000..c1b2d0e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_inigo_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( producer_type, "inigo", producer_inigo_init );
+       MLT_REGISTER( producer_type, "inigo_file", producer_inigo_file_init );
+}
diff --git a/src/modules/inigo/producer_inigo.c b/src/modules/inigo/producer_inigo.c
new file mode 100644 (file)
index 0000000..88ea2a5
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * producer_inigo.c -- simple inigo test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv );
+
+mlt_producer producer_inigo_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+       FILE *input = fopen( file, "r" );
+       char **args = calloc( sizeof( char * ), 1000 );
+       int count = 0;
+       char temp[ 2048 ];
+
+       if ( input != NULL )
+       {
+               while( fgets( temp, 2048, input ) )
+               {
+                       temp[ strlen( temp ) - 1 ] = '\0';
+                       if ( strcmp( temp, "" ) )
+                               args[ count ++ ] = strdup( temp );
+               }
+       }
+
+       mlt_producer result = producer_inigo_init( profile, type, id, args );
+
+       if ( result != NULL )
+       {
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( result );
+               mlt_properties_set( properties, "resource", file );
+       }
+
+       while( count -- )
+               free( args[ count ] );
+       free( args );
+
+       return result;
+}
+
+static void track_service( mlt_field field, void *service, mlt_destructor destructor )
+{
+       mlt_properties properties = mlt_field_properties( field );
+       int registered = mlt_properties_get_int( properties, "registered" );
+       char *key = mlt_properties_get( properties, "registered" );
+       mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+       mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+static mlt_producer create_producer( mlt_profile profile, mlt_field field, char *file )
+{
+       mlt_producer result = mlt_factory_producer( profile, "fezzik", file );
+
+       if ( result != NULL )
+               track_service( field, result, ( mlt_destructor )mlt_producer_close );
+
+       return result;
+}
+
+static mlt_filter create_attach( mlt_profile profile, mlt_field field, char *id, int track )
+{
+       char *temp = strdup( id );
+       char *arg = strchr( temp, ':' );
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_filter filter = mlt_factory_filter( profile, temp, arg );
+       if ( filter != NULL )
+               track_service( field, filter, ( mlt_destructor )mlt_filter_close );
+       free( temp );
+       return filter;
+}
+
+static mlt_filter create_filter( mlt_profile profile, mlt_field field, char *id, int track )
+{
+       char *temp = strdup( id );
+       char *arg = strchr( temp, ':' );
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_filter filter = mlt_factory_filter( profile, temp, arg );
+       if ( filter != NULL )
+       {
+               mlt_field_plant_filter( field, filter, track );
+               track_service( field, filter, ( mlt_destructor )mlt_filter_close );
+       }
+       free( temp );
+       return filter;
+}
+
+static mlt_transition create_transition( mlt_profile profile, mlt_field field, char *id, int track )
+{
+       char *arg = strchr( id, ':' );
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_transition transition = mlt_factory_transition( profile, id, arg );
+       if ( transition != NULL )
+       {
+               mlt_field_plant_transition( field, transition, track, track + 1 );
+               track_service( field, transition, ( mlt_destructor )mlt_transition_close );
+       }
+       return transition;
+}
+
+mlt_producer producer_inigo_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv )
+{
+       int i;
+       int track = 0;
+       mlt_producer producer = NULL;
+       mlt_tractor mix = NULL;
+       mlt_playlist playlist = mlt_playlist_init( );
+       mlt_properties group = mlt_properties_new( );
+       mlt_tractor tractor = mlt_tractor_new( );
+       mlt_properties properties = MLT_TRACTOR_PROPERTIES( tractor );
+       mlt_field field = mlt_tractor_field( tractor );
+       mlt_properties field_properties = mlt_field_properties( field );
+       mlt_multitrack multitrack = mlt_tractor_multitrack( tractor );
+       char *title = NULL;
+
+       // Assistance for template construction (allows -track usage to specify the first track)
+       mlt_properties_set_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first", 1 );
+
+       // We need to track the number of registered filters
+       mlt_properties_set_int( field_properties, "registered", 0 );
+
+       // Parse the arguments
+       if ( argv )
+       for ( i = 0; argv[ i ] != NULL; i ++ )
+       {
+               if ( !strcmp( argv[ i ], "-group" ) )
+               {
+                       if ( mlt_properties_count( group ) != 0 )
+                       {
+                               mlt_properties_close( group );
+                               group = mlt_properties_new( );
+                       }
+                       if ( group != NULL )
+                               properties = group;
+               }
+               else if ( !strcmp( argv[ i ], "-attach" ) || 
+                                 !strcmp( argv[ i ], "-attach-cut" ) ||
+                                 !strcmp( argv[ i ], "-attach-track" ) ||
+                                 !strcmp( argv[ i ], "-attach-clip" ) )
+               {
+                       int type = !strcmp( argv[ i ], "-attach" ) ? 0 : 
+                                          !strcmp( argv[ i ], "-attach-cut" ) ? 1 : 
+                                          !strcmp( argv[ i ], "-attach-track" ) ? 2 : 3;
+                       mlt_filter filter = create_attach( profile, field, argv[ ++ i ], track );
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_append( playlist, producer );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                       }
+
+                       if ( type == 1 || type == 2 )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                       }
+
+                       if ( filter != NULL && mlt_playlist_count( playlist ) > 0 )
+                       {
+                               if ( type == 0 )
+                                       mlt_service_attach( ( mlt_service )properties, filter );
+                               else if ( type == 1 )
+                                       mlt_service_attach( ( mlt_service )producer, filter );
+                               else if ( type == 2 )
+                                       mlt_service_attach( ( mlt_service )playlist, filter );
+                               else if ( type == 3 )
+                                       mlt_service_attach( ( mlt_service )mlt_producer_cut_parent( producer ), filter );
+
+                               properties = MLT_FILTER_PROPERTIES( filter );
+                               mlt_properties_inherit( properties, group );
+                       }
+                       else if ( filter != NULL )
+                       {
+                               mlt_service_attach( ( mlt_service )playlist, filter );
+                               properties = MLT_FILTER_PROPERTIES( filter );
+                               mlt_properties_inherit( properties, group );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-repeat" ) )
+               {
+                       int repeat = atoi( argv[ ++ i ] );
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) > 0 )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_repeat_clip( playlist, mlt_playlist_count( playlist ) - 1, repeat );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-split" ) )
+               {
+                       int split = atoi( argv[ ++ i ] );
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) > 0 )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               split = split < 0 ? info.frame_out + split : split;
+                               mlt_playlist_split( playlist, mlt_playlist_count( playlist ) - 1, split );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-swap" ) )
+               {
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) >= 2 )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_move( playlist, mlt_playlist_count( playlist ) - 2, mlt_playlist_count( playlist ) - 1 );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-join" ) )
+               {
+                       int clips = atoi( argv[ ++ i ] );
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) > 0 )
+                       {
+                               mlt_playlist_clip_info info;
+                               int clip = clips <= 0 ? 0 : mlt_playlist_count( playlist ) - clips - 1;
+                               if ( clip < 0 ) clip = 0;
+                               if ( clip >= mlt_playlist_count( playlist ) ) clip = mlt_playlist_count( playlist ) - 2;
+                               if ( clips < 0 ) clips =  mlt_playlist_count( playlist ) - 1;
+                               mlt_playlist_join( playlist, clip, clips, 0 );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-remove" ) )
+               {
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) > 0 )
+                       {
+                               mlt_playlist_clip_info info;
+                               mlt_playlist_remove( playlist, mlt_playlist_count( playlist ) - 1 );
+                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                               producer = info.cut;
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-mix" ) )
+               {
+                       int length = atoi( argv[ ++ i ] );
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( mlt_playlist_count( playlist ) >= 2 )
+                       {
+                               if ( mlt_playlist_mix( playlist, mlt_playlist_count( playlist ) - 2, length, NULL ) == 0 )
+                               {
+                                       mlt_playlist_clip_info info;
+                                       mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+                                       if ( mlt_properties_get_data( ( mlt_properties )info.producer, "mlt_mix", NULL ) == NULL )
+                                               mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 2 );
+                                       mix = ( mlt_tractor )mlt_properties_get_data( ( mlt_properties )info.producer, "mlt_mix", NULL );
+                                       properties = NULL;
+                               }
+                               else
+                               {
+                                       fprintf( stderr, "Mix failed?\n" );
+                               }
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Invalid position for a mix...\n" );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-mixer" ) )
+               {
+                       if ( mix != NULL )
+                       {
+                               char *id = strdup( argv[ ++ i ] );
+                               char *arg = strchr( id, ':' );
+                               mlt_field field = mlt_tractor_field( mix );
+                               mlt_transition transition = NULL;
+                               if ( arg != NULL )
+                                       *arg ++ = '\0';
+                               transition = mlt_factory_transition( profile, id, arg );
+                               if ( transition != NULL )
+                               {
+                                       properties = MLT_TRANSITION_PROPERTIES( transition );
+                                       mlt_properties_inherit( properties, group );
+                                       mlt_field_plant_transition( field, transition, 0, 1 );
+                                       mlt_properties_set_position( properties, "in", 0 );
+                                       mlt_properties_set_position( properties, "out", mlt_producer_get_out( ( mlt_producer )mix ) );
+                                       mlt_transition_close( transition );
+                               }
+                               free( id );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Invalid mixer...\n" );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-filter" ) )
+               {
+                       mlt_filter filter = create_filter( profile, field, argv[ ++ i ], track );
+                       if ( filter != NULL )
+                       {
+                               properties = MLT_FILTER_PROPERTIES( filter );
+                               mlt_properties_inherit( properties, group );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-transition" ) )
+               {
+                       mlt_transition transition = create_transition( profile, field, argv[ ++ i ], track - 1 );
+                       if ( transition != NULL )
+                       {
+                               properties = MLT_TRANSITION_PROPERTIES( transition );
+                               mlt_properties_inherit( properties, group );
+                       }
+               }
+               else if ( !strcmp( argv[ i ], "-blank" ) )
+               {
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       mlt_playlist_blank( playlist, atof( argv[ ++ i ] ) );
+               }
+               else if ( !strcmp( argv[ i ], "-track" ) ||
+                                 !strcmp( argv[ i ], "-null-track" ) ||
+                                 !strcmp( argv[ i ], "-video-track" ) ||
+                                 !strcmp( argv[ i ], "-audio-track" ) ||
+                                 !strcmp( argv[ i ], "-hide-track" ) ||
+                                 !strcmp( argv[ i ], "-hide-video" ) ||
+                                 !strcmp( argv[ i ], "-hide-audio" ) )
+               {
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       producer = NULL;
+                       if ( !mlt_properties_get_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first" ) || 
+                                 mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) ) > 0 )
+                       {
+                               mlt_multitrack_connect( multitrack, MLT_PLAYLIST_PRODUCER( playlist ), track ++ );
+                               track_service( field, playlist, ( mlt_destructor )mlt_playlist_close );
+                               playlist = mlt_playlist_init( );
+                       }
+                       if ( playlist != NULL )
+                       {
+                               properties = MLT_PLAYLIST_PROPERTIES( playlist );
+                               if ( !strcmp( argv[ i ], "-null-track" ) || !strcmp( argv[ i ], "-hide-track" ) )
+                                       mlt_properties_set_int( properties, "hide", 3 );
+                               else if ( !strcmp( argv[ i ], "-audio-track" ) || !strcmp( argv[ i ], "-hide-video" ) )
+                                       mlt_properties_set_int( properties, "hide", 1 );
+                               else if ( !strcmp( argv[ i ], "-video-track" ) || !strcmp( argv[ i ], "-hide-audio" ) )
+                                       mlt_properties_set_int( properties, "hide", 2 );
+                       }
+               }
+               else if ( strchr( argv[ i ], '=' ) && strstr( argv[ i ], "<?xml" ) != argv[ i ] )
+               {
+                       mlt_properties_parse( properties, argv[ i ] );
+               }
+               else if ( argv[ i ][ 0 ] != '-' )
+               {
+                       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+                               mlt_playlist_append( playlist, producer );
+                       if ( title == NULL && strstr( argv[ i ], "<?xml" ) != argv[ i ] )
+                               title = argv[ i ];
+                       producer = create_producer( profile, field, argv[ i ] );
+                       if ( producer != NULL )
+                       {
+                               properties = MLT_PRODUCER_PROPERTIES( producer );
+                               mlt_properties_inherit( properties, group );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Failed to load \"%s\"\n", argv[ i ] );
+                       }
+               }
+               else
+               {
+                       int backtrack = 0;
+                       if ( !strcmp( argv[ i ], "-serialise" ) ||
+                            !strcmp( argv[ i ], "-consumer" ) ||
+                            !strcmp( argv[ i ], "-profile" ) )
+                       {
+                               i += 2;
+                               backtrack = 1;
+                       }
+
+                       while ( argv[ i ] != NULL && strchr( argv[ i ], '=' ) )
+                       {
+                               i ++;
+                               backtrack = 1;
+                       }
+                       if ( backtrack )
+                               i --;
+               }
+       }
+
+       // Connect last producer to playlist
+       if ( producer != NULL && !mlt_producer_is_cut( producer ) )
+               mlt_playlist_append( playlist, producer );
+
+       // Track the last playlist too
+       track_service( field, playlist, ( mlt_destructor )mlt_playlist_close );
+
+       // We must have a playlist to connect
+       if ( !mlt_properties_get_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_inigo_first" ) || 
+                 mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) ) > 0 )
+               mlt_multitrack_connect( multitrack, MLT_PLAYLIST_PRODUCER( playlist ), track );
+
+       mlt_producer prod = MLT_TRACTOR_PRODUCER( tractor );
+       mlt_producer_optimise( prod );
+       mlt_properties props = MLT_TRACTOR_PROPERTIES( tractor );
+       mlt_properties_set_data( props, "group", group, 0, ( mlt_destructor )mlt_properties_close, NULL );
+       mlt_properties_set_position( props, "length", mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) + 1 );
+       mlt_producer_set_in_and_out( prod, 0, mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) );
+       if ( title != NULL )
+               mlt_properties_set( props, "title", strchr( title, '/' ) ? strrchr( title, '/' ) + 1 : title );
+
+       return prod;
+}
diff --git a/src/modules/jackrack/Makefile b/src/modules/jackrack/Makefile
new file mode 100644 (file)
index 0000000..50d7d88
--- /dev/null
@@ -0,0 +1,47 @@
+include ../../../config.mak
+
+TARGET = ../libmltjackrack$(LIBSUF)
+
+OBJS = factory.o \
+          jack_rack.o \
+          lock_free_fifo.o \
+          plugin.o \
+          plugin_desc.o \
+          plugin_mgr.o \
+          plugin_settings.o \
+          process.o \
+          filter_jackrack.o \
+          filter_ladspa.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags jack`
+CFLAGS += `xml2-config --cflags`
+CFLAGS += `pkg-config glib-2.0 --cflags` 
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs jack`
+LDFLAGS += `xml2-config --libs`
+LDFLAGS += `pkg-config glib-2.0 --libs`
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/jackrack/configure b/src/modules/jackrack/configure
new file mode 100755 (executable)
index 0000000..d10b294
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       pkg-config jack
+       disable_jack=$?
+
+       which xml2-config > /dev/null 2>&1
+       disable_xml2=$?
+
+       disable_ladspa=1
+       ladspa_prefix=`which listplugins 2> /dev/null`
+       if [ "$ladspa_prefix" != "" ]
+       then
+               ladspa_prefix=`dirname "$ladspa_prefix"`
+               disable_ladspa=`[ -f "$ladspa_prefix/include/ladspa.h" ] && echo 1 || echo 0`
+       fi
+
+       if [ "$disable_jack" = "1" -o "$disable_xml2" = "1" -o "$disable_ladspa" = "1" ]
+       then
+               [ "$disable_jack" = "1" ] && echo "- jackrack not found: disabling"
+               [ "$disable_xml2" = "1" ] && echo "- xml2 not found: disabling jackrack"
+               [ "$disable_ladspa" = "1" ] && echo "- ladspa not found; disabling"
+               touch ../disable-jackrack
+       fi
+       exit 0
+fi
diff --git a/src/modules/jackrack/factory.c b/src/modules/jackrack/factory.c
new file mode 100644 (file)
index 0000000..1476b15
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_jackrack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "jackrack", filter_jackrack_init );
+       MLT_REGISTER( filter_type, "ladspa", filter_ladspa_init );
+}
diff --git a/src/modules/jackrack/filter_jackrack.c b/src/modules/jackrack/filter_jackrack.c
new file mode 100644 (file)
index 0000000..0c9fe9b
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * filter_jackrack.c -- filter audio through Jack and/or LADSPA plugins
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "jack_rack.h"
+
+#define BUFFER_LEN 204800 * 3
+
+static void initialise_jack_ports( mlt_properties properties )
+{
+       int i;
+       char mlt_name[20], rack_name[30];
+       jack_port_t **port = NULL;
+       jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL );
+       jack_nframes_t jack_buffer_size = jack_get_buffer_size( jack_client );
+       
+       // Propogate these for the Jack processing callback
+       int channels = mlt_properties_get_int( properties, "channels" );
+
+       // Start JackRack
+       if ( mlt_properties_get( properties, "src" ) )
+       {
+               snprintf( rack_name, sizeof( rack_name ), "jackrack%d", getpid() );
+               jack_rack_t *jackrack = jack_rack_new( rack_name, mlt_properties_get_int( properties, "channels" ) );
+               jack_rack_open_file( jackrack, mlt_properties_get( properties, "src" ) );               
+               
+               mlt_properties_set_data( properties, "jackrack", jackrack, 0, NULL, NULL );
+               mlt_properties_set( properties, "_rack_client_name", rack_name );
+       }
+               
+       // Allocate buffers and ports
+       jack_ringbuffer_t **output_buffers = mlt_pool_alloc( sizeof( jack_ringbuffer_t *) * channels );
+       jack_ringbuffer_t **input_buffers = mlt_pool_alloc( sizeof( jack_ringbuffer_t *) * channels );
+       jack_port_t **jack_output_ports = mlt_pool_alloc( sizeof(jack_port_t *) * channels );
+       jack_port_t **jack_input_ports = mlt_pool_alloc( sizeof(jack_port_t *) * channels );
+       float **jack_output_buffers = mlt_pool_alloc( sizeof(float *) * jack_buffer_size );
+       float **jack_input_buffers = mlt_pool_alloc( sizeof(float *) * jack_buffer_size );
+
+       // Set properties - released inside filter_close
+       mlt_properties_set_data( properties, "output_buffers", output_buffers, sizeof( jack_ringbuffer_t *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "input_buffers", input_buffers, sizeof( jack_ringbuffer_t *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "jack_output_ports", jack_output_ports, sizeof( jack_port_t *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "jack_input_ports", jack_input_ports, sizeof( jack_port_t *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "jack_output_buffers", jack_output_buffers, sizeof( float *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "jack_input_buffers", jack_input_buffers, sizeof( float *) * channels, NULL, NULL );
+
+       // Start Jack processing - required before registering ports
+       jack_activate( jack_client );
+       
+       // Register Jack ports
+       for ( i = 0; i < channels; i++ )
+       {
+               int in;
+               
+               output_buffers[i] = jack_ringbuffer_create( BUFFER_LEN * sizeof(float) );
+               input_buffers[i] = jack_ringbuffer_create( BUFFER_LEN * sizeof(float) );
+               snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+               mlt_properties_set_data( properties, mlt_name, output_buffers[i], BUFFER_LEN * sizeof(float), NULL, NULL );
+               snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+               mlt_properties_set_data( properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof(float), NULL, NULL );
+               
+               for ( in = 0; in < 2; in++ )
+               {
+                       snprintf( mlt_name, sizeof( mlt_name ), "%s_%d", in ? "in" : "out", i + 1);
+                       port = ( in ? &jack_input_ports[i] : &jack_output_ports[i] );
+                       
+                       *port =  jack_port_register( jack_client, mlt_name, JACK_DEFAULT_AUDIO_TYPE,
+                               ( in ? JackPortIsInput : JackPortIsOutput ) | JackPortIsTerminal, 0 );
+               }
+       }
+       
+       // Establish connections
+       for ( i = 0; i < channels; i++ )
+       {
+               int in;
+               for ( in = 0; in < 2; in++ )
+               {
+                       port = ( in ? &jack_input_ports[i] : &jack_output_ports[i] );
+                       snprintf( mlt_name, sizeof( mlt_name ), "%s", jack_port_name( *port ) );
+
+                       snprintf( rack_name, sizeof( rack_name ), "%s_%d", in ? "in" : "out", i + 1 );
+                       if ( mlt_properties_get( properties, "_rack_client_name" ) )
+                               snprintf( rack_name, sizeof( rack_name ), "%s:%s_%d", mlt_properties_get( properties, "_rack_client_name" ), in ? "out" : "in", i + 1);
+                       else if ( mlt_properties_get( properties, rack_name ) )
+                               snprintf( rack_name, sizeof( rack_name ), "%s", mlt_properties_get( properties, rack_name ) );
+                       else
+                               snprintf( rack_name, sizeof( rack_name ), "%s:%s_%d", mlt_properties_get( properties, "_client_name" ), in ? "out" : "in", i + 1);
+                       
+                       if ( in )
+                       {
+                               fprintf( stderr, "jack connect %s to %s\n", rack_name, mlt_name );
+                               jack_connect( jack_client, rack_name, mlt_name );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "jack connect %s to %s\n", mlt_name, rack_name );
+                               jack_connect( jack_client, mlt_name, rack_name );
+                       }
+               }
+       }
+}
+
+static int jack_process (jack_nframes_t frames, void * data)
+{
+       mlt_filter filter = (mlt_filter) data;
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       int channels = mlt_properties_get_int( properties, "channels" );
+       int frame_size = mlt_properties_get_int( properties, "_samples" ) * sizeof(float);
+       int sync = mlt_properties_get_int( properties, "_sync" );
+       int err = 0;
+       int i;
+       static int total_size = 0;
+  
+       jack_ringbuffer_t **output_buffers = mlt_properties_get_data( properties, "output_buffers", NULL );
+       if ( output_buffers == NULL )
+               return 0;
+       jack_ringbuffer_t **input_buffers = mlt_properties_get_data( properties, "input_buffers", NULL );
+       jack_port_t **jack_output_ports = mlt_properties_get_data( properties, "jack_output_ports", NULL );
+       jack_port_t **jack_input_ports = mlt_properties_get_data( properties, "jack_input_ports", NULL );
+       float **jack_output_buffers = mlt_properties_get_data( properties, "jack_output_buffers", NULL );
+       float **jack_input_buffers = mlt_properties_get_data( properties, "jack_input_buffers", NULL );
+       pthread_mutex_t *output_lock = mlt_properties_get_data( properties, "output_lock", NULL );
+       pthread_cond_t *output_ready = mlt_properties_get_data( properties, "output_ready", NULL );
+       
+       for ( i = 0; i < channels; i++ )
+       {
+               size_t jack_size = ( frames * sizeof(float) );
+               size_t ring_size;
+               
+               // Send audio through out port
+               jack_output_buffers[i] = jack_port_get_buffer( jack_output_ports[i], frames );
+               if ( ! jack_output_buffers[i] )
+               {
+                       fprintf( stderr, "%s: no jack buffer for output port %d\n", __FUNCTION__, i );
+                       err = 1;
+                       break;
+               }
+               ring_size = jack_ringbuffer_read_space( output_buffers[i] );
+               jack_ringbuffer_read( output_buffers[i], ( char * )jack_output_buffers[i], ring_size < jack_size ? ring_size : jack_size );
+               
+               // Return audio through in port
+               jack_input_buffers[i] = jack_port_get_buffer( jack_input_ports[i], frames );
+               if ( ! jack_input_buffers[i] )
+               {
+                       fprintf( stderr, "%s: no jack buffer for input port %d\n", __FUNCTION__, i );
+                       err = 1;
+                       break;
+               }
+               
+               // Do not start returning audio until we have sent first mlt frame
+               if ( sync && i == 0 && frame_size > 0 )
+                       total_size += ring_size;
+               //fprintf(stderr, "sync %d frame_size %d ring_size %d jack_size %d\n", sync, frame_size, ring_size, jack_size );
+               
+               if ( ! sync || ( frame_size > 0  && total_size >= frame_size ) )
+               {
+                       ring_size = jack_ringbuffer_write_space( input_buffers[i] );
+                       jack_ringbuffer_write( input_buffers[i], ( char * )jack_input_buffers[i], ring_size < jack_size ? ring_size : jack_size );
+                       
+                       if ( sync )
+                       {
+                               // Tell mlt that audio is available
+                               pthread_mutex_lock( output_lock);
+                               pthread_cond_signal( output_ready );
+                               pthread_mutex_unlock( output_lock);
+
+                               // Clear sync phase
+                               mlt_properties_set_int( properties, "_sync", 0 );
+                       }
+               }
+       }
+
+       return err;
+}
+
+
+/** Get the audio.
+*/
+
+static int jackrack_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_audio( frame );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       int jack_frequency = mlt_properties_get_int( filter_properties, "_sample_rate" );
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, &jack_frequency, channels, samples );
+       
+       // TODO: Deal with sample rate differences
+       if ( *frequency != jack_frequency )
+               fprintf( stderr, "mismatching frequencies in filter jackrack\n" );
+       *frequency = jack_frequency;
+
+       // Initialise Jack ports and connections if needed
+       if ( mlt_properties_get_int( filter_properties, "_samples" ) == 0 )
+               mlt_properties_set_int( filter_properties, "_samples", *samples );
+       
+       // Get the filter-specific properties
+       jack_ringbuffer_t **output_buffers = mlt_properties_get_data( filter_properties, "output_buffers", NULL );
+       jack_ringbuffer_t **input_buffers = mlt_properties_get_data( filter_properties, "input_buffers", NULL );
+//     pthread_mutex_t *output_lock = mlt_properties_get_data( filter_properties, "output_lock", NULL );
+//     pthread_cond_t *output_ready = mlt_properties_get_data( filter_properties, "output_ready", NULL );
+       
+       // Process the audio
+       int16_t *q = *buffer;
+       float sample[ 2 ][ 10000 ];
+       int i, j;
+//     struct timespec tm = { 0, 0 };
+
+       // Convert to floats and write into output ringbuffer
+       if ( jack_ringbuffer_write_space( output_buffers[0] ) >= ( *samples * sizeof(float) ) )
+       {
+               for ( i = 0; i < *samples; i++ )
+                       for ( j = 0; j < *channels; j++ )
+                               sample[ j ][ i ] = ( float )( *q ++ ) / 32768.0;
+
+               for ( j = 0; j < *channels; j++ )
+                       jack_ringbuffer_write( output_buffers[j], ( char * )sample[ j ], *samples * sizeof(float) );
+       }
+
+       // Synchronization phase - wait for signal from Jack process
+       while ( jack_ringbuffer_read_space( input_buffers[ *channels - 1 ] ) < ( *samples * sizeof(float) ) ) ;
+               //pthread_cond_wait( output_ready, output_lock );
+               
+       // Read from input ringbuffer and convert from floats
+       if ( jack_ringbuffer_read_space( input_buffers[0] ) >= ( *samples * sizeof(float) ) )
+       {
+               // Initialise to silence, but repeat last frame if available in case of 
+               // buffer underrun
+               for ( j = 0; j < *channels; j++ )
+                       jack_ringbuffer_read( input_buffers[j], ( char * )sample[ j ], *samples * sizeof(float) );
+
+               q = *buffer;
+               for ( i = 0; i < *samples; i++ )
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               if ( sample[ j ][ i ] > 1.0 )
+                                       sample[ j ][ i ] = 1.0;
+                               else if ( sample[ j ][ i ] < -1.0 )
+                                       sample[ j ][ i ] = -1.0;
+                       
+                               if ( sample[ j ][ i ] > 0 )
+                                       *q ++ = 32767 * sample[ j ][ i ];
+                               else
+                                       *q ++ = 32768 * sample[ j ][ i ];
+                       }
+       }
+
+       return 0;
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               mlt_frame_push_audio( frame, this );
+               mlt_frame_push_audio( frame, jackrack_get_audio );
+               
+               if ( mlt_properties_get_int( properties, "_sync" ) )
+                       initialise_jack_ports( properties );
+       }
+
+       return frame;
+}
+
+
+static void filter_close( mlt_filter this )
+{
+       int i;
+       char mlt_name[20];
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+       jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL );
+       
+       jack_deactivate( jack_client );
+       jack_client_close( jack_client );
+       for ( i = 0; i < mlt_properties_get_int( properties, "channels" ); i++ )
+       {
+               snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+               jack_ringbuffer_free( mlt_properties_get_data( properties, mlt_name, NULL ) );
+               snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+               jack_ringbuffer_free( mlt_properties_get_data( properties, mlt_name, NULL ) );
+       }
+       mlt_pool_release( mlt_properties_get_data( properties, "output_buffers", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "input_buffers", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "jack_output_ports", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "jack_input_ports", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "jack_output_buffers", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "jack_input_buffers", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "output_lock", NULL ) );
+       mlt_pool_release( mlt_properties_get_data( properties, "output_ready", NULL ) );
+
+       jack_rack_t *jackrack = mlt_properties_get_data( properties, "jackrack", NULL );
+       jack_rack_destroy( jackrack );
+       
+       this->parent.close = NULL;
+       mlt_service_close( &this->parent );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_jackrack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               char name[14];
+               
+               snprintf( name, sizeof( name ), "mlt%d", getpid() );
+               jack_client_t *jack_client = jack_client_new( name );
+               if ( jack_client )
+               {
+                       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+                       pthread_mutex_t *output_lock = mlt_pool_alloc( sizeof( pthread_mutex_t ) );
+                       pthread_cond_t  *output_ready = mlt_pool_alloc( sizeof( pthread_cond_t ) );
+                       
+                       jack_set_process_callback( jack_client, jack_process, this );
+                       //TODO: jack_on_shutdown( jack_client, jack_shutdown_cb, this );
+                       this->process = filter_process;
+                       this->close = filter_close;
+                       pthread_mutex_init( output_lock, NULL );
+                       pthread_cond_init( output_ready, NULL );
+                       
+                       mlt_properties_set( properties, "src", arg );
+                       mlt_properties_set( properties, "_client_name", name );
+                       mlt_properties_set_data( properties, "jack_client", jack_client, 0, NULL, NULL );
+                       mlt_properties_set_int( properties, "_sample_rate", jack_get_sample_rate( jack_client ) );
+                       mlt_properties_set_data( properties, "output_lock", output_lock, 0, NULL, NULL );
+                       mlt_properties_set_data( properties, "output_ready", output_ready, 0, NULL, NULL );
+                       mlt_properties_set_int( properties, "_sync", 1 );
+                       mlt_properties_set_int( properties, "channels", 2 );
+               }
+       }
+       return this;
+}
diff --git a/src/modules/jackrack/filter_ladspa.c b/src/modules/jackrack/filter_ladspa.c
new file mode 100644 (file)
index 0000000..7eff6fa
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * filter_ladspa.c -- filter audio through LADSPA plugins
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <string.h>
+
+#include "jack_rack.h"
+
+#define BUFFER_LEN 10000
+
+static void initialise_jack_rack( mlt_properties properties, int channels )
+{
+       int i;
+       char mlt_name[20];
+       
+       // Propogate these for the Jack processing callback
+       mlt_properties_set_int( properties, "channels", channels );
+
+       // Start JackRack
+       if ( mlt_properties_get( properties, "src" ) )
+       {
+               // Create JackRack without Jack client name so that it only uses LADSPA
+               jack_rack_t *jackrack = jack_rack_new( NULL, channels );
+               mlt_properties_set_data( properties, "jackrack", jackrack, 0, NULL, NULL );
+               jack_rack_open_file( jackrack, mlt_properties_get( properties, "src" ) );
+       }
+               
+       // Allocate buffers
+       LADSPA_Data **input_buffers = mlt_pool_alloc( sizeof( LADSPA_Data ) * channels );
+       LADSPA_Data **output_buffers = mlt_pool_alloc( sizeof( LADSPA_Data ) * channels );
+
+       // Set properties - released inside filter_close
+       mlt_properties_set_data( properties, "input_buffers", input_buffers, sizeof( LADSPA_Data *) * channels, NULL, NULL );
+       mlt_properties_set_data( properties, "output_buffers", output_buffers, sizeof( LADSPA_Data *) * channels, NULL, NULL );
+
+       // Register Jack ports
+       for ( i = 0; i < channels; i++ )
+       {
+               input_buffers[i] = mlt_pool_alloc( BUFFER_LEN * sizeof( LADSPA_Data ) );
+               snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+               mlt_properties_set_data( properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof( LADSPA_Data ), NULL, NULL );
+
+               output_buffers[i] = mlt_pool_alloc( BUFFER_LEN * sizeof( LADSPA_Data ) );
+               snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+               mlt_properties_set_data( properties, mlt_name, output_buffers[i], BUFFER_LEN * sizeof( LADSPA_Data ), NULL, NULL );
+       }
+}
+
+
+/** Get the audio.
+*/
+
+static int ladspa_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_audio( frame );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+       
+       // Initialise LADSPA if needed
+       jack_rack_t *jackrack = mlt_properties_get_data( filter_properties, "jackrack", NULL );
+       if ( jackrack == NULL )
+       {
+               sample_rate = *frequency;
+               initialise_jack_rack( filter_properties, *channels );
+               jackrack = mlt_properties_get_data( filter_properties, "jackrack", NULL );
+       }
+               
+       // Get the filter-specific properties
+       LADSPA_Data **input_buffers = mlt_properties_get_data( filter_properties, "input_buffers", NULL );
+       LADSPA_Data **output_buffers = mlt_properties_get_data( filter_properties, "output_buffers", NULL );
+       
+       // Process the audio
+       int16_t *q = *buffer;
+       int i, j;
+
+       // Convert to floats and write into output ringbuffer
+       for ( i = 0; i < *samples; i++ )
+               for ( j = 0; j < *channels; j++ )
+                       input_buffers[ j ][ i ] = ( float )( *q ++ ) / 32768.0;
+
+       // Do LADSPA processing
+       if ( jackrack && process_ladspa( jackrack->procinfo, *samples, input_buffers, output_buffers) == 0 )
+       {
+               // Read from output buffer and convert from floats
+               q = *buffer;
+               for ( i = 0; i < *samples; i++ )
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               if ( output_buffers[ j ][ i ] > 1.0 )
+                                       output_buffers[ j ][ i ] = 1.0;
+                               else if ( output_buffers[ j ][ i ] < -1.0 )
+                                       output_buffers[ j ][ i ] = -1.0;
+                       
+                               if ( output_buffers[ j ][ i ] > 0 )
+                                       *q ++ = 32767 * output_buffers[ j ][ i ];
+                               else
+                                       *q ++ = 32768 * output_buffers[ j ][ i ];
+                       }
+       }
+
+       return 0;
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       {
+               mlt_frame_push_audio( frame, this );
+               mlt_frame_push_audio( frame, ladspa_get_audio );
+       }
+
+       return frame;
+}
+
+
+static void filter_close( mlt_filter this )
+{
+       int i;
+       char mlt_name[20];
+       mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+       
+       if ( mlt_properties_get_data( properties, "jackrack", NULL ) != NULL )
+       {
+               for ( i = 0; i < mlt_properties_get_int( properties, "channels" ); i++ )
+               {
+                       snprintf( mlt_name, sizeof( mlt_name ), "obuf%d", i );
+                       mlt_pool_release( mlt_properties_get_data( properties, mlt_name, NULL ) );
+                       snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i );
+                       mlt_pool_release( mlt_properties_get_data( properties, mlt_name, NULL ) );
+               }
+               mlt_pool_release( mlt_properties_get_data( properties, "output_buffers", NULL ) );
+               mlt_pool_release( mlt_properties_get_data( properties, "input_buffers", NULL ) );
+       
+               jack_rack_t *jackrack = mlt_properties_get_data( properties, "jackrack", NULL );
+               jack_rack_destroy( jackrack );
+       }       
+       this->parent.close = NULL;
+       mlt_service_close( &this->parent );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               
+               this->process = filter_process;
+               this->close = filter_close;
+               
+               mlt_properties_set( properties, "src", arg );
+       }
+       return this;
+}
diff --git a/src/modules/jackrack/gpl b/src/modules/jackrack/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/jackrack/jack_rack.c b/src/modules/jackrack/jack_rack.c
new file mode 100644 (file)
index 0000000..92f8272
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ladspa.h>
+#include <libxml/tree.h>
+
+#include "jack_rack.h"
+#include "lock_free_fifo.h"
+#include "plugin_settings.h"
+
+#ifndef _
+#define _(x) x
+#endif
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+jack_rack_t *
+jack_rack_new (const char * client_name, unsigned long channels)
+{
+  jack_rack_t *rack;
+
+  rack = g_malloc (sizeof (jack_rack_t));
+  rack->saved_plugins  = NULL;
+  rack->channels       = channels;
+  rack->procinfo = process_info_new (client_name, channels, FALSE, FALSE);
+  if (!rack->procinfo) {
+    g_free (rack);
+    return NULL;
+  }
+  rack->plugin_mgr = plugin_mgr_new ();
+  plugin_mgr_set_plugins (rack->plugin_mgr, channels);
+
+  return rack;
+}
+
+
+void
+jack_rack_destroy (jack_rack_t * jack_rack)
+{
+  process_quit (jack_rack->procinfo);
+  plugin_mgr_destroy (jack_rack->plugin_mgr);
+  process_info_destroy (jack_rack->procinfo);
+  g_slist_free (jack_rack->saved_plugins);
+  g_free (jack_rack);
+}
+
+plugin_t *
+jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc)
+{
+  plugin_t * plugin;
+  
+  /* check whether or not the plugin is RT capable and confirm with the user if it isn't */
+  if (!LADSPA_IS_HARD_RT_CAPABLE(desc->properties)) {
+    fprintf (stderr, "Plugin not RT capable. The plugin '%s' does not describe itself as being capable of real-time operation. You may experience drop outs or jack may even kick us out if you use it.\n",
+               desc->name);
+  }
+
+  /* create the plugin */
+  plugin = plugin_new (desc, jack_rack);
+
+  if (!plugin) {
+   fprintf (stderr, "Error loading file plugin '%s' from file '%s'\n",
+               desc->name, desc->object_file);
+  }
+  
+  return plugin;
+}
+
+
+void
+jack_rack_add_saved_plugin (jack_rack_t * jack_rack, saved_plugin_t * saved_plugin)
+{
+  plugin_t * plugin = jack_rack_instantiate_plugin (jack_rack, saved_plugin->settings->desc);
+  if (!plugin)
+    return;
+  jack_rack->saved_plugins = g_slist_append (jack_rack->saved_plugins, saved_plugin);
+  process_add_plugin (jack_rack->procinfo, plugin);
+  jack_rack_add_plugin (jack_rack, plugin);
+}
+
+
+void
+jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin)
+{
+  saved_plugin_t * saved_plugin = NULL;
+  GSList * list;
+  unsigned long control, channel;
+  LADSPA_Data value;
+  guint copy;
+  
+  /* see if there's any saved settings that match the plugin id */
+  for (list = jack_rack->saved_plugins; list; list = g_slist_next (list))
+    {
+      saved_plugin = list->data;
+      
+      if (saved_plugin->settings->desc->id == plugin->desc->id)
+        {
+          /* process the settings! */
+          jack_rack->saved_plugins = g_slist_remove (jack_rack->saved_plugins, saved_plugin);
+          break;
+        }
+      saved_plugin = NULL;
+    }
+       
+  /* initialize plugin parameters */
+  plugin->enabled = settings_get_enabled (saved_plugin->settings);
+  plugin->wet_dry_enabled = settings_get_wet_dry_enabled (saved_plugin->settings);
+       
+  for (control = 0; control < saved_plugin->settings->desc->control_port_count; control++)
+    for (copy = 0; copy < plugin->copies; copy++)
+      {
+        value = settings_get_control_value (saved_plugin->settings, copy, control);
+        plugin->holders[copy].control_memory[control] = value;
+//printf("setting control value %s (%d) = %f\n", saved_plugin->settings->desc->port_names[control], copy, value);
+//        lff_write (plugin->holders[copy].ui_control_fifos + control, &value);
+      }
+  if (plugin->wet_dry_enabled)
+    for (channel = 0; channel < jack_rack->channels; channel++)
+      {
+        value = settings_get_wet_dry_value (saved_plugin->settings, channel);
+        plugin->wet_dry_values[channel] = value;
+//printf("setting wet/dry value %d = %f\n", channel, value);
+//        lff_write (plugin->wet_dry_fifos + channel, &value);
+      }
+}
+
+
+static void
+saved_rack_parse_plugin (jack_rack_t * jack_rack, saved_rack_t * saved_rack, saved_plugin_t * saved_plugin,
+                         const char * filename, xmlNodePtr plugin)
+{
+  plugin_desc_t * desc;
+  settings_t * settings = NULL;
+  xmlNodePtr node;
+  xmlNodePtr sub_node;
+  xmlChar *content;
+  unsigned long num;
+  unsigned long control = 0;
+
+  for (node = plugin->children; node; node = node->next)
+    {
+      if (xmlStrcmp (node->name, _x("id")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          num = strtoul (_s(content), NULL, 10);
+          xmlFree (content);
+
+          desc = plugin_mgr_get_any_desc (jack_rack->plugin_mgr, num);
+          if (!desc)
+            {
+              fprintf (stderr, _("The file '%s' contains an unknown plugin with ID '%ld'; skipping\n"), filename, num);
+              return;
+            }
+          
+          settings = settings_new (desc, saved_rack->channels, saved_rack->sample_rate);
+        }
+      else if (xmlStrcmp (node->name, _x("enabled")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          settings_set_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("wet_dry_enabled")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          settings_set_wet_dry_enabled (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("wet_dry_locked")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          settings_set_wet_dry_locked (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("wet_dry_values")) == 0)
+        {
+          unsigned long channel = 0;
+          
+          for (sub_node = node->children; sub_node; sub_node = sub_node->next)
+            {
+              if (xmlStrcmp (sub_node->name, _x("value")) == 0)
+                {
+                  content = xmlNodeGetContent (sub_node);
+                  settings_set_wet_dry_value (settings, channel, strtod (_s(content), NULL));
+                  xmlFree (content);
+                  
+                  channel++;
+                }
+            }
+        }
+      else if (xmlStrcmp (node->name, _x("lockall")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          settings_set_lock_all (settings, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("controlrow")) == 0)
+        {
+          gint copy = 0;
+
+          for (sub_node = node->children; sub_node; sub_node = sub_node->next)
+            {
+              if (xmlStrcmp (sub_node->name, _x("lock")) == 0)
+                {
+                  content = xmlNodeGetContent (sub_node);
+                  settings_set_lock (settings, control, xmlStrcmp (content, _x("true")) == 0 ? TRUE : FALSE);
+                  xmlFree (content);
+                }
+              else if (xmlStrcmp (sub_node->name, _x("value")) == 0)
+                {
+                  content = xmlNodeGetContent (sub_node);
+                  settings_set_control_value (settings, copy, control, strtod (_s(content), NULL));
+                  xmlFree (content);
+                  copy++;
+                }
+            }
+          
+          control++;
+        }
+    }
+  
+  if (settings)
+    saved_plugin->settings = settings;
+}
+
+static void
+saved_rack_parse_jackrack (jack_rack_t * jack_rack, saved_rack_t * saved_rack, const char * filename, xmlNodePtr jackrack)
+{
+  xmlNodePtr node;
+  xmlChar *content;
+  saved_plugin_t * saved_plugin;
+
+  for (node = jackrack->children; node; node = node->next)
+    {
+      if (xmlStrcmp (node->name, _x("channels")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          saved_rack->channels = strtoul (_s(content), NULL, 10);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("samplerate")) == 0)
+        {
+          content = xmlNodeGetContent (node);
+          saved_rack->sample_rate = strtoul (_s(content), NULL, 10);
+          xmlFree (content);
+        }
+      else if (xmlStrcmp (node->name, _x("plugin")) == 0)
+        {
+          saved_plugin = g_malloc0 (sizeof (saved_plugin_t));
+          saved_rack->plugins = g_slist_append (saved_rack->plugins, saved_plugin);
+          saved_rack_parse_plugin (jack_rack, saved_rack, saved_plugin, filename, node);
+        }
+    }
+}
+
+static saved_rack_t *
+saved_rack_new (jack_rack_t * jack_rack, const char * filename, xmlDocPtr doc)
+{
+  xmlNodePtr node;
+  saved_rack_t *saved_rack;
+  
+  /* create the saved rack */
+  saved_rack = g_malloc (sizeof (saved_rack_t));
+  saved_rack->plugins = NULL;
+  saved_rack->sample_rate = 48000;
+  saved_rack->channels = 2;
+  
+  for (node = doc->children; node; node = node->next)
+    {
+      if (xmlStrcmp (node->name, _x("jackrack")) == 0)
+        saved_rack_parse_jackrack (jack_rack, saved_rack, filename, node);
+    }
+  
+  return saved_rack;
+}
+
+static void
+saved_rack_destroy (saved_rack_t * saved_rack)
+{
+  GSList * list;
+  
+  for (list = saved_rack->plugins; list; list = g_slist_next (list))
+    settings_destroy (((saved_plugin_t *) list->data)->settings);
+  g_slist_free (saved_rack->plugins);
+  g_free (saved_rack);
+}
+
+
+int
+jack_rack_open_file (jack_rack_t * jack_rack, const char * filename)
+{
+  xmlDocPtr doc;
+  saved_rack_t * saved_rack;
+  GSList * list;
+  saved_plugin_t * saved_plugin;
+
+  doc = xmlParseFile (filename);
+  if (!doc)
+    {
+      fprintf (stderr, _("Could not parse file '%s'\n"), filename);
+      return 1;
+    }
+  
+  if (xmlStrcmp ( ((xmlDtdPtr)doc->children)->name, _x("jackrack")) != 0)
+    {
+      fprintf (stderr, _("The file '%s' is not a JACK Rack settings file\n"), filename);
+      return 1;
+    }
+  
+  saved_rack = saved_rack_new (jack_rack, filename, doc);
+  xmlFreeDoc (doc);
+  
+  if (!saved_rack)
+    return 1;
+
+  for (list = saved_rack->plugins; list; list = g_slist_next (list))
+    {
+      saved_plugin = list->data;
+      
+      settings_set_sample_rate (saved_plugin->settings, sample_rate);
+      
+      jack_rack_add_saved_plugin (jack_rack, saved_plugin);
+    }
+  
+  saved_rack_destroy (saved_rack);
+  
+  return 0;
+}
+
+
+/* EOF */
diff --git a/src/modules/jackrack/jack_rack.h b/src/modules/jackrack/jack_rack.h
new file mode 100644 (file)
index 0000000..7936a8b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_JACK_RACK_H__
+#define __JR_JACK_RACK_H__
+
+#include <glib.h>
+#include <ladspa.h>
+
+#include "plugin.h"
+#include "plugin_mgr.h"
+#include "plugin_settings.h"
+#include "process.h"
+
+typedef struct _saved_plugin saved_plugin_t;
+
+struct _saved_plugin
+{
+  settings_t     *settings;
+};
+
+typedef struct _saved_rack   saved_rack_t;
+
+struct _saved_rack
+{
+  unsigned long  channels;
+  jack_nframes_t sample_rate;
+  GSList *       plugins;
+};
+
+typedef struct _jack_rack jack_rack_t;
+
+struct _jack_rack
+{
+  plugin_mgr_t *    plugin_mgr;
+  process_info_t *  procinfo;
+  unsigned long     channels;
+  GSList *          saved_plugins;
+};
+
+jack_rack_t * jack_rack_new     (const char * client_name, unsigned long channels);
+void          jack_rack_destroy (jack_rack_t * jack_rack);
+
+int jack_rack_open_file (jack_rack_t * jack_rack, const char * filename);
+void jack_rack_add_plugin (jack_rack_t * jack_rack, plugin_t * plugin);
+void jack_rack_add_saved_plugin (jack_rack_t * jack_rack, struct _saved_plugin * saved_plugin);
+
+plugin_t * jack_rack_instantiate_plugin (jack_rack_t * jack_rack, plugin_desc_t * desc);
+
+#endif /* __JR_JACK_RACK_H__ */
diff --git a/src/modules/jackrack/lock_free_fifo.c b/src/modules/jackrack/lock_free_fifo.c
new file mode 100644 (file)
index 0000000..177f6b4
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "lock_free_fifo.h"
+
+/** initialise a lock free fifo */
+
+void
+lff_init (lff_t * lff, unsigned int size, size_t object_size)
+{
+  lff->size = size;
+  lff->object_size = object_size;
+  lff->read_index = 0;
+  lff->write_index = 0;
+  lff->data = g_malloc (object_size * size);
+}
+
+lff_t *
+lff_new (unsigned int size, size_t object_size)
+{
+  lff_t * lff;
+  
+  lff = g_malloc (sizeof (lff_t));
+  
+  lff_init (lff, size, object_size);
+  
+  return lff;
+}
+
+void
+lff_free (lff_t * lff)
+{
+  g_free (lff->data);
+}
+
+void
+lff_destroy (lff_t * lff)
+{
+  lff_free (lff);
+  g_free (lff);
+}
+
+/** read an element from the fifo into data.
+returns 0 on success, non-zero if there were no elements to read */
+int lff_read (lff_t * lff, void * data) {
+  if (lff->read_index == lff->write_index) {
+    return -1;
+  } else {
+    memcpy (data, ((char *)lff->data) + (lff->read_index * lff->object_size),
+            lff->object_size);
+    lff->read_index++;
+    if (lff->read_index >= lff->size) {
+      lff->read_index = 0;
+    }
+    return 0;
+  }
+}
+
+/** write an element from data to the fifo.
+returns 0 on success, non-zero if there was no space */
+int lff_write (lff_t * lff, void * data) {
+  static unsigned int ri;
+  
+  /* got to read read_index only once for safety */
+  ri = lff->read_index;
+
+  /* lots of logic for when we're allowed to write to the fifo which basically
+     boils down to "don't write if we're one element behind the read index" */  
+  if ((ri > lff->write_index && ri - lff->write_index > 1) ||
+      (lff->write_index >= ri && lff->write_index != lff->size - 1) ||
+      (lff->write_index >= ri && lff->write_index == lff->size - 1 && ri != 0)) { 
+
+/*  if ((ri > lff->write_index && ri - lff->write_index > 1) ||
+      (lff->write_index >= ri && (lff->write_index != lff->size - 1 || ri != 0))) { */
+
+    memcpy (((char *)lff->data) + (lff->write_index * lff->object_size),
+            data, lff->object_size);
+
+    /* FIXME: is this safe? */
+    lff->write_index++;
+    if (lff->write_index >= lff->size) {
+      lff->write_index = 0;
+    }
+
+    return 0;
+  } else {
+    return -1;
+  }
+}
diff --git a/src/modules/jackrack/lock_free_fifo.h b/src/modules/jackrack/lock_free_fifo.h
new file mode 100644 (file)
index 0000000..29abd3c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JLH_LOCK_FREE_FIFO_H__
+#define __JLH_LOCK_FREE_FIFO_H__
+
+/** lock free fifo ring buffer structure */
+typedef struct lock_free_fifo {
+  /** Size of the ringbuffer (in elements) */
+  unsigned int size;
+  /** the memory containing the ringbuffer */
+  void * data;
+  /** the size of an element */
+  size_t object_size;
+  /** the current position of the reader */
+  unsigned int read_index;
+  /** the current position of the writer */
+  unsigned int write_index;
+} lff_t;
+
+void lff_init (lff_t * lff, unsigned int size, size_t object_size);
+void lff_free (lff_t * lff);
+
+lff_t * lff_new     (unsigned int size, size_t object_size);
+void    lff_destroy (lff_t * lock_free_fifo);
+               
+int lff_read (lff_t * lock_free_fifo, void * data);
+int lff_write (lff_t * lock_free_fifo, void * data);
+
+
+#endif /* __JLH_LOCK_FREE_FIFO_H__ */
diff --git a/src/modules/jackrack/plugin.c b/src/modules/jackrack/plugin.c
new file mode 100644 (file)
index 0000000..6a483a8
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ladspa.h>
+#include <dlfcn.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "plugin.h"
+#include "jack_rack.h"
+#include "process.h"
+
+#define CONTROL_FIFO_SIZE   128
+
+
+
+/* swap over the jack ports in two plugins */
+static void
+plugin_swap_aux_ports (plugin_t * plugin, plugin_t * other)
+{
+  guint copy;
+  jack_port_t ** aux_ports_tmp;
+  
+  for (copy = 0; copy < plugin->copies; copy++)
+    {
+      aux_ports_tmp = other->holders[copy].aux_ports;
+      other->holders[copy].aux_ports = plugin->holders[copy].aux_ports;
+      plugin->holders[copy].aux_ports = aux_ports_tmp;
+    }
+}
+
+/** connect up the ladspa instance's input buffers to the previous
+    plugin's audio memory.  make sure to check that plugin->prev
+    exists. */
+void
+plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs)
+{
+  gint copy;
+  unsigned long channel;
+  unsigned long rack_channel;
+
+  if (!plugin || !inputs)
+    return;
+
+  rack_channel = 0;
+  for (copy = 0; copy < plugin->copies; copy++)
+    {
+      for (channel = 0; channel < plugin->desc->channels; channel++)
+        {
+          plugin->descriptor->
+            connect_port (plugin->holders[copy].instance,
+                          plugin->desc->audio_input_port_indicies[channel],
+                          inputs[rack_channel]);
+          rack_channel++;
+        }
+    }
+  
+  plugin->audio_input_memory = inputs;
+}
+
+/** connect up a plugin's output ports to its own audio_output_memory output memory */
+void
+plugin_connect_output_ports (plugin_t * plugin)
+{
+  gint copy;
+  unsigned long channel;
+  unsigned long rack_channel = 0;
+
+  if (!plugin)
+    return;
+
+
+  for (copy = 0; copy < plugin->copies; copy++)
+    {
+      for (channel = 0; channel < plugin->desc->channels; channel++)
+        {
+          plugin->descriptor->
+            connect_port (plugin->holders[copy].instance,
+                          plugin->desc->audio_output_port_indicies[channel],
+                          plugin->audio_output_memory[rack_channel]);
+          rack_channel++;
+        }
+    }
+}
+
+void
+process_add_plugin (process_info_t * procinfo, plugin_t * plugin)
+{
+
+  /* sort out list pointers */
+  plugin->next = NULL;
+  plugin->prev = procinfo->chain_end;
+
+  if (procinfo->chain_end)
+    procinfo->chain_end->next = plugin;
+  else
+    procinfo->chain = plugin;
+
+  procinfo->chain_end = plugin;
+
+}
+
+
+/** remove a plugin from the chain */
+plugin_t *
+process_remove_plugin (process_info_t * procinfo, plugin_t *plugin)
+{
+  /* sort out chain pointers */
+  if (plugin->prev)
+    plugin->prev->next = plugin->next;
+  else
+    procinfo->chain = plugin->next;
+
+  if (plugin->next)
+    plugin->next->prev = plugin->prev;
+  else
+    procinfo->chain_end = plugin->prev;
+    
+  /* sort out the aux ports */
+  if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+    {
+      plugin_t * other;
+      
+      for (other = plugin->next; other; other = other->next)
+        if (other->desc->id == plugin->desc->id)
+          plugin_swap_aux_ports (plugin, other);
+    }
+
+  return plugin;
+}
+
+/** enable/disable a plugin */
+void
+process_ablise_plugin (process_info_t * procinfo, plugin_t *plugin, gboolean enable)
+{
+  plugin->enabled = enable;
+}
+
+/** enable/disable a plugin */
+void
+process_ablise_plugin_wet_dry (process_info_t * procinfo, plugin_t *plugin, gboolean enable)
+{
+  plugin->wet_dry_enabled = enable;
+}
+
+/** move a plugin up or down one place in the chain */
+void
+process_move_plugin (process_info_t * procinfo, plugin_t *plugin, gint up)
+{
+  /* other plugins in the chain */
+  plugin_t *pp = NULL, *p, *n, *nn = NULL;
+
+  /* note that we should never recieve an illogical move request
+     ie, there will always be at least 1 plugin before for an up
+     request or 1 plugin after for a down request */
+
+  /* these are pointers to the plugins surrounding the specified one:
+     { pp, p, plugin, n, nn } which makes things much clearer than
+     tptr, tptr2 etc */
+  p = plugin->prev;
+  if (p) pp = p->prev;
+  n = plugin->next;
+  if (n) nn = n->next;
+
+  if (up)
+    {
+      if (!p)
+       return;
+
+      if (pp)
+        pp->next = plugin;
+      else
+        procinfo->chain = plugin;
+
+      p->next = n;
+      p->prev = plugin;
+
+      plugin->prev = pp;
+      plugin->next = p;
+
+      if (n)
+       n->prev = p;
+      else
+       procinfo->chain_end = p;
+
+    }
+  else
+    {
+      if (!n)
+       return;
+
+      if (p)
+       p->next = n;
+      else
+       procinfo->chain = n;
+
+      n->prev = p;
+      n->next = plugin;
+
+      plugin->prev = n;
+      plugin->next = nn;
+
+      if (nn)
+       nn->prev = plugin;
+      else
+       procinfo->chain_end = plugin;
+    }
+  
+  if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+    {
+      plugin_t * other;
+      other = up ? plugin->next : plugin->prev;
+      
+      /* swap around the jack ports */
+      if (other->desc->id == plugin->desc->id)
+        plugin_swap_aux_ports (plugin, other);
+    }
+}
+
+/** exchange an existing plugin for a newly created one */
+plugin_t *
+process_change_plugin (process_info_t * procinfo,
+                      plugin_t *plugin, plugin_t * new_plugin)
+{
+  new_plugin->next = plugin->next;
+  new_plugin->prev = plugin->prev;
+
+  if (plugin->prev)
+    plugin->prev->next = new_plugin;
+  else
+    procinfo->chain = new_plugin;
+
+  if (plugin->next)
+    plugin->next->prev = new_plugin;
+  else
+    procinfo->chain_end = new_plugin;
+
+  /* sort out the aux ports */
+  if (procinfo->jack_client && plugin->desc->aux_channels > 0)
+    {
+      plugin_t * other;
+      
+      for (other = plugin->next; other; other = other->next)
+        if (other->desc->id == plugin->desc->id)
+          plugin_swap_aux_ports (plugin, other);
+    }
+
+  return plugin;
+}
+
+
+/******************************************
+ ************* non RT stuff ***************
+ ******************************************/
+
+
+static int
+plugin_open_plugin (plugin_desc_t * desc,
+                    void ** dl_handle_ptr,
+                    const LADSPA_Descriptor ** descriptor_ptr)
+{
+  void * dl_handle;
+  const char * dlerr;
+  LADSPA_Descriptor_Function get_descriptor;
+    
+  /* open the object file */
+  dl_handle = dlopen (desc->object_file, RTLD_NOW|RTLD_GLOBAL);
+  if (!dl_handle)
+    {
+      fprintf (stderr, "%s: error opening shared object file '%s': %s\n",
+               __FUNCTION__, desc->object_file, dlerror());
+      return 1;
+    }
+  
+  
+  /* get the get_descriptor function */
+  dlerror (); /* clear the error report */
+  
+  get_descriptor = (LADSPA_Descriptor_Function)
+    dlsym (dl_handle, "ladspa_descriptor");
+  
+  dlerr = dlerror();
+  if (dlerr)
+    {
+      fprintf (stderr, "%s: error finding descriptor symbol in object file '%s': %s\n",
+               __FUNCTION__, desc->object_file, dlerr);
+      dlclose (dl_handle);
+      return 1;
+    }
+  
+  *descriptor_ptr = get_descriptor (desc->index);
+  *dl_handle_ptr = dl_handle;
+  
+  return 0;
+}
+
+static int
+plugin_instantiate (const LADSPA_Descriptor * descriptor,
+                    unsigned long plugin_index,
+                    gint copies,
+                    LADSPA_Handle * instances)
+{
+  gint i;
+  
+  for (i = 0; i < copies; i++)
+    {
+      instances[i] = descriptor->instantiate (descriptor, sample_rate);
+      
+      if (!instances[i])
+        {
+          unsigned long d;
+          for (d = 0; d < i; d++)
+            descriptor->cleanup (instances[d]);
+          
+          return 1;
+        }
+    }
+  
+  return 0;
+}
+
+static void
+plugin_create_aux_ports (plugin_t * plugin, guint copy, jack_rack_t * jack_rack)
+{
+  plugin_desc_t * desc;
+//  plugin_slot_t * slot;
+  unsigned long aux_channel = 1;
+  unsigned long plugin_index = 1;
+  unsigned long i;
+  char port_name[64];
+  char * plugin_name;
+  char * ptr;
+//  GList * list;
+  ladspa_holder_t * holder;
+  
+  desc = plugin->desc;
+  holder = plugin->holders + copy;
+  
+  holder->aux_ports = g_malloc (sizeof (jack_port_t *) * desc->aux_channels);
+  
+  /* make the plugin name jack worthy */
+  ptr = plugin_name = g_strndup (plugin->desc->name, 7);
+  while (*ptr != '\0')
+    {
+      if (*ptr == ' ')
+        *ptr = '_';
+      else
+        *ptr = tolower (*ptr);
+      
+      ptr++;
+    }
+
+/*     
+  for (list = jack_rack->slots; list; list = g_list_next (list))
+    {
+      slot = (plugin_slot_t *) list->data;
+      
+      if (slot->plugin->desc->id == plugin->desc->id)
+        plugin_index++;
+    }
+*/
+      
+  for (i = 0; i < desc->aux_channels; i++, aux_channel++)
+    {
+      sprintf (port_name, "%s_%ld-%d_%c%ld",
+               plugin_name,
+               plugin_index,
+               copy + 1,
+               desc->aux_are_input ? 'i' : 'o',
+               aux_channel);
+      
+      holder->aux_ports[i] =
+        jack_port_register (jack_rack->procinfo->jack_client,
+                            port_name,
+                            JACK_DEFAULT_AUDIO_TYPE,
+                            desc->aux_are_input ? JackPortIsInput : JackPortIsOutput,
+                            0);
+      
+      if (!holder->aux_ports[i])
+        {
+          fprintf (stderr, "Could not register jack port '%s'; aborting\n", port_name);
+          abort ();
+        }
+    }
+  
+  g_free (plugin_name);
+}
+
+static LADSPA_Data unused_control_port_output;
+
+static void
+plugin_init_holder (plugin_t * plugin,
+                    guint copy,
+                    LADSPA_Handle instance,
+                    jack_rack_t * jack_rack)
+{
+  unsigned long i;
+  plugin_desc_t * desc;
+  ladspa_holder_t * holder;
+  
+  desc = plugin->desc;
+  holder = plugin->holders + copy;
+  
+  holder->instance = instance;
+  
+  if (desc->control_port_count > 0)
+    {
+      holder->ui_control_fifos    = g_malloc (sizeof (lff_t) * desc->control_port_count);
+      holder->control_memory = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+    }
+  else
+    {
+      holder->ui_control_fifos  = NULL;
+      holder->control_memory = NULL;
+    }
+  
+  for (i = 0; i < desc->control_port_count; i++)
+    {
+      lff_init (holder->ui_control_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data));
+      holder->control_memory[i] =
+        plugin_desc_get_default_control_value (desc, desc->control_port_indicies[i], sample_rate);        
+      
+      plugin->descriptor->
+        connect_port (instance, desc->control_port_indicies[i], holder->control_memory + i);
+    }
+  
+  for (i = 0; i < desc->port_count; i++)
+    {
+      if (!LADSPA_IS_PORT_CONTROL (desc->port_descriptors[i]))
+        continue;
+      
+      if (LADSPA_IS_PORT_OUTPUT (desc->port_descriptors[i]))
+        plugin->descriptor-> connect_port (instance, i, &unused_control_port_output);
+    }
+  
+  if (jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0)
+    plugin_create_aux_ports (plugin, copy, jack_rack);
+  
+  if (plugin->descriptor->activate)
+    plugin->descriptor->activate (instance);
+}
+
+
+plugin_t *
+plugin_new (plugin_desc_t * desc, jack_rack_t * jack_rack)
+{
+  void * dl_handle;
+  const LADSPA_Descriptor * descriptor;
+  LADSPA_Handle * instances;
+  gint copies;
+  unsigned long i;
+  int err;
+  plugin_t * plugin;
+  
+  /* open the plugin */
+  err = plugin_open_plugin (desc, &dl_handle, &descriptor);
+  if (err)
+    return NULL;
+
+  /* create the instances */
+  copies = plugin_desc_get_copies (desc, jack_rack->channels);
+  instances = g_malloc (sizeof (LADSPA_Handle) * copies);
+
+  err = plugin_instantiate (descriptor, desc->index, copies, instances);
+  if (err)
+    {
+      g_free (instances);
+      dlclose (dl_handle);
+      return NULL;
+    }
+  
+
+  plugin = g_malloc (sizeof (plugin_t));
+  
+  plugin->descriptor = descriptor;
+  plugin->dl_handle = dl_handle;
+  plugin->desc = desc;
+  plugin->copies = copies;
+  plugin->enabled = FALSE;
+  plugin->next = NULL;
+  plugin->prev = NULL;
+  plugin->wet_dry_enabled = FALSE;
+  plugin->jack_rack = jack_rack;
+  
+  /* create audio memory and wet/dry stuff */
+  plugin->audio_output_memory   = g_malloc (sizeof (LADSPA_Data *) * jack_rack->channels);
+  plugin->wet_dry_fifos  = g_malloc (sizeof (lff_t) * jack_rack->channels);
+  plugin->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * jack_rack->channels);
+  
+  for (i = 0; i < jack_rack->channels; i++)
+    {
+      plugin->audio_output_memory[i] = g_malloc (sizeof (LADSPA_Data) * buffer_size);
+      lff_init (plugin->wet_dry_fifos + i, CONTROL_FIFO_SIZE, sizeof (LADSPA_Data));
+      plugin->wet_dry_values[i] = 1.0;
+    }
+  
+  /* create holders and fill them out */
+  plugin->holders = g_malloc (sizeof (ladspa_holder_t) * copies);
+  for (i = 0; i < copies; i++)
+    plugin_init_holder (plugin, i, instances[i], jack_rack);
+  
+  return plugin;
+}
+
+
+void
+plugin_destroy (plugin_t * plugin)
+{
+  unsigned long i, j;
+  int err;
+
+  /* destroy holders */
+  for (i = 0; i < plugin->copies; i++)
+    {
+      if (plugin->descriptor->deactivate)
+        plugin->descriptor->deactivate (plugin->holders[i].instance);
+      
+/*      if (plugin->descriptor->cleanup)
+        plugin->descriptor->cleanup (plugin->holders[i].instance); */
+      
+      if (plugin->desc->control_port_count > 0)
+        {
+          for (j = 0; j < plugin->desc->control_port_count; j++)
+            {
+              lff_free (plugin->holders[i].ui_control_fifos + j);
+            }
+          g_free (plugin->holders[i].ui_control_fifos);
+          g_free (plugin->holders[i].control_memory);
+        }
+      
+      /* aux ports */
+      if (plugin->jack_rack->procinfo->jack_client && plugin->desc->aux_channels > 0)
+        {
+          for (j = 0; j < plugin->desc->aux_channels; j++)
+            {
+              err = jack_port_unregister (plugin->jack_rack->procinfo->jack_client,
+                                          plugin->holders[i].aux_ports[j]);
+          
+              if (err)
+                fprintf (stderr, "%s: could not unregister jack port\n", __FUNCTION__);
+            }
+       
+          g_free (plugin->holders[i].aux_ports);
+        }
+    }
+    
+  g_free (plugin->holders);
+  
+  for (i = 0; i < plugin->jack_rack->channels; i++)
+    {
+      g_free (plugin->audio_output_memory[i]);
+      lff_free (plugin->wet_dry_fifos + i);
+    }
+    
+  g_free (plugin->audio_output_memory);
+  g_free (plugin->wet_dry_fifos);
+  g_free (plugin->wet_dry_values);
+  
+  err = dlclose (plugin->dl_handle);
+  if (err)
+    {
+      fprintf (stderr, "%s: error closing shared object '%s': %s\n",
+               __FUNCTION__, plugin->desc->object_file, dlerror ());
+    }
+   
+  g_free (plugin);
+}
+
+
+/* EOF */
diff --git a/src/modules/jackrack/plugin.h b/src/modules/jackrack/plugin.h
new file mode 100644 (file)
index 0000000..576b803
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_H__
+#define __JR_PLUGIN_H__
+
+#include <glib.h>
+#include <ladspa.h>
+#include <jack/jack.h>
+
+#include "process.h"
+#include "plugin_desc.h"
+
+typedef struct _ladspa_holder ladspa_holder_t;
+typedef struct _plugin plugin_t;
+
+struct _ladspa_holder
+{
+  LADSPA_Handle instance;
+  lff_t * ui_control_fifos;
+  LADSPA_Data * control_memory;
+
+  jack_port_t **             aux_ports;
+};
+
+struct _plugin
+{
+  plugin_desc_t *            desc;
+  gint                       enabled;
+
+  gint                       copies;
+  ladspa_holder_t *          holders;
+  LADSPA_Data **             audio_input_memory;
+  LADSPA_Data **             audio_output_memory;
+  
+  gboolean                   wet_dry_enabled;
+  /* 1.0 = all wet, 0.0 = all dry, 0.5 = 50% wet/50% dry */
+  LADSPA_Data *              wet_dry_values;
+  lff_t *                    wet_dry_fifos;
+  
+  plugin_t *                 next;
+  plugin_t *                 prev;
+
+  const LADSPA_Descriptor *  descriptor;
+  void *                     dl_handle;
+  struct _jack_rack *        jack_rack;
+  
+};
+
+void       process_add_plugin            (process_info_t *, plugin_t *plugin);
+plugin_t * process_remove_plugin         (process_info_t *, plugin_t *plugin);
+void       process_ablise_plugin         (process_info_t *, plugin_t *plugin, gboolean able);
+void       process_ablise_plugin_wet_dry (process_info_t *, plugin_t *plugin, gboolean enable);
+void       process_move_plugin           (process_info_t *, plugin_t *plugin, gint up);
+plugin_t * process_change_plugin         (process_info_t *, plugin_t *plugin, plugin_t * new_plugin);
+
+struct _jack_rack;
+struct _ui;
+
+plugin_t * plugin_new (plugin_desc_t * plugin_desc, struct _jack_rack * jack_rack);
+void       plugin_destroy (plugin_t * plugin);
+
+void plugin_connect_input_ports (plugin_t * plugin, LADSPA_Data ** inputs);
+void plugin_connect_output_ports (plugin_t * plugin);
+
+#endif /* __JR_PLUGIN_H__ */
diff --git a/src/modules/jackrack/plugin_desc.c b/src/modules/jackrack/plugin_desc.c
new file mode 100644 (file)
index 0000000..b325989
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+
+#include "plugin_desc.h"
+#include "plugin.h"
+
+#define set_string_property(property, value) \
+  \
+  if (property) \
+    g_free (property); \
+  \
+  if (value) \
+    (property) = g_strdup (value); \
+  else \
+    (property) = NULL;
+
+
+void
+plugin_desc_set_ports (plugin_desc_t * pd,
+                       unsigned long port_count,
+                      const LADSPA_PortDescriptor * port_descriptors,
+                       const LADSPA_PortRangeHint * port_range_hints,
+                       const char * const * port_names);
+
+
+
+static void
+plugin_desc_init (plugin_desc_t * pd)
+{
+  pd->object_file      = NULL;
+  pd->id               = 0;
+  pd->name             = NULL;
+  pd->properties       = 0;
+  pd->channels         = 0;
+  pd->port_count       = 0;
+  pd->port_descriptors = NULL;
+  pd->port_range_hints = NULL;
+  pd->audio_input_port_indicies = NULL;
+  pd->audio_output_port_indicies = NULL;
+  pd->audio_aux_port_indicies = NULL;
+  pd->control_port_count = 0;
+  pd->control_port_indicies = NULL;
+  pd->aux_channels = 0;
+  pd->aux_are_input = TRUE;
+}
+
+static void
+plugin_desc_free_ports (plugin_desc_t * pd)
+{
+  if (pd->port_count)
+    {
+      g_free (pd->port_descriptors);
+      g_free (pd->port_range_hints);
+      pd->port_descriptors = NULL;
+      pd->port_range_hints = NULL;
+      pd->port_count = 0;
+    }
+}
+
+static void
+plugin_desc_free (plugin_desc_t * pd)
+{
+  plugin_desc_set_object_file (pd, NULL);
+  plugin_desc_set_name        (pd, NULL);
+  plugin_desc_free_ports      (pd);
+}
+
+plugin_desc_t *
+plugin_desc_new ()
+{
+  plugin_desc_t * pd;
+  pd = g_malloc (sizeof (plugin_desc_t));
+  plugin_desc_init (pd);
+  return pd;
+}
+
+plugin_desc_t *
+plugin_desc_new_with_descriptor (const char * object_file,
+                                 unsigned long index,
+                                 const LADSPA_Descriptor * descriptor)
+{
+  plugin_desc_t * pd;
+  pd = plugin_desc_new ();
+  
+  plugin_desc_set_object_file (pd, object_file);
+  plugin_desc_set_index       (pd, index);
+  plugin_desc_set_id          (pd, descriptor->UniqueID);
+  plugin_desc_set_name        (pd, descriptor->Name);
+  plugin_desc_set_properties  (pd, descriptor->Properties);
+  plugin_desc_set_ports       (pd,
+                               descriptor->PortCount,
+                               descriptor->PortDescriptors,
+                               descriptor->PortRangeHints,
+                               descriptor->PortNames);
+  
+  pd->rt = LADSPA_IS_HARD_RT_CAPABLE(pd->properties) ? TRUE : FALSE;
+
+  return pd;
+}
+
+void
+plugin_desc_destroy (plugin_desc_t * pd)
+{
+  plugin_desc_free (pd);
+  g_free (pd);
+}
+
+void
+plugin_desc_set_object_file (plugin_desc_t * pd, const char * object_file)
+{
+  set_string_property (pd->object_file, object_file);
+}
+
+void
+plugin_desc_set_index          (plugin_desc_t * pd, unsigned long index)
+{
+  pd->index = index;
+}
+
+
+void
+plugin_desc_set_id          (plugin_desc_t * pd, unsigned long id)
+{
+  pd->id = id;
+}
+
+void
+plugin_desc_set_name        (plugin_desc_t * pd, const char * name)
+{
+  set_string_property (pd->name, name);
+}
+
+void
+plugin_desc_set_properties  (plugin_desc_t * pd, LADSPA_Properties properties)
+{
+  pd->properties = properties;
+}
+
+static void
+plugin_desc_add_audio_port_index (unsigned long ** indicies,
+                                  unsigned long * current_port_count,
+                                  unsigned long index)
+{
+  (*current_port_count)++;
+  
+  if (*current_port_count == 0)
+    *indicies = g_malloc (sizeof (unsigned long) * *current_port_count);
+  else
+    *indicies = g_realloc (*indicies, sizeof (unsigned long) * *current_port_count);
+  
+  (*indicies)[*current_port_count - 1] = index;
+}
+
+static void
+plugin_desc_set_port_counts (plugin_desc_t * pd)
+{
+  unsigned long i;
+  unsigned long icount = 0;
+  unsigned long ocount = 0;
+  
+  for (i = 0; i < pd->port_count; i++)
+    {
+      if (LADSPA_IS_PORT_AUDIO (pd->port_descriptors[i]))
+        {
+          if (LADSPA_IS_PORT_INPUT (pd->port_descriptors[i]))
+            plugin_desc_add_audio_port_index (&pd->audio_input_port_indicies, &icount, i);
+          else
+            plugin_desc_add_audio_port_index (&pd->audio_output_port_indicies, &ocount, i);
+        }
+      else
+        {
+          if (LADSPA_IS_PORT_OUTPUT (pd->port_descriptors[i]))
+            continue;
+            
+          pd->control_port_count++;
+          if (pd->control_port_count == 0)
+            pd->control_port_indicies = g_malloc (sizeof (unsigned long) * pd->control_port_count);
+          else
+            pd->control_port_indicies = g_realloc (pd->control_port_indicies,
+                                                   sizeof (unsigned long) * pd->control_port_count);
+          
+          pd->control_port_indicies[pd->control_port_count - 1] = i;
+        }
+    }
+  
+  if (icount == ocount)
+    pd->channels = icount;
+  else
+    { /* deal with auxilliary ports */
+      unsigned long ** port_indicies;
+      unsigned long port_count;
+      unsigned long i, j;
+     
+      if (icount > ocount)
+        {
+          pd->channels = ocount;
+          pd->aux_channels = icount - ocount;
+          pd->aux_are_input = TRUE;
+          port_indicies = &pd->audio_input_port_indicies;
+          port_count = icount;
+        }
+      else
+        {
+          pd->channels = icount;
+          pd->aux_channels = ocount - icount;
+          pd->aux_are_input = FALSE;
+          port_indicies = &pd->audio_output_port_indicies;
+          port_count = ocount;
+        }
+      
+      /* allocate indicies */
+      pd->audio_aux_port_indicies = g_malloc (sizeof (unsigned long) * pd->aux_channels);
+      
+      /* copy indicies */
+      for (i = pd->channels, j = 0; i < port_count; i++, j++)
+        pd->audio_aux_port_indicies[j] = (*port_indicies)[i];
+      
+      /* shrink the main indicies to only have channels indicies */
+      *port_indicies = g_realloc (*port_indicies, sizeof (unsigned long) * pd->channels);
+    }
+}
+
+void
+plugin_desc_set_ports (plugin_desc_t * pd,
+                       unsigned long port_count,
+                       const LADSPA_PortDescriptor * port_descriptors,
+                       const LADSPA_PortRangeHint * port_range_hints,
+                       const char * const * port_names)
+{
+  unsigned long i;
+
+  plugin_desc_free_ports (pd);
+  
+  if (!port_count)
+    return;
+  
+  pd->port_count = port_count;
+  pd->port_descriptors = g_malloc (sizeof (LADSPA_PortDescriptor) * port_count);
+  pd->port_range_hints = g_malloc (sizeof (LADSPA_PortRangeHint) * port_count);
+  pd->port_names       = g_malloc (sizeof (char *) * port_count);
+  
+  memcpy (pd->port_descriptors, port_descriptors, sizeof (LADSPA_PortDescriptor) * port_count);
+  memcpy (pd->port_range_hints, port_range_hints, sizeof (LADSPA_PortRangeHint) * port_count);
+  
+  for (i = 0; i < port_count; i++)
+    pd->port_names[i] = g_strdup (port_names[i]);
+  
+  plugin_desc_set_port_counts (pd);
+}
+
+
+LADSPA_Data
+plugin_desc_get_default_control_value (plugin_desc_t * pd, unsigned long port_index, guint32 sample_rate)
+{
+  LADSPA_Data upper, lower;
+  LADSPA_PortRangeHintDescriptor hint_descriptor;
+  
+  hint_descriptor = pd->port_range_hints[port_index].HintDescriptor;
+  
+  /* set upper and lower, possibly adjusted to the sample rate */
+  if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+    upper = pd->port_range_hints[port_index].UpperBound * (LADSPA_Data) sample_rate;
+    lower = pd->port_range_hints[port_index].LowerBound * (LADSPA_Data) sample_rate;
+  } else {
+    upper = pd->port_range_hints[port_index].UpperBound;
+    lower = pd->port_range_hints[port_index].LowerBound;
+  }
+  
+  if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor))
+    {
+      if (lower < FLT_EPSILON)
+        lower = FLT_EPSILON;
+    }
+    
+
+  if (LADSPA_IS_HINT_HAS_DEFAULT(hint_descriptor)) {
+      
+           if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) {
+    
+      return lower;
+       
+    } else if (LADSPA_IS_HINT_DEFAULT_LOW(hint_descriptor)) {
+        
+      if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+        return exp(log(lower) * 0.75 + log(upper) * 0.25);
+      } else {
+        return lower * 0.75 + upper * 0.25;
+      }
+
+    } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) {
+        
+      if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+        return exp(log(lower) * 0.5 + log(upper) * 0.5);
+      } else {
+        return lower * 0.5 + upper * 0.5;
+      }
+
+    } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) {
+      
+      if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+        return exp(log(lower) * 0.25 + log(upper) * 0.75);
+      } else {
+        return lower * 0.25 + upper * 0.75;
+      }
+      
+    } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) {
+      
+      return upper;
+    
+    } else if (LADSPA_IS_HINT_DEFAULT_0(hint_descriptor)) {
+      
+      return 0.0;
+      
+    } else if (LADSPA_IS_HINT_DEFAULT_1(hint_descriptor)) {
+      
+      if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+        return (LADSPA_Data) sample_rate;
+      } else {
+        return 1.0;
+      }
+      
+    } else if (LADSPA_IS_HINT_DEFAULT_100(hint_descriptor)) {
+      
+      if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+        return 100.0 * (LADSPA_Data) sample_rate;
+      } else {
+        return 100.0;
+      }
+      
+    } else if (LADSPA_IS_HINT_DEFAULT_440(hint_descriptor)) {
+      
+      if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+        return 440.0 * (LADSPA_Data) sample_rate;
+      } else {
+        return 440.0;
+      }
+      
+    }  
+      
+  } else { /* try and find a reasonable default */
+        
+           if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) {
+      return lower;
+    } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) {
+      return upper;
+    }
+  }
+
+  return 0.0;
+}
+
+LADSPA_Data
+plugin_desc_change_control_value (plugin_desc_t * pd,
+                                  unsigned long control_index,
+                                  LADSPA_Data value,
+                                  guint32 old_sample_rate,
+                                  guint32 new_sample_rate)
+{
+  
+  if (LADSPA_IS_HINT_SAMPLE_RATE (pd->port_range_hints[control_index].HintDescriptor))
+    {
+      LADSPA_Data old_sr, new_sr;
+  
+      old_sr = (LADSPA_Data) old_sample_rate;
+      new_sr = (LADSPA_Data) new_sample_rate;
+  
+      value /= old_sr;
+      value *= new_sr;
+    }
+  
+  return value;
+}
+
+gint
+plugin_desc_get_copies (plugin_desc_t * pd, unsigned long rack_channels)
+{
+  gint copies = 1;
+  
+  if (pd->channels > rack_channels)
+    return 0;
+  
+  while (pd->channels * copies < rack_channels)
+    copies++;
+  
+  if (pd->channels * copies > rack_channels)
+    return 0;
+  
+  return copies;
+}
+
+/* EOF */
diff --git a/src/modules/jackrack/plugin_desc.h b/src/modules/jackrack/plugin_desc.h
new file mode 100644 (file)
index 0000000..d6b5ca3
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_DESC_H__
+#define __JR_PLUGIN_DESC_H__
+
+#include <ladspa.h>
+#include <glib.h>
+
+typedef struct _plugin_desc plugin_desc_t;
+
+struct _plugin_desc
+{
+  char *                   object_file;
+  unsigned long            index;
+  unsigned long            id;
+  char *                   name;
+  LADSPA_Properties        properties;
+  gboolean                 rt;
+  
+  unsigned long            channels;
+  
+  gboolean                 aux_are_input;
+  unsigned long            aux_channels;
+
+  unsigned long            port_count;
+  LADSPA_PortDescriptor *  port_descriptors;
+  LADSPA_PortRangeHint *   port_range_hints;
+  char **                  port_names;
+  
+  unsigned long *          audio_input_port_indicies;
+  unsigned long *          audio_output_port_indicies;
+  
+  unsigned long *          audio_aux_port_indicies;
+
+  unsigned long            control_port_count;
+  unsigned long *          control_port_indicies;
+};
+
+plugin_desc_t * plugin_desc_new ();
+plugin_desc_t * plugin_desc_new_with_descriptor (const char * object_file,
+                                                 unsigned long index,
+                                                 const LADSPA_Descriptor * descriptor);
+void            plugin_desc_destroy ();
+
+void plugin_desc_set_object_file (plugin_desc_t * pd, const char * object_file);
+void plugin_desc_set_index       (plugin_desc_t * pd, unsigned long index);
+void plugin_desc_set_id          (plugin_desc_t * pd, unsigned long id);
+void plugin_desc_set_name        (plugin_desc_t * pd, const char * name);
+void plugin_desc_set_properties  (plugin_desc_t * pd, LADSPA_Properties properties);
+
+struct _plugin * plugin_desc_instantiate (plugin_desc_t * pd);
+
+LADSPA_Data plugin_desc_get_default_control_value (plugin_desc_t * pd, unsigned long port_index, guint32 sample_rate);
+LADSPA_Data plugin_desc_change_control_value (plugin_desc_t *, unsigned long, LADSPA_Data, guint32, guint32);
+
+gint plugin_desc_get_copies (plugin_desc_t * pd, unsigned long rack_channels);
+
+#endif /* __JR_PLUGIN_DESC_H__ */
diff --git a/src/modules/jackrack/plugin_mgr.c b/src/modules/jackrack/plugin_mgr.c
new file mode 100644 (file)
index 0000000..ca80609
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <strings.h>
+#include <ctype.h>
+#include <ladspa.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "plugin_mgr.h"
+#include "plugin_desc.h"
+
+
+static gboolean
+plugin_is_valid (const LADSPA_Descriptor * descriptor)
+{
+  unsigned long i;
+  unsigned long icount = 0;
+  unsigned long ocount = 0;
+  
+  for (i = 0; i < descriptor->PortCount; i++)
+    {
+      if (!LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[i]))
+        continue;
+      
+      if (LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[i]))
+        icount++;
+      else
+        ocount++;
+    }
+  
+  if (icount == 0 || ocount == 0)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static void
+plugin_mgr_get_object_file_plugins (plugin_mgr_t * plugin_mgr, const char * filename)
+{
+  const char * dlerr;
+  void * dl_handle;
+  LADSPA_Descriptor_Function get_descriptor;
+  const LADSPA_Descriptor * descriptor;
+  unsigned long plugin_index;
+  plugin_desc_t * desc, * other_desc = NULL;
+  GSList * list;
+  gboolean exists;
+  int err;
+  
+  /* open the object file */
+  dl_handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
+  if (!dl_handle)
+    {
+      fprintf (stderr, "%s: error opening shared object file '%s': %s\n",
+               __FUNCTION__, filename, dlerror());
+      return;
+    }
+  
+  
+  /* get the get_descriptor function */
+  dlerror (); /* clear the error report */
+  
+  get_descriptor = (LADSPA_Descriptor_Function)
+    dlsym (dl_handle, "ladspa_descriptor");
+  
+  dlerr = dlerror();
+  if (dlerr) {
+    fprintf (stderr, "%s: error finding ladspa_descriptor symbol in object file '%s': %s\n",
+             __FUNCTION__, filename, dlerr);
+    dlclose (dl_handle);
+    return;
+  }
+  
+  plugin_index = 0;
+  while ( (descriptor = get_descriptor (plugin_index)) )
+    {
+      if (!plugin_is_valid (descriptor))
+        {
+          plugin_index++;
+          continue;
+        }
+
+      
+      /* check it doesn't already exist */
+      exists = FALSE;
+      for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+        {
+          other_desc = (plugin_desc_t *) list->data;
+          
+          if (other_desc->id == descriptor->UniqueID)
+            {
+              exists = TRUE;
+              break;
+            }
+        }
+      
+      if (exists)
+        {
+          printf ("Plugin %ld exists in both '%s' and '%s'; using version in '%s'\n",
+                  descriptor->UniqueID, other_desc->object_file, filename, other_desc->object_file);
+          plugin_index++;
+          continue;
+        }
+
+      
+      desc = plugin_desc_new_with_descriptor (filename, plugin_index, descriptor);
+      plugin_mgr->all_plugins = g_slist_append (plugin_mgr->all_plugins, desc);
+      plugin_index++;
+      plugin_mgr->plugin_count++;
+      
+      /* print in the splash screen */
+      /* printf ("Loaded plugin '%s'\n", desc->name); */
+    }
+  
+  err = dlclose (dl_handle);
+  if (err)
+    {
+      fprintf (stderr, "%s: error closing object file '%s': %s\n",
+               __FUNCTION__, filename, dlerror ());
+    }
+}
+
+static void
+plugin_mgr_get_dir_plugins (plugin_mgr_t * plugin_mgr, const char * dir)
+{
+  DIR * dir_stream;
+  struct dirent * dir_entry;
+  char * file_name;
+  int err;
+  size_t dirlen;
+  
+  dir_stream = opendir (dir);
+  if (!dir_stream)
+    {
+/*      fprintf (stderr, "%s: error opening directory '%s': %s\n",
+               __FUNCTION__, dir, strerror (errno)); */
+      return;
+    }
+  
+  dirlen = strlen (dir);
+  
+  while ( (dir_entry = readdir (dir_stream)) )
+    {
+      struct stat info;
+
+      if (strcmp (dir_entry->d_name, ".") == 0 ||
+          strcmp (dir_entry->d_name, "..") == 0)
+        continue;
+  
+      file_name = g_malloc (dirlen + 1 + strlen (dir_entry->d_name) + 1);
+    
+      strcpy (file_name, dir);
+      if (file_name[dirlen - 1] == '/')
+        strcpy (file_name + dirlen, dir_entry->d_name);
+      else
+        {
+          file_name[dirlen] = '/';
+          strcpy (file_name + dirlen + 1, dir_entry->d_name);
+        }
+    
+      stat (file_name, &info);
+      if (S_ISDIR (info.st_mode))
+        plugin_mgr_get_dir_plugins (plugin_mgr, file_name);
+      else
+        plugin_mgr_get_object_file_plugins (plugin_mgr, file_name);
+      
+      g_free (file_name);
+    }
+
+  err = closedir (dir_stream);
+  if (err)
+    fprintf (stderr, "%s: error closing directory '%s': %s\n",
+             __FUNCTION__, dir, strerror (errno));
+}
+
+static void
+plugin_mgr_get_path_plugins (plugin_mgr_t * plugin_mgr)
+{
+  char * ladspa_path, * dir;
+  
+  ladspa_path = g_strdup (getenv ("LADSPA_PATH"));
+  if (!ladspa_path)
+    ladspa_path = g_strdup ("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa");
+  
+  dir = strtok (ladspa_path, ":");
+  do
+    plugin_mgr_get_dir_plugins (plugin_mgr, dir);
+  while ((dir = strtok (NULL, ":")));
+
+  g_free (ladspa_path);
+}
+
+static gint
+plugin_mgr_sort (gconstpointer a, gconstpointer b)
+{
+  const plugin_desc_t * da;
+  const plugin_desc_t * db;
+  da = (const plugin_desc_t *) a;
+  db = (const plugin_desc_t *) b;
+  
+  return strcasecmp (da->name, db->name);
+}
+
+plugin_mgr_t *
+plugin_mgr_new ()
+{
+  plugin_mgr_t * pm;
+  
+  pm = g_malloc (sizeof (plugin_mgr_t));
+  pm->all_plugins = NULL;  
+  pm->plugins = NULL;
+  pm->plugin_count = 0;
+  
+  plugin_mgr_get_path_plugins (pm);
+  
+  if (!pm->all_plugins)
+    {
+      fprintf (stderr, "No LADSPA plugins were found!\n\nCheck your LADSPA_PATH environment variable.\n");
+      abort ();
+    }
+  
+  pm->all_plugins = g_slist_sort (pm->all_plugins, plugin_mgr_sort);
+  
+  return pm;
+}
+
+void
+plugin_mgr_destroy (plugin_mgr_t * plugin_mgr)
+{
+  GSList * list;
+  
+  for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+    plugin_desc_destroy ((plugin_desc_t *) list->data);
+  
+  g_slist_free (plugin_mgr->plugins);
+  g_slist_free (plugin_mgr->all_plugins);
+  free (plugin_mgr);
+}
+
+
+void
+plugin_mgr_set_plugins (plugin_mgr_t * plugin_mgr, unsigned long rack_channels)
+{
+  GSList * list;
+  plugin_desc_t * desc;
+
+  /* clear the current plugins */
+  g_slist_free (plugin_mgr->plugins);
+  plugin_mgr->plugins = NULL;
+  
+  for (list = plugin_mgr->all_plugins; list; list = g_slist_next (list))
+    {
+      desc = (plugin_desc_t *) list->data;
+      
+      if (plugin_desc_get_copies (desc, rack_channels) != 0)
+        plugin_mgr->plugins = g_slist_append (plugin_mgr->plugins, desc);
+    }
+}
+
+static plugin_desc_t *
+plugin_mgr_find_desc (plugin_mgr_t * plugin_mgr, GSList * plugins, unsigned long id)
+{
+  GSList * list;
+  plugin_desc_t * desc;
+  
+  for (list = plugins; list; list = g_slist_next (list))
+    {
+      desc = (plugin_desc_t *) list->data;
+      
+      if (desc->id == id)
+        return desc;
+    }
+  
+  return NULL;
+}
+
+plugin_desc_t *
+plugin_mgr_get_desc (plugin_mgr_t * plugin_mgr, unsigned long id)
+{
+  return plugin_mgr_find_desc (plugin_mgr, plugin_mgr->plugins, id);
+}
+
+plugin_desc_t *
+plugin_mgr_get_any_desc (plugin_mgr_t * plugin_mgr, unsigned long id)
+{
+  return plugin_mgr_find_desc (plugin_mgr, plugin_mgr->all_plugins, id);
+}
+
+
+/* EOF */
diff --git a/src/modules/jackrack/plugin_mgr.h b/src/modules/jackrack/plugin_mgr.h
new file mode 100644 (file)
index 0000000..bc70237
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_MANAGER_H__
+#define __JR_PLUGIN_MANAGER_H__
+
+#include <glib.h>
+
+#include "plugin_desc.h"
+
+typedef struct _plugin_mgr plugin_mgr_t;
+
+struct _plugin_mgr
+{
+  GSList * all_plugins;
+
+  GSList * plugins;
+  unsigned long plugin_count;
+};
+
+struct _ui;
+
+plugin_mgr_t * plugin_mgr_new ();
+void           plugin_mgr_destroy (plugin_mgr_t * plugin_mgr);
+
+void plugin_mgr_set_plugins (plugin_mgr_t * plugin_mgr, unsigned long rack_channels);
+
+plugin_desc_t * plugin_mgr_get_desc (plugin_mgr_t * plugin_mgr, unsigned long id);
+plugin_desc_t * plugin_mgr_get_any_desc (plugin_mgr_t * plugin_mgr, unsigned long id);
+
+#endif /* __JR_PLUGIN_MANAGER_H__ */
diff --git a/src/modules/jackrack/plugin_settings.c b/src/modules/jackrack/plugin_settings.c
new file mode 100644 (file)
index 0000000..7284b80
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <math.h>
+
+#include "plugin_settings.h"
+
+
+static void
+settings_set_to_default (settings_t * settings, guint32 sample_rate)
+{
+  unsigned long control;
+  guint copy;
+  LADSPA_Data value;
+  
+  for (control = 0; control < settings->desc->control_port_count; control++)
+    {
+      value = plugin_desc_get_default_control_value (settings->desc, control, sample_rate);
+
+      for (copy = 0; copy < settings->copies; copy++)
+        {
+          settings->control_values[copy][control] = value;
+        }
+          
+      settings->locks[control] = TRUE;
+    }
+}
+
+settings_t *
+settings_new     (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate)
+{
+  settings_t * settings;
+  unsigned long channel;
+  guint copies;
+  
+  settings = g_malloc (sizeof (settings_t));
+  copies = plugin_desc_get_copies (desc, channels);
+  
+  settings->sample_rate = sample_rate;
+  settings->desc = desc;
+  settings->copies = copies;
+  settings->channels = channels;
+  settings->lock_all = TRUE;
+  settings->enabled = FALSE;
+  settings->locks = NULL;
+  settings->control_values = NULL;
+  settings->wet_dry_enabled = FALSE;
+  settings->wet_dry_locked = TRUE;
+  
+  /* control settings */  
+  if (desc->control_port_count > 0)
+    {
+      guint copy;
+      
+      settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count);
+
+      settings->control_values = g_malloc (sizeof (LADSPA_Data *) * copies);
+      for (copy = 0; copy < copies; copy++)
+        {
+          settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+        }
+      
+      settings_set_to_default (settings, sample_rate);
+    }
+  
+  /* wet/dry settings */
+  settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * channels);
+  for (channel = 0; channel < channels; channel++)
+    settings->wet_dry_values[channel] = 1.0;
+  
+  return settings;
+}
+
+settings_t *
+settings_dup     (settings_t * other)
+{
+  settings_t * settings;
+  plugin_desc_t * desc;
+  unsigned long channel;
+  
+  settings = g_malloc (sizeof (settings_t));
+  
+  settings->sample_rate     = other->sample_rate;
+  settings->desc            = other->desc;
+  settings->copies          = settings_get_copies (other);
+  settings->channels        = settings_get_channels (other);
+  settings->wet_dry_enabled = settings_get_wet_dry_enabled (other);
+  settings->wet_dry_locked  = settings_get_wet_dry_locked (other);
+  settings->lock_all        = settings_get_lock_all (other);
+  settings->enabled         = settings_get_enabled (other);
+  settings->locks           = NULL;
+  settings->control_values  = NULL;
+  
+  desc = other->desc;
+  
+  if (desc->control_port_count > 0)
+    {
+      guint copy;
+      unsigned long control;
+      
+      settings->locks = g_malloc (sizeof (gboolean) * desc->control_port_count);
+      for (control = 0; control < desc->control_port_count; control++)
+        settings_set_lock (settings, control, settings_get_lock (other, control));
+
+      settings->control_values = g_malloc (sizeof (LADSPA_Data *) * settings->copies);
+      for (copy = 0; copy < settings->copies; copy++)
+        {
+          settings->control_values[copy] = g_malloc (sizeof (LADSPA_Data) * desc->control_port_count);
+
+          for (control = 0; control < desc->control_port_count; control++)
+            {
+              settings->control_values[copy][control] = settings_get_control_value (other, copy, control);
+            }
+        }
+    }
+  
+  settings->wet_dry_values = g_malloc (sizeof (LADSPA_Data) * settings->channels);
+  for (channel = 0; channel < settings->channels; channel++)
+    settings->wet_dry_values[channel] = settings_get_wet_dry_value (other, channel);
+  
+  return settings;
+}
+
+void
+settings_destroy (settings_t * settings)
+{
+  if (settings->desc->control_port_count > 0)
+    {
+      guint i;
+      for (i = 0; i < settings->copies; i++)
+        g_free (settings->control_values[i]);
+
+      g_free (settings->control_values);
+      g_free (settings->locks);
+    }
+    
+  g_free (settings->wet_dry_values);
+  
+  g_free (settings);
+}
+
+static void
+settings_set_copies (settings_t * settings, guint copies)
+{
+  guint copy;
+  guint last_copy;
+  unsigned long control;
+  
+  if (copies <= settings->copies)
+    return;
+  
+  last_copy = settings->copies - 1;
+  
+  settings->control_values = g_realloc (settings->control_values,
+                                        sizeof (LADSPA_Data *) * copies);
+  
+  /* copy over the last settings to the new copies */
+  for (copy = settings->copies; copy < copies; copy++)
+    {
+      for (control = 0; control < settings->desc->control_port_count; control++)
+        {
+          settings->control_values[copy][control] = 
+            settings->control_values[last_copy][control];
+        }
+    }
+  
+  settings->copies = copies;
+}
+
+static void
+settings_set_channels (settings_t * settings, unsigned long channels)
+{
+  unsigned long channel;
+  LADSPA_Data last_value;
+      
+  if (channels <= settings->channels)
+    return;
+  
+  settings->wet_dry_values = g_realloc (settings->wet_dry_values, sizeof (LADSPA_Data) * channels);
+  
+  last_value = settings->wet_dry_values[settings->channels - 1];
+  
+  for (channel = settings->channels; channel < channels; channel++)
+    settings->wet_dry_values[channel] = last_value;
+  
+  settings->channels = channels;
+}
+
+void
+settings_set_sample_rate (settings_t * settings, guint32 sample_rate)
+{
+  LADSPA_Data old_sample_rate;
+  LADSPA_Data new_sample_rate;
+
+  g_return_if_fail (settings != NULL);
+  
+  if (settings->sample_rate == sample_rate)
+    return;
+  
+  if (settings->desc->control_port_count > 0)
+    {
+      unsigned long control;
+      guint copy;
+      
+      new_sample_rate = (LADSPA_Data) sample_rate;
+      old_sample_rate = (LADSPA_Data) settings->sample_rate;
+
+      for (control = 0; control < settings->desc->control_port_count; control++)
+        {
+          for (copy = 0; copy < settings->copies; copy++)
+            {
+              if (LADSPA_IS_HINT_SAMPLE_RATE (settings->desc->port_range_hints[control].HintDescriptor))
+                {
+                  settings->control_values[copy][control] =
+                    (settings->control_values[copy][control] / old_sample_rate) * new_sample_rate;
+                }
+            }
+        }
+    }
+  
+  settings->sample_rate = sample_rate;
+}
+
+void
+settings_set_control_value (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value)
+{
+  g_return_if_fail (settings != NULL);
+  g_return_if_fail (control_index < settings->desc->control_port_count);
+  
+  if (copy >= settings->copies)
+    settings_set_copies (settings, copy + 1);
+  
+  settings->control_values[copy][control_index] = value;
+}
+
+void
+settings_set_lock          (settings_t * settings, unsigned long control_index, gboolean locked)
+{
+  g_return_if_fail (settings != NULL);
+  g_return_if_fail (control_index < settings->desc->control_port_count);
+  
+  settings->locks[control_index] = locked;
+}
+
+void
+settings_set_lock_all (settings_t * settings, gboolean lock_all)
+{
+  g_return_if_fail (settings != NULL);
+
+  settings->lock_all = lock_all;
+}
+
+void
+settings_set_enabled (settings_t * settings, gboolean enabled)
+{
+  g_return_if_fail (settings != NULL);
+
+  settings->enabled = enabled;
+}
+
+void
+settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled)
+{
+  g_return_if_fail (settings != NULL);
+  
+  settings->wet_dry_enabled = enabled;
+}
+
+void
+settings_set_wet_dry_locked  (settings_t * settings, gboolean locked)
+{
+  g_return_if_fail (settings != NULL);
+  
+  settings->wet_dry_locked = locked;
+}
+
+void
+settings_set_wet_dry_value   (settings_t * settings, unsigned long channel, LADSPA_Data value)
+{
+  g_return_if_fail (settings != NULL);
+
+  if (channel >= settings->channels)
+    settings_set_channels (settings, channel + 1);
+  
+  settings->wet_dry_values[channel] = value;
+}
+
+
+LADSPA_Data
+settings_get_control_value (settings_t * settings, guint copy, unsigned long control_index)
+{
+  g_return_val_if_fail (settings != NULL, NAN);
+  g_return_val_if_fail (control_index < settings->desc->control_port_count, NAN);
+
+  if (copy >= settings->copies)
+    settings_set_copies (settings, copy - 1);
+
+  return settings->control_values[copy][control_index];
+}
+
+gboolean
+settings_get_lock          (const settings_t * settings, unsigned long control_index)
+{
+  g_return_val_if_fail (settings != NULL, FALSE);
+  
+  return settings->locks[control_index]; 
+}
+
+gboolean
+settings_get_lock_all      (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, FALSE);
+
+  return settings->lock_all;
+}
+
+gboolean
+settings_get_enabled      (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, FALSE);
+  
+  return settings->enabled;
+}
+
+guint
+settings_get_copies        (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, 0);
+  
+  return settings->copies;
+}
+
+
+unsigned long
+settings_get_channels        (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, 0);
+  
+  return settings->channels;
+}
+
+gboolean
+settings_get_wet_dry_enabled (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, FALSE);
+
+  return settings->wet_dry_enabled;
+}
+
+gboolean
+settings_get_wet_dry_locked  (const settings_t * settings)
+{
+  g_return_val_if_fail (settings != NULL, FALSE);
+  
+  return settings->wet_dry_locked;
+}
+
+LADSPA_Data
+settings_get_wet_dry_value   (settings_t * settings, unsigned long channel)
+{
+  g_return_val_if_fail (settings != NULL, NAN);
+
+  if (channel >= settings->channels)
+    settings_set_channels (settings, channel + 1);
+  
+  return settings->wet_dry_values[channel];
+}
+
+
+
+/* EOF */
diff --git a/src/modules/jackrack/plugin_settings.h b/src/modules/jackrack/plugin_settings.h
new file mode 100644 (file)
index 0000000..852333b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JR_PLUGIN_SETTINGS_H__
+#define __JR_PLUGIN_SETTINGS_H__
+
+#include <glib.h>
+#include <ladspa.h>
+
+#include "plugin_mgr.h"
+#include "plugin_desc.h"
+
+typedef struct _settings settings_t;
+
+struct _settings
+{
+  guint32   sample_rate;
+  plugin_desc_t *  desc;
+  guint            copies;
+  LADSPA_Data **   control_values;
+  gboolean *       locks;
+  gboolean         lock_all;
+  gboolean         enabled;
+  unsigned long    channels;
+  gboolean         wet_dry_enabled;
+  gboolean         wet_dry_locked;
+  LADSPA_Data *    wet_dry_values;
+};
+
+settings_t * settings_new     (plugin_desc_t * desc, unsigned long channels, guint32 sample_rate);
+settings_t * settings_dup     (settings_t * settings);
+void         settings_destroy (settings_t * settings);
+
+void settings_set_control_value   (settings_t * settings, guint copy, unsigned long control_index, LADSPA_Data value);
+void settings_set_lock            (settings_t * settings, unsigned long control_index, gboolean locked);
+void settings_set_lock_all        (settings_t * settings, gboolean lock_all);
+void settings_set_enabled         (settings_t * settings, gboolean enabled);
+void settings_set_wet_dry_enabled (settings_t * settings, gboolean enabled);
+void settings_set_wet_dry_locked  (settings_t * settings, gboolean locked);
+void settings_set_wet_dry_value   (settings_t * settings, unsigned long channel, LADSPA_Data value);
+
+LADSPA_Data   settings_get_control_value   (settings_t * settings, guint copy, unsigned long control_index);
+gboolean      settings_get_lock            (const settings_t * settings, unsigned long control_index);
+gboolean      settings_get_lock_all        (const settings_t * settings);
+gboolean      settings_get_enabled         (const settings_t * settings);
+guint         settings_get_copies          (const settings_t * settings);
+unsigned long settings_get_channels        (const settings_t * settings);
+gboolean      settings_get_wet_dry_enabled (const settings_t * settings);
+gboolean      settings_get_wet_dry_locked  (const settings_t * settings);
+LADSPA_Data   settings_get_wet_dry_value   (settings_t * settings, unsigned long channel);
+
+void settings_set_sample_rate (settings_t * settings, guint32 sample_rate);
+
+#endif /* __JR_PLUGIN_SETTINGS_H__ */
diff --git a/src/modules/jackrack/process.c b/src/modules/jackrack/process.c
new file mode 100644 (file)
index 0000000..efd497f
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <jack/jack.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "process.h"
+#include "lock_free_fifo.h"
+#include "plugin.h"
+#include "jack_rack.h"
+
+#ifndef _
+#define _(x) x
+#endif
+
+#define USEC_PER_SEC         1000000
+#define MSEC_PER_SEC         1000
+#define TIME_RUN_SKIP_COUNT  5
+#define MAX_BUFFER_SIZE      4096
+
+jack_nframes_t sample_rate;
+jack_nframes_t buffer_size;
+
+static void
+jack_shutdown_cb (void * data)
+{
+  process_info_t * procinfo = data;
+  
+  procinfo->quit = TRUE;
+}
+
+/** process messages for plugins' control ports */
+void process_control_port_messages (process_info_t * procinfo) {
+  plugin_t * plugin;
+  unsigned long control;
+  unsigned long channel;
+  gint copy;
+  
+  if (!procinfo->chain) return;
+  
+  for (plugin = procinfo->chain; plugin; plugin = plugin->next)
+    {
+      if (plugin->desc->control_port_count > 0)
+        for (control = 0; control < plugin->desc->control_port_count; control++)
+          for (copy = 0; copy < plugin->copies; copy++)
+            {
+              while (lff_read (plugin->holders[copy].ui_control_fifos + control,
+                               plugin->holders[copy].control_memory + control) == 0);
+            }
+      
+      if (plugin->wet_dry_enabled)
+        for (channel = 0; channel < procinfo->channels; channel++)
+          {
+            while (lff_read (plugin->wet_dry_fifos + channel,
+                             plugin->wet_dry_values + channel) == 0);
+          }
+    }
+}
+
+int get_jack_buffers (process_info_t * procinfo, jack_nframes_t frames) {
+  unsigned long channel;
+  
+  for (channel = 0; channel < procinfo->channels; channel++)
+    {
+      procinfo->jack_input_buffers[channel] = jack_port_get_buffer (procinfo->jack_input_ports[channel], frames);
+      if (!procinfo->jack_input_buffers[channel])
+        {
+          fprintf (stderr, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel);
+          return 1;
+        }
+
+      procinfo->jack_output_buffers[channel] = jack_port_get_buffer (procinfo->jack_output_ports[channel], frames);
+      if (!procinfo->jack_output_buffers[channel])
+        {
+          fprintf (stderr, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel);
+          return 1;
+        }
+    }
+
+  return 0;
+}
+
+plugin_t *
+get_first_enabled_plugin (process_info_t * procinfo)
+{
+  plugin_t * first_enabled;
+  
+  if (!procinfo->chain) return NULL;
+
+  for (first_enabled = procinfo->chain;
+       first_enabled;
+       first_enabled = first_enabled->next)
+    {
+      if (first_enabled->enabled) return first_enabled;
+    }
+  return NULL;
+}
+
+plugin_t *
+get_last_enabled_plugin (process_info_t * procinfo)
+{
+  plugin_t * last_enabled;
+  
+  if (!procinfo->chain) return NULL;
+
+  for (last_enabled = procinfo->chain_end;
+       last_enabled;
+       last_enabled = last_enabled->prev)
+    {
+      if (last_enabled->enabled) return last_enabled;
+    }
+  
+  return NULL;
+}
+
+void
+connect_chain (process_info_t * procinfo, jack_nframes_t frames)
+{
+  plugin_t * first_enabled, * last_enabled, * plugin;
+  gint copy;
+  unsigned long channel;
+  if (!procinfo->chain) return;
+  
+  first_enabled = get_first_enabled_plugin (procinfo);
+  if (!first_enabled) return;
+  
+  last_enabled = get_last_enabled_plugin (procinfo);
+  
+  /* sort out the aux ports */
+  plugin = first_enabled;
+  do
+    {
+      if (plugin->desc->aux_channels > 0 && plugin->enabled)
+        {
+          if (procinfo->jack_client)
+            {
+              for (copy = 0; copy < plugin->copies; copy++)
+                for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+                  plugin->descriptor->
+                    connect_port (plugin->holders[copy].instance,
+                                  plugin->desc->audio_aux_port_indicies[channel],
+                                  jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames));
+            }
+          else
+            {
+              for (copy = 0; copy < frames; copy++)
+                procinfo->silent_buffer[copy] = 0.0;
+
+              for (copy = 0; copy < plugin->copies; copy++)
+                for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+                  plugin->descriptor->
+                    connect_port (plugin->holders[copy].instance,
+                                  plugin->desc->audio_aux_port_indicies[channel],
+                                  procinfo->silent_buffer);
+            }
+        }
+    }
+  while ( (plugin != last_enabled) && (plugin = plugin->next) );
+
+  /* ensure that all the of the enabled plugins are connected to their memory */
+  plugin_connect_output_ports (first_enabled);
+  if (first_enabled != last_enabled)
+    {
+      plugin_connect_input_ports (last_enabled, last_enabled->prev->audio_output_memory);
+      for (plugin = first_enabled->next; plugin; plugin = plugin->next)
+        {
+          if (plugin->enabled)
+            {
+              plugin_connect_input_ports (plugin, plugin->prev->audio_output_memory);
+              plugin_connect_output_ports (plugin);
+            }
+        }
+    }
+
+  /* input buffers for first plugin */
+  plugin_connect_input_ports (first_enabled, procinfo->jack_input_buffers);
+}
+
+void
+process_chain (process_info_t * procinfo, jack_nframes_t frames)
+{
+  plugin_t * first_enabled;
+  plugin_t * last_enabled = NULL;
+  plugin_t * plugin;
+  unsigned long channel;
+  unsigned long i;
+
+  if (procinfo->jack_client)
+    {
+      LADSPA_Data zero_signal[frames];
+      guint copy;
+
+      /* set the zero signal to zero */
+      for (channel = 0; channel < frames; channel++)
+        zero_signal[channel] = 0.0;
+    
+      /* possibly set aux output channels to zero if they're not enabled */
+      for (plugin = procinfo->chain; plugin; plugin = plugin->next)
+        if (!plugin->enabled &&
+            plugin->desc->aux_channels > 0 &&
+            !plugin->desc->aux_are_input)
+          for (copy = 0; copy < plugin->copies; copy++)
+            for (channel = 0; channel < plugin->desc->aux_channels; channel++)
+              memcpy (jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames),
+                      zero_signal, sizeof (LADSPA_Data) * frames);
+    }
+
+  first_enabled = get_first_enabled_plugin (procinfo);
+  
+  /* no chain; just copy input to output */
+  if (!procinfo->chain || !first_enabled)
+    {
+      unsigned long channel;
+      for (channel = 0; channel < procinfo->channels; channel++)
+        {
+          memcpy (procinfo->jack_output_buffers[channel],
+                  procinfo->jack_input_buffers[channel],
+                  sizeof(LADSPA_Data) * frames);
+        }
+      return;
+    }
+  
+  /* all past here is guaranteed to have at least 1 enabled plugin */
+
+  last_enabled = get_last_enabled_plugin (procinfo);
+  
+  for (plugin = first_enabled;
+       plugin;
+       plugin = plugin->next)
+    {
+      if (plugin->enabled)
+        {
+          for (i = 0; i < plugin->copies; i++)
+            plugin->descriptor->run (plugin->holders[i].instance, frames);
+          
+          if (plugin->wet_dry_enabled)
+            for (channel = 0; channel < procinfo->channels; channel++)
+              for (i = 0; i < frames; i++)
+                {
+                  plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel];
+                  plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]);
+                }
+          
+          if (plugin == last_enabled)
+            break;
+        }
+      else
+        {
+    
+          /* copy the data through */
+          for (i = 0; i < procinfo->channels; i++)
+            memcpy (plugin->audio_output_memory[i],
+                    plugin->prev->audio_output_memory[i],
+                    sizeof(LADSPA_Data) * frames);
+        }
+    }
+  
+  /* copy the last enabled data to the jack ports */
+  for (i = 0; i < procinfo->channels; i++)
+    memcpy (procinfo->jack_output_buffers[i],
+            last_enabled->audio_output_memory[i],
+            sizeof(LADSPA_Data) * frames);
+  
+}
+
+int process_ladspa (process_info_t * procinfo, jack_nframes_t frames,
+                    LADSPA_Data ** inputs, LADSPA_Data ** outputs) {
+  unsigned long channel;
+  
+  if (!procinfo)
+    {
+      fprintf (stderr, "%s: no process_info from jack!\n", __FUNCTION__);
+      return 1;
+    }
+  
+  if (procinfo->quit == TRUE)
+    return 1;
+  
+  process_control_port_messages (procinfo);
+  
+  for (channel = 0; channel < procinfo->channels; channel++)
+    {
+      procinfo->jack_input_buffers[channel] = inputs[channel];
+      if (!procinfo->jack_input_buffers[channel])
+        {
+          fprintf (stderr, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel);
+          return 1;
+        }
+
+      procinfo->jack_output_buffers[channel] = outputs[channel];
+      if (!procinfo->jack_output_buffers[channel])
+        {
+          fprintf (stderr, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel);
+          return 1;
+        }
+    }
+  
+  connect_chain (procinfo, frames);
+  
+  process_chain (procinfo, frames);
+  
+  return 0;
+}
+
+int process_jack (jack_nframes_t frames, void * data) {
+  int err;
+  process_info_t * procinfo;
+  
+  procinfo = (process_info_t *) data;
+  
+  if (!procinfo)
+    {
+      fprintf (stderr, "%s: no process_info from jack!\n", __FUNCTION__);
+      return 1;
+    }
+  
+  if (procinfo->port_count == 0)
+    return 0;
+  
+  if (procinfo->quit == TRUE)
+    return 1;
+  
+  process_control_port_messages (procinfo);
+  
+  err = get_jack_buffers (procinfo, frames);
+  if (err)
+    {
+      fprintf(stderr, "%s: failed to get jack ports, not processing\n", __FUNCTION__);
+      return 0;
+    }
+  
+  connect_chain (procinfo, frames);
+  
+  process_chain (procinfo, frames);
+  
+  return 0;
+}
+
+
+
+/*******************************************
+ ************** non RT stuff ***************
+ *******************************************/
+static int
+process_info_connect_jack (process_info_t * procinfo)
+{
+  printf (_("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name);
+
+  procinfo->jack_client = jack_client_new (procinfo->jack_client_name);
+
+  if (!procinfo->jack_client)
+    {
+      fprintf (stderr, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__);
+      return 1;
+    }
+
+  printf (_("Connected to JACK server\n"));
+
+  jack_set_process_callback (procinfo->jack_client, process_jack, procinfo);
+  jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo);
+                                            
+  return 0;
+}
+
+static void
+process_info_connect_port (process_info_t * procinfo,
+                           gshort in,
+                           unsigned long port_index,
+                           const char * port_name)
+{
+  const char ** jack_ports;
+  unsigned long jack_port_index;
+  int err;
+  char * full_port_name;
+  
+  jack_ports = jack_get_ports (procinfo->jack_client, NULL, NULL,
+                               JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput));
+  
+  if (!jack_ports)
+    return;
+  
+  for (jack_port_index = 0;
+       jack_ports[jack_port_index] && jack_port_index <= port_index;
+       jack_port_index++)
+    {
+      if (jack_port_index != port_index)
+        continue;
+        
+      full_port_name = g_strdup_printf ("%s:%s", procinfo->jack_client_name, port_name);
+
+      printf (_("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]);
+
+      err = jack_connect (procinfo->jack_client,
+                          in ? jack_ports[jack_port_index] : full_port_name,
+                          in ? full_port_name : jack_ports[jack_port_index]);
+
+      if (err)
+        fprintf (stderr, "%s: error connecting ports '%s' and '%s'\n",
+                 __FUNCTION__, full_port_name, jack_ports[jack_port_index]);
+      else
+        printf (_("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]);
+      
+      free (full_port_name);
+    }
+  
+  free (jack_ports);
+}
+
+int
+process_info_set_port_count (process_info_t * procinfo,
+       unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs)
+{
+  unsigned long i;
+  char * port_name;
+  jack_port_t ** port_ptr;
+  gshort in;
+  
+  if (procinfo->port_count >= port_count)
+      return -1;
+  
+  if (procinfo->port_count == 0)
+    {
+      procinfo->jack_input_ports = g_malloc (sizeof (jack_port_t *) * port_count);
+      procinfo->jack_output_ports = g_malloc (sizeof (jack_port_t *) * port_count);
+      
+      procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count);
+      procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count);
+    }
+  else
+    {
+      procinfo->jack_input_ports = g_realloc (procinfo->jack_input_ports, sizeof (jack_port_t *) * port_count);
+      procinfo->jack_output_ports = g_realloc (procinfo->jack_output_ports, sizeof (jack_port_t *) * port_count);
+
+      procinfo->jack_input_buffers = g_realloc (procinfo->jack_input_buffers, sizeof (LADSPA_Data *) * port_count);
+      procinfo->jack_output_buffers = g_realloc (procinfo->jack_output_buffers, sizeof (LADSPA_Data *) * port_count);
+    }
+  
+  for (i = procinfo->port_count; i < port_count; i++)
+    {
+    for (in = 0; in < 2; in++)
+      {
+        port_name = g_strdup_printf ("%s_%ld", in ? "in" : "out", i + 1);
+       
+        //printf (_("Creating %s port %s\n"), in ? "input" : "output", port_name);
+        
+        port_ptr = (in ? &procinfo->jack_input_ports[i]
+                       : &procinfo->jack_output_ports[i]);
+        
+        *port_ptr =  jack_port_register (procinfo->jack_client,
+                                         port_name,
+                                         JACK_DEFAULT_AUDIO_TYPE,
+                                         in ? JackPortIsInput : JackPortIsOutput,
+                                         0);
+        
+        if (!*port_ptr)
+          {
+            fprintf (stderr, "%s: could not register port '%s'; aborting\n",
+                     __FUNCTION__, port_name);
+            return 1;
+          }
+
+        //printf (_("Created %s port %s\n"), in ? "input" : "output", port_name);
+        
+        if ((in && connect_inputs) || (!in && connect_outputs))
+          process_info_connect_port (procinfo, in, i, port_name);
+        
+        g_free (port_name);
+      }
+    }
+  
+  procinfo->port_count = port_count;
+
+  return 0;
+}
+
+void
+process_info_set_channels (process_info_t * procinfo,
+       unsigned long channels, gboolean connect_inputs, gboolean connect_outputs)
+{
+  process_info_set_port_count (procinfo, channels, connect_inputs, connect_outputs);
+  procinfo->channels = channels; 
+}
+
+process_info_t *
+process_info_new (const char * client_name, unsigned long rack_channels, 
+       gboolean connect_inputs, gboolean connect_outputs)
+{
+  process_info_t * procinfo;
+  char * jack_client_name;
+  int err;
+
+  procinfo = g_malloc (sizeof (process_info_t));
+  
+  procinfo->chain = NULL;
+  procinfo->chain_end = NULL;
+  procinfo->jack_client = NULL;
+  procinfo->port_count = 0;
+  procinfo->jack_input_ports = NULL;
+  procinfo->jack_output_ports = NULL;
+  procinfo->channels = rack_channels;
+  procinfo->quit = FALSE;
+       
+  if ( client_name == NULL )
+    {
+      sample_rate = 48000; // should be set externally before calling process_ladspa
+      buffer_size = MAX_BUFFER_SIZE;
+      procinfo->silent_buffer = g_malloc (sizeof (LADSPA_Data) * buffer_size );
+      procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels);
+      procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels);
+
+      return procinfo;
+    }
+
+  /* sort out the client name */
+  procinfo->jack_client_name = jack_client_name = strdup (client_name);
+  for (err = 0; jack_client_name[err] != '\0'; err++)
+    {
+      if (jack_client_name[err] == ' ')
+        jack_client_name[err] = '_';
+      else if (!isalnum (jack_client_name[err]))
+        { /* shift all the chars up one (to remove the non-alphanumeric char) */
+          int i;
+          for (i = err; jack_client_name[i] != '\0'; i++)
+            jack_client_name[i] = jack_client_name[i + 1];
+        }
+      else if (isupper (jack_client_name[err]))
+        jack_client_name[err] = tolower (jack_client_name[err]);
+    }
+  
+  err = process_info_connect_jack (procinfo);
+  if (err)
+    {
+/*      g_free (procinfo); */
+      return NULL;
+/*      abort (); */
+    }
+  
+  sample_rate = jack_get_sample_rate (procinfo->jack_client);
+  buffer_size = jack_get_sample_rate (procinfo->jack_client);
+  
+  jack_set_process_callback (procinfo->jack_client, process_jack, procinfo);
+  jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo);
+  
+  jack_activate (procinfo->jack_client);
+
+  err = process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs);
+  if (err)
+    return NULL;
+
+  return procinfo;
+}
+
+void
+process_info_destroy (process_info_t * procinfo) {
+  if (procinfo->jack_client)
+    {
+      jack_deactivate (procinfo->jack_client);
+      jack_client_close (procinfo->jack_client);
+    }
+  g_free (procinfo->silent_buffer);
+  g_free (procinfo->jack_input_ports);
+  g_free (procinfo->jack_output_ports);
+  g_free (procinfo->jack_input_buffers);
+  g_free (procinfo->jack_output_buffers);
+  g_free (procinfo);
+}
+
+void process_quit (process_info_t * procinfo) {
+  procinfo->quit = TRUE;
+}
diff --git a/src/modules/jackrack/process.h b/src/modules/jackrack/process.h
new file mode 100644 (file)
index 0000000..8b0c365
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * JACK Rack
+ *
+ * Original:
+ * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net)
+ *
+ * Modification for MLT:
+ * Copyright (C) 2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __JLH_PROCESS_H__
+#define __JLH_PROCESS_H__
+
+#include <glib.h>
+#include <jack/jack.h>
+#include <ladspa.h>
+
+#include "lock_free_fifo.h"
+
+typedef struct _process_info process_info_t;
+
+/** this is what gets passed to the process() callback and contains all
+    the data the process callback will need */
+struct _process_info {
+
+  /** the plugin instance chain */
+  struct _plugin * chain;
+  struct _plugin * chain_end;
+  
+  jack_client_t * jack_client;
+  unsigned long port_count;
+  jack_port_t ** jack_input_ports;
+  jack_port_t ** jack_output_ports;
+
+  unsigned long channels;
+  LADSPA_Data ** jack_input_buffers;
+  LADSPA_Data ** jack_output_buffers;
+  LADSPA_Data *  silent_buffer;
+  
+  char * jack_client_name;
+  int quit;
+};
+
+extern jack_nframes_t sample_rate;
+extern jack_nframes_t buffer_size;
+
+process_info_t * process_info_new (const char * client_name,
+       unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs);
+void process_info_destroy (process_info_t * procinfo);
+
+void process_info_set_channels (process_info_t * procinfo,
+       unsigned long channels, gboolean connect_inputs, gboolean connect_outputs);
+
+int process_ladspa (process_info_t * procinfo, jack_nframes_t frames,
+                    LADSPA_Data ** inputs, LADSPA_Data ** outputs);
+
+int process_jack (jack_nframes_t frames, void * data);
+
+void process_quit (process_info_t * procinfo);
+
+#endif /* __JLH_PROCESS_H__ */
diff --git a/src/modules/kdenlive/Makefile b/src/modules/kdenlive/Makefile
new file mode 100644 (file)
index 0000000..d32ab0c
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltkdenlive$(LIBSUF)
+
+OBJS = factory.o \
+          filter_boxblur.o \
+          filter_freeze.o \
+          filter_wave.o \
+          producer_framebuffer.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/kdenlive/factory.c b/src/modules/kdenlive/factory.c
new file mode 100644 (file)
index 0000000..80914c7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2007 Jean-Baptiste Mardelle
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_boxblur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_freeze_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_wave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "boxblur", filter_boxblur_init );
+       MLT_REGISTER( filter_type, "freeze", filter_freeze_init );
+       MLT_REGISTER( filter_type, "wave", filter_wave_init );
+       MLT_REGISTER( producer_type, "framebuffer", producer_framebuffer_init );
+}
diff --git a/src/modules/kdenlive/filter_boxblur.c b/src/modules/kdenlive/filter_boxblur.c
new file mode 100644 (file)
index 0000000..f1d5425
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * filter_boxblur.c -- blur filter
+ * Copyright (C) ?-2007 Leny Grisel <leny.grisel@laposte.net>
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+static void PreCompute(uint8_t *yuv, int32_t *rgb, int width, int height)
+{
+       register int x, y, z;
+       register int uneven = width % 2;
+       int w = (width - uneven ) / 2;
+       int yy, uu, vv;
+       int r, g, b;
+       int32_t pts[3];
+       for (y=0; y<height; y++)
+       {
+               for (x=0; x<w; x++)
+               {
+                       uu = yuv[1];
+                       vv = yuv[3];
+                       yy = yuv[0];
+                       YUV2RGB(yy, uu, vv, r, g, b);
+                       pts[0] = r;
+                       pts[1] = g;
+                       pts[2] = b;
+                       for (z = 0; z < 3; z++) 
+                       {
+                               if (x>0) pts[z]+=rgb[-3];
+                               if (y>0) pts[z]+=rgb[-(width*3)];
+                               if (x>0 && y>0) pts[z]-=rgb[-((width+1)*3)];
+                               *rgb++=pts[z];
+                       }
+
+                       yy = yuv[2];
+                       YUV2RGB(yy, uu, vv, r, g, b);
+                       pts[0] = r;
+                       pts[1] = g;
+                       pts[2] = b;
+                       for (z = 0; z < 3; z++)
+                       {
+                               pts[z]+=rgb[-3];
+                               if (y>0)
+                               {
+                                       pts[z]+=rgb[-(width*3)];
+                                       pts[z]-=rgb[-((width+1)*3)];
+                               }
+                               *rgb++=pts[z];
+                       }
+                       yuv += 4;
+               }
+               if (uneven) 
+               {
+                       uu = yuv[1];
+                       vv = yuv[3];
+                       yy = yuv[0];
+                       YUV2RGB(yy, uu, vv, r, g, b);
+                       pts[0] = r;
+                       pts[1] = g;
+                       pts[2] = b;
+                       for (z = 0; z < 3; z++)
+                       {
+                               pts[z]+=rgb[-3];
+                               if (y>0)
+                               {
+                                       pts[z]+=rgb[-(width*3)];
+                                       pts[z]-=rgb[-((width+1)*3)];
+                               }
+                               *rgb++=pts[z];
+                       }
+                       yuv += 2;
+               }
+       }
+}
+
+static int32_t GetRGB(int32_t *rgb, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z)
+{
+       int xtheo = x * 2 + offsetx;
+       int ytheo = y + offsety;
+       if (xtheo < 0) xtheo = 0; else if (xtheo >= w) xtheo = w - 1;
+       if (ytheo < 0) ytheo = 0; else if (ytheo >= h) ytheo = h - 1;
+       return rgb[3*(xtheo+ytheo*w)+z];
+}
+
+static int32_t GetRGB2(int32_t *rgb, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z)
+{
+       int xtheo = x * 2 + 1 + offsetx;
+       int ytheo = y + offsety;
+       if (xtheo < 0) xtheo = 0; else if (xtheo >= w) xtheo = w - 1;
+       if (ytheo < 0) ytheo = 0; else if (ytheo >= h) ytheo = h - 1;
+       return rgb[3*(xtheo+ytheo*w)+z];
+}
+
+static void DoBoxBlur(uint8_t *yuv, int32_t *rgb, unsigned int width, unsigned int height, unsigned int boxw, unsigned int boxh)
+{
+       register int x, y;
+       int32_t r, g, b;
+       register int uneven = width % 2;
+       register int y0, y1, u0, u1, v0, v1;
+       int w = (width - uneven ) / 2;
+       float mul = 1.f / ((boxw*2) * (boxh*2));
+
+       for (y = 0; y < height; y++)
+       {
+               for (x = 0; x < w; x++)
+               {
+                       r = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB(rgb, width, height, x, -boxw, y, + boxh, 0) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 0);
+                       g = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 1);
+                       b = GetRGB(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 2);
+                       r = (int32_t) (r * mul);
+                       g = (int32_t) (g * mul);
+                       b = (int32_t) (b * mul);
+                       RGB2YUV (r, g, b, y0, u0, v0);
+
+                       r = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 0) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 0);
+                       g = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 1);
+                       b = GetRGB2(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB2(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB2(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB2(rgb, width, height, x, +boxw, y, -boxh, 2);
+                       r = (int32_t) (r * mul);
+                       g = (int32_t) (g * mul);
+                       b = (int32_t) (b * mul);
+                       RGB2YUV (r, g, b, y1, u1, v1);
+                       *yuv++ = y0;
+                       *yuv++ = (u0+u1) >> 1;
+                       *yuv++ = y1;
+                       *yuv++ = (v0+v1) >> 1;
+               }
+               if (uneven)
+               {
+                       r =  GetRGB(rgb, width, height, x, +boxw, y, +boxh, 0) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 0) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 0) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 0);
+                       g =  GetRGB(rgb, width, height, x, +boxw, y, +boxh, 1) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 1) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 1) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 1);
+                       b =  GetRGB(rgb, width, height, x, +boxw, y, +boxh, 2) + GetRGB(rgb, width, height, x, -boxw, y, -boxh, 2) - GetRGB(rgb, width, height, x, -boxw, y, +boxh, 2) - GetRGB(rgb, width, height, x, +boxw, y, -boxh, 2);
+                       r = (int32_t) (r * mul);
+                       g = (int32_t) (g * mul);
+                       b = (int32_t) (b * mul);
+                       RGB2YUV (r, g, b, y0, u0, v0);
+                       *yuv++ = mul * y0;
+                       *yuv++ = mul * u0;
+               }
+       }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       short hori = mlt_properties_get_int(MLT_FRAME_PROPERTIES( this ), "hori" );
+       short vert = mlt_properties_get_int(MLT_FRAME_PROPERTIES( this ), "vert" );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               double factor = mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "boxblur" );
+               if (factor != 0) {
+                       int h = *height + 1;
+                       int32_t *rgb = mlt_pool_alloc (3 * *width * h * sizeof(int32_t));
+                       PreCompute (*image, rgb, *width, h);
+                       DoBoxBlur (*image, rgb, *width, h, (int) factor*hori, (int) factor*vert);
+                       mlt_pool_release (rgb);
+               }
+       }
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the starting blur level
+       double blur = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "start" );
+       short hori = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "hori" );
+       short vert = mlt_properties_get_int(MLT_FILTER_PROPERTIES( this ), "vert" );
+
+       // If there is an end adjust gain to the range
+       if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+       {
+               // Determine the time position of this frame in the transition duration
+               mlt_position in = mlt_filter_get_in( this );
+               mlt_position out = mlt_filter_get_out( this );
+               mlt_position time = mlt_frame_get_position( frame );
+               double position = (double) ( time - in ) / ( out - in + 1.0 );
+               double end = (double) mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "end" );
+               blur += ( end - blur ) * position;
+       }
+
+       // Push the frame filter
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "boxblur", blur );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "hori", hori );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "vert", vert );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_boxblur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "10" : arg);
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "hori", arg == NULL ? "1" : arg);
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "vert", arg == NULL ? "1" : arg);
+       }
+       return this;
+}
+
+
+
diff --git a/src/modules/kdenlive/filter_freeze.c b/src/modules/kdenlive/filter_freeze.c
new file mode 100644 (file)
index 0000000..ef85423
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * filter_freeze.c -- simple frame freezing filter
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@kdenlive.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_service.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_property.h>
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the image
+       mlt_filter filter = mlt_frame_pop_service( this );
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+       mlt_frame freeze_frame = NULL;;
+       int freeze_before = mlt_properties_get_int( properties, "freeze_before" );
+       int freeze_after = mlt_properties_get_int( properties, "freeze_after" );
+       mlt_position pos = mlt_properties_get_position( properties, "frame" );
+       mlt_position currentpos = mlt_properties_get_position( properties, "_seek_frame" );
+
+       int do_freeze = 0;
+       if (freeze_before == 0 && freeze_after == 0) {
+               do_freeze = 1;
+       } else if (freeze_before != 0 && pos > currentpos) {
+               do_freeze = 1;
+       } else if (freeze_after != 0 && pos < currentpos) {
+               do_freeze = 1;
+       }
+
+       if (do_freeze == 1) {
+               freeze_frame = mlt_properties_get_data( properties, "freeze_frame", NULL );
+
+               if( freeze_frame == NULL || mlt_properties_get_position( properties, "_frame" ) != pos )
+               {
+                       // freeze_frame has not been fetched yet, so fetch it and cache it.
+                       mlt_producer producer = mlt_frame_get_original_producer(this);
+                       mlt_producer_seek( producer, pos );
+           
+                       // Get the frame
+                       mlt_service_get_frame( mlt_producer_service(producer), &freeze_frame, 0 );
+
+                       mlt_properties props = MLT_FRAME_PROPERTIES( this );
+                       mlt_properties freeze_properties = MLT_FRAME_PROPERTIES( freeze_frame );
+                       mlt_properties_set_double( freeze_properties, "consumer_aspect_ratio", mlt_properties_get_double( props, "consumer_aspect_ratio" ) );
+                       mlt_properties_set( freeze_properties, "rescale.interp", mlt_properties_get( props, "rescale.interp" ) );
+                       mlt_properties_set_double( freeze_properties, "aspect_ratio", mlt_frame_get_aspect_ratio( this ) );
+                       mlt_properties_set_int( freeze_properties, "progressive", mlt_properties_get_int( props, "progressive" ) );
+
+                       mlt_properties_set_data( properties, "freeze_frame", freeze_frame, 0, NULL, NULL );
+                       mlt_properties_set_position( properties, "_frame", pos );
+               }
+               int error = mlt_frame_get_image( freeze_frame, image, format, width, height, 1 );
+               return error;
+       }
+
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       // Push the filter on to the stack
+       mlt_frame_push_service( frame, this );
+
+       // Determine the time position of this frame
+       mlt_properties_set_position( MLT_FILTER_PROPERTIES( this ), "_seek_frame", mlt_frame_get_position( frame ) -  mlt_filter_get_in( this ) );
+
+       // Push the frame filter
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_freeze_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               // Set the frame which will be chosen for freeze
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "frame", "0" );
+
+               // If freeze_after = 1, only frames after the "frame" value will be frozen
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "freeze_after", "0" );
+
+               // If freeze_before = 1, only frames after the "frame" value will be frozen
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "freeze_before", "0" );
+       }
+       return this;
+}
+
+
+
diff --git a/src/modules/kdenlive/filter_wave.c b/src/modules/kdenlive/filter_wave.c
new file mode 100644 (file)
index 0000000..d01944f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * wave.c -- wave filter
+ * Copyright (C) ?-2007 Leny Grisel <leny.grisel@laposte.net>
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+// this is a utility function used by DoWave below
+static uint8_t getPoint(uint8_t *src, int w, int h, int x, int y, int z)
+{
+       if (x<0) x+=-((-x)%w)+w; else if (x>=w) x=x%w;
+       if (y<0) y+=-((-y)%h)+h; else if (y>=h) y=y%h;
+       return src[(x+y*w)*4+z];
+}
+
+// the main meat of the algorithm lies here
+static void DoWave(uint8_t *src, int src_w, int src_h, uint8_t *dst, mlt_position position, int speed, int factor, int deformX, int deformY)
+{
+       register int x, y;
+       int decalY, decalX, z;
+       float amplitude, phase, pulsation;
+       register int uneven = src_w % 2;
+       int w = (src_w - uneven ) / 2;
+       amplitude = factor;
+       pulsation = 0.5 / factor;   // smaller means bigger period
+       phase = position * pulsation * speed / 10; // smaller means longer
+       for (y=0;y<src_h;y++) {
+               decalX = deformX ? sin(pulsation * y + phase) * amplitude : 0;
+               for (x=0;x<w;x++) {
+                       decalY = deformY ? sin(pulsation * x * 2 + phase) * amplitude : 0;
+                       for (z=0; z<4; z++)
+                               *dst++ = getPoint(src, w, src_h, (x+decalX), (y+decalY), z);
+               }
+               if (uneven) {
+                       decalY = sin(pulsation * x * 2 + phase) * amplitude;
+                       for (z=0; z<2; z++)
+                               *dst++ = getPoint(src, w, src_h, (x+decalX), (y+decalY), z);
+               }
+       }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       mlt_position position = mlt_frame_get_position( this );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               double factor = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "wave" );
+               int speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "speed" );
+               int deformX = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "deformX" );
+               int deformY = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "deformY" );
+               if (factor != 0) {
+                       int image_size = *width * (*height + 1) * 2;
+                       uint8_t *dest = mlt_pool_alloc (image_size);
+                       DoWave(*image, *width, (*height + 1), dest, position, speed, factor, deformX, deformY);
+                       memcpy(*image, dest, image_size);
+                       mlt_pool_release(dest);
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the starting wave level
+       double wave = mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "start" );
+       int speed = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "speed" );
+       int deformX = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "deformX" );
+       int deformY = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "deformY" );
+
+       // If there is an end adjust gain to the range
+       if ( mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "end" ) != NULL )
+       {
+               // Determine the time position of this frame in the transition duration
+               mlt_position in = mlt_filter_get_in( this );
+               mlt_position out = mlt_filter_get_out( this );
+               mlt_position time = mlt_frame_get_position( frame );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               double end = fabs( mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "end" ) );
+               wave += ( end - wave ) * position;
+       }
+
+       // Push the frame filter
+       mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "wave", wave );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "speed", speed );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "deformX", deformX );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "deformY", deformY );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_wave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "start", arg == NULL ? "10" : arg);
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "speed", arg == NULL ? "5" : arg);
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "deformX", arg == NULL ? "1" : arg);
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "deformY", arg == NULL ? "1" : arg);
+               }
+       return this;
+}
+
+
+
diff --git a/src/modules/kdenlive/producer_framebuffer.c b/src/modules/kdenlive/producer_framebuffer.c
new file mode 100644 (file)
index 0000000..10dc22f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * producer_framebuffer.c -- create subspeed frames
+ * Copyright (C) 2007 Jean-Baptiste Mardelle <jb@ader.ch>
+ * Author: Jean-Baptiste Mardelle, based on the code of motion_est by Zachary Drew
+ *
+ * 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 <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+
+// Forward references.
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Image stack(able) method
+*/
+
+static int framebuffer_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       // Get the filter object and properties
+       mlt_producer producer = mlt_frame_pop_service( this );
+       mlt_frame first_frame = mlt_frame_pop_service( this );
+
+       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Frame properties objects
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+       mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES( first_frame );
+
+       *width = mlt_properties_get_int( frame_properties, "width" );
+       *height = mlt_properties_get_int( frame_properties, "height" );
+
+       int size;
+       switch ( *format )
+       {
+               case mlt_image_yuv420p:
+                       size = *width * 3 * ( *height + 1 ) / 2;
+                       break;
+               case mlt_image_rgb24:
+                       size = *width * ( *height + 1 ) * 3;
+                       break;
+               default:
+                       *format = mlt_image_yuv422;
+                       size = *width * ( *height + 1 ) * 2;
+                       break;
+       }
+
+       uint8_t *output = mlt_properties_get_data( producer_properties, "output_buffer", NULL );
+
+       if( output == NULL )
+       {
+               output = mlt_pool_alloc( size );
+
+               // Let someone else clean up
+               mlt_properties_set_data( producer_properties, "output_buffer", output, size, mlt_pool_release, NULL ); 
+       }
+
+       uint8_t *first_image = mlt_properties_get_data( first_frame_properties, "image", NULL );
+
+       // which frames are buffered?
+
+       int error = 0;
+
+       if( first_image == NULL )
+       {
+               mlt_properties props = MLT_FRAME_PROPERTIES( this );
+               mlt_properties test_properties = MLT_FRAME_PROPERTIES( first_frame );
+               mlt_properties_set_double( test_properties, "consumer_aspect_ratio", mlt_properties_get_double( props, "consumer_aspect_ratio" ) );
+               mlt_properties_set( test_properties, "rescale.interp", mlt_properties_get( props, "rescale.interp" ) );
+
+               error = mlt_frame_get_image( first_frame, &first_image, format, width, height, writable );
+
+               if( error != 0 ) {
+                       fprintf(stderr, "first_image == NULL get image died\n");
+                       return error;
+               }
+       }
+
+       // Start with a base image
+       memcpy( output, first_image, size );
+
+       *image = output;
+       mlt_properties_set_data( frame_properties, "image", output, size, NULL, NULL );
+
+       // Make sure that no further scaling is done
+       mlt_properties_set( frame_properties, "rescale.interps", "none" );
+       mlt_properties_set( frame_properties, "scale", "off" );
+
+       mlt_frame_close( first_frame );
+
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Construct a new frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       if( frame != NULL )
+       {
+               mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL );
+
+               mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position( first_frame ) : -1;
+
+               // Get the real producer
+               mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL );
+
+               // get properties               
+               int strobe = mlt_properties_get_int( properties, "strobe");
+               int freeze = mlt_properties_get_int( properties, "freeze");
+               int freeze_after = mlt_properties_get_int( properties, "freeze_after");
+               int freeze_before = mlt_properties_get_int( properties, "freeze_before");
+
+               mlt_position need_first;
+
+               if (!freeze || freeze_after || freeze_before) {
+                       double prod_speed = mlt_properties_get_double( properties, "_speed");
+                       double actual_position = prod_speed * (double) mlt_producer_position( this );
+
+                       if (mlt_properties_get_int( properties, "reverse")) actual_position = mlt_producer_get_playtime(this) - actual_position;
+
+                       if (strobe < 2)
+                       { 
+                               need_first = floor( actual_position );
+                       }
+                       else 
+                       {
+                               // Strobe effect wanted, calculate frame position
+                               need_first = floor( actual_position );
+                               need_first -= need_first%strobe;
+                       }
+                       if (freeze)
+                       {
+                               if (freeze_after && need_first > freeze) need_first = freeze;
+                               else if (freeze_before && need_first < freeze) need_first = freeze;
+                       }
+               }
+               else need_first = freeze;
+
+               if( need_first != first_position )
+               {
+                       mlt_frame_close( first_frame );
+                       first_position = -1;
+                       first_frame = NULL;
+               }
+
+               if( first_frame == NULL )
+               {
+                       // Seek the producer to the correct place
+                       mlt_producer_seek( real_producer, need_first );
+
+                       // Get the frame
+                       mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index );
+               }
+
+               // Make sure things are in their place
+               mlt_properties_set_data( properties, "first_frame", first_frame, 0, NULL, NULL );
+
+               // Stack the producer and producer's get image
+               mlt_frame_push_service( *frame, first_frame );
+               mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( first_frame ) );
+
+               mlt_frame_push_service( *frame, this );
+               mlt_frame_push_service( *frame, framebuffer_get_image );
+
+
+               // Give the returned frame temporal identity
+               mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+       }
+
+       return 0;
+}
+
+
+mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       if ( !arg ) return NULL;
+       mlt_producer this = NULL;
+       this = calloc( 1, sizeof( struct mlt_producer_s ) );
+       mlt_producer_init( this, NULL );
+
+       // Wrap fezzik
+       mlt_producer real_producer;
+       
+       // Check if a speed was specified.
+       /** 
+
+       * Speed must be appended to the filename with '?'. To play your video at 50%:
+        inigo framebuffer:my_video.mpg?0.5
+
+       * Stroboscope effect can be obtained by adding a stobe=x parameter, where
+        x is the number of frames that will be ignored.
+
+       * You can play the movie backwards by adding reverse=1
+
+       * You can freeze the clip at a determined position by adding freeze=frame_pos
+         add freeze_after=1 to freeze only paste position or freeze_before to freeze before it
+
+       **/
+
+       double speed = 0.0;
+       char *props = strdup( arg );
+       char *ptr = strrchr( props, '?' );
+       
+       if ( ptr )
+       {
+               speed = atof( ptr + 1 );
+               if ( speed != 0.0 )
+                       // If speed was valid, then strip it and the delimiter.
+                       // Otherwise, an invalid speed probably means this '?' was not a delimiter.
+                       *ptr = '\0';
+       }
+               
+       real_producer = mlt_factory_producer( profile, "fezzik", props );
+       free( props );
+
+       if (speed == 0.0) speed = 1.0;
+
+       if ( this != NULL && real_producer != NULL)
+       {
+               // Get the properties of this producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+               // Fezzik normalised it for us already
+               mlt_properties_set_int( properties, "fezzik_normalised", 1);
+               mlt_properties_set( properties, "resource", arg);
+
+               // Store the producer and fitler
+               mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+
+               // Grab some stuff from the real_producer
+               mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "length, width,height" );
+
+               if ( speed < 0 )
+               {
+                       speed = -speed;
+                       mlt_properties_set_int( properties, "reverse", 1 );
+               }
+
+               if ( speed != 1.0 )
+               {
+                       double real_length = ( (double)  mlt_producer_get_length( real_producer ) ) / speed;
+                       mlt_properties_set_position( properties, "length", real_length );
+               }
+               mlt_properties_set_position( properties, "out", mlt_producer_get_length( this ) - 1 );
+
+               // Since we control the seeking, prevent it from seeking on its own
+               mlt_producer_set_speed( real_producer, 0 );
+               mlt_producer_set_speed( this, speed );
+
+               // Override the get_frame method
+               this->get_frame = producer_get_frame;
+       }
+       else
+       {
+               if ( this )
+                       mlt_producer_close( this );
+               if ( real_producer )
+                       mlt_producer_close( real_producer );
+
+               this = NULL;
+       }
+       return this;
+}
diff --git a/src/modules/kino/Makefile b/src/modules/kino/Makefile
new file mode 100644 (file)
index 0000000..32cdeaa
--- /dev/null
@@ -0,0 +1,48 @@
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltkino.so
+
+OBJS = factory.o producer_kino.o
+CPPOBJS = kino_wrapper.o avi.o error.o filehandler.o riff.o
+
+CFLAGS += -I../../
+CXXFLAGS += $(CFLAGS) -Wno-deprecated
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lstdc++
+
+ifdef HAVE_LIBQUICKTIME
+CFLAGS += `pkg-config --cflags libquicktime` 
+CXXFLAGS += `pkg-config --cflags libquicktime` 
+LDFLAGS += `pkg-config --libs libquicktime`
+endif
+
+ifdef HAVE_LIBDV
+CFLAGS += `pkg-config --cflags libdv`
+LDFLAGS += `pkg-config --libs libdv`
+endif
+
+
+SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cc)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS) $(CPPOBJS)
+               $(CC) -shared -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend config.h config.mak
+
+clean: 
+               rm -f $(OBJS) $(TARGET) $(CPPOBJS)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/kino/avi.cc b/src/modules/kino/avi.cc
new file mode 100644 (file)
index 0000000..ced8993
--- /dev/null
@@ -0,0 +1,1707 @@
+/*
+* avi.cc library for AVI file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+
+// C++ includes
+
+#include <string>
+#include <iostream>
+#include <iomanip>
+
+using std::cout;
+using std::hex;
+using std::dec;
+using std::setw;
+using std::setfill;
+using std::endl;
+
+// C includes
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+// local includes
+
+#include "error.h"
+#include "riff.h"
+#include "avi.h"
+
+#define PADDING_SIZE (512)
+#define PADDING_1GB (0x40000000)
+#define IX00_INDEX_SIZE (4028)
+
+#define AVIF_HASINDEX 0x00000010
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_TRUSTCKTYPE 0x00000800
+#define AVIF_ISINTERLEAVED 0x00000100
+#define AVIF_WASCAPTUREFILE 0x00010000
+#define AVIF_COPYRIGHTED 0x00020000
+
+
+//static char g_zeroes[ PADDING_SIZE ];
+
+/** The constructor
+    \todo mainHdr not initialized
+    \todo add checking for NULL pointers
+*/
+
+AVIFile::AVIFile() : RIFFFile(),
+               idx1( NULL ), file_list( -1 ), riff_list( -1 ),
+               hdrl_list( -1 ), avih_chunk( -1 ), movi_list( -1 ), junk_chunk( -1 ), idx1_chunk( -1 ),
+               index_type( -1 ), current_ix00( -1 ), odml_list( -1 ), dmlh_chunk( -1 ), isUpdateIdx1( true )
+{
+       // cerr << "0x" << hex << (long)this << dec << " AVIFile::AVIFile() : RIFFFile(), ..." << endl;
+
+       for ( int i = 0; i < 2; ++i )
+       {
+               indx[ i ] = new AVISuperIndex;
+               memset( indx[ i ], 0, sizeof( AVISuperIndex ) );
+               ix[ i ] = new AVIStdIndex;
+               memset( ix[ i ], 0, sizeof( AVIStdIndex ) );
+               indx_chunk[ i ] = -1;
+               ix_chunk[ i ] = -1;
+               strl_list[ i ] = -1;
+               strh_chunk[ i ] = -1;
+               strf_chunk[ i ] = -1;
+       }
+       idx1 = new AVISimpleIndex;
+       memset( idx1, 0, sizeof( AVISimpleIndex ) );
+}
+
+
+/** The copy constructor
+    \todo add checking for NULL pointers
+*/
+
+AVIFile::AVIFile( const AVIFile& avi ) : RIFFFile( avi )
+{
+       // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile::AVIFile(const AVIFile& avi) : RIFFFile(avi)" << endl;
+
+       mainHdr = avi.mainHdr;
+       idx1 = new AVISimpleIndex;
+       *idx1 = *avi.idx1;
+       file_list = avi.file_list;
+       riff_list = avi.riff_list;
+       hdrl_list = avi.hdrl_list;
+       avih_chunk = avi.avih_chunk;
+       movi_list = avi.movi_list;
+       junk_chunk = avi.junk_chunk;
+       idx1_chunk = avi.idx1_chunk;
+
+       for ( int i = 0; i < 2; ++i )
+       {
+               indx[ i ] = new AVISuperIndex;
+               *indx[ i ] = *avi.indx[ i ];
+               ix[ i ] = new AVIStdIndex;
+               *ix[ i ] = *avi.ix[ i ];
+               indx_chunk[ i ] = avi.indx_chunk[ i ];
+               ix_chunk[ i ] = avi.ix_chunk[ i ];
+               strl_list[ i ] = avi.strl_list[ i ];
+               strh_chunk[ i ] = avi.strh_chunk[ i ];
+               strf_chunk[ i ] = avi.strf_chunk[ i ];
+       }
+
+       index_type = avi.index_type;
+       current_ix00 = avi.current_ix00;
+
+       for ( int i = 0; i < 62; ++i )
+               dmlh[ i ] = avi.dmlh[ i ];
+
+       isUpdateIdx1 = avi.isUpdateIdx1;
+
+}
+
+
+/** The assignment operator
+*/
+
+AVIFile& AVIFile::operator=( const AVIFile& avi )
+{
+       // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile& AVIFile::operator=(const AVIFile& avi)" << endl;
+
+       if ( this != &avi )
+       {
+               RIFFFile::operator=( avi );
+               mainHdr = avi.mainHdr;
+               *idx1 = *avi.idx1;
+               file_list = avi.file_list;
+               riff_list = avi.riff_list;
+               hdrl_list = avi.hdrl_list;
+               avih_chunk = avi.avih_chunk;
+               movi_list = avi.movi_list;
+               junk_chunk = avi.junk_chunk;
+               idx1_chunk = avi.idx1_chunk;
+
+               for ( int i = 0; i < 2; ++i )
+               {
+                       *indx[ i ] = *avi.indx[ i ];
+                       *ix[ i ] = *avi.ix[ i ];
+                       indx_chunk[ i ] = avi.indx_chunk[ i ];
+                       ix_chunk[ i ] = avi.ix_chunk[ i ];
+                       strl_list[ i ] = avi.strl_list[ i ];
+                       strh_chunk[ i ] = avi.strh_chunk[ i ];
+                       strf_chunk[ i ] = avi.strf_chunk[ i ];
+               }
+
+               index_type = avi.index_type;
+               current_ix00 = avi.current_ix00;
+
+               for ( int i = 0; i < 62; ++i )
+                       dmlh[ i ] = avi.dmlh[ i ];
+
+               isUpdateIdx1 = avi.isUpdateIdx1;
+       }
+       return *this;
+}
+
+
+/** The destructor
+*/
+
+AVIFile::~AVIFile()
+{
+       // cerr << "0x" << hex << (long)this << dec << " AVIFile::~AVIFile()" << endl;
+
+       for ( int i = 0; i < 2; ++i )
+       {
+               delete ix[ i ];
+               delete indx[ i ];
+       }
+       delete idx1;
+}
+
+/** Initialize the AVI structure to its initial state, either for PAL or NTSC format
+    Initialize the AVIFile attributes: mainHdr, indx, ix00, idx1
+    \todo consolidate AVIFile::Init, AVI1File::Init, AVI2File::Init. They are somewhat redundant.
+    \param format pass AVI_PAL or AVI_NTSC
+    \param sampleFrequency the sample frequency of the audio content
+    \param indexType pass AVI_SMALL_INDEX or AVI_LARGE_INDEX
+*/
+
+void AVIFile::Init( int format, int sampleFrequency, int indexType )
+{
+       int i, j;
+
+       assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+       index_type = indexType;
+
+       switch ( format )
+       {
+       case AVI_PAL:
+               mainHdr.dwMicroSecPerFrame = 40000;
+               mainHdr.dwSuggestedBufferSize = 144008;
+               break;
+
+       case AVI_NTSC:
+               mainHdr.dwMicroSecPerFrame = 33366;
+               mainHdr.dwSuggestedBufferSize = 120008;
+               break;
+
+       default:   /* no default allowed */
+               assert( 0 );
+               break;
+       }
+
+       /* Initialize the 'avih' chunk */
+
+       mainHdr.dwMaxBytesPerSec = 3600000 + sampleFrequency * 4;
+       mainHdr.dwPaddingGranularity = PADDING_SIZE;
+       mainHdr.dwFlags = AVIF_TRUSTCKTYPE;
+       if ( indexType & AVI_SMALL_INDEX )
+               mainHdr.dwFlags |= AVIF_HASINDEX;
+       mainHdr.dwTotalFrames = 0;
+       mainHdr.dwInitialFrames = 0;
+       mainHdr.dwStreams = 1;
+       mainHdr.dwWidth = 0;
+       mainHdr.dwHeight = 0;
+       mainHdr.dwReserved[ 0 ] = 0;
+       mainHdr.dwReserved[ 1 ] = 0;
+       mainHdr.dwReserved[ 2 ] = 0;
+       mainHdr.dwReserved[ 3 ] = 0;
+
+       /* Initialize the 'idx1' chunk */
+
+       for ( int i = 0; i < 8000; ++i )
+       {
+               idx1->aIndex[ i ].dwChunkId = 0;
+               idx1->aIndex[ i ].dwFlags = 0;
+               idx1->aIndex[ i ].dwOffset = 0;
+               idx1->aIndex[ i ].dwSize = 0;
+       }
+       idx1->nEntriesInUse = 0;
+
+       /* Initialize the 'indx' chunk */
+
+       for ( i = 0; i < 2; ++i )
+       {
+               indx[ i ] ->wLongsPerEntry = 4;
+               indx[ i ] ->bIndexSubType = 0;
+               indx[ i ] ->bIndexType = KINO_AVI_INDEX_OF_INDEXES;
+               indx[ i ] ->nEntriesInUse = 0;
+               indx[ i ] ->dwReserved[ 0 ] = 0;
+               indx[ i ] ->dwReserved[ 1 ] = 0;
+               indx[ i ] ->dwReserved[ 2 ] = 0;
+               for ( j = 0; j < 2014; ++j )
+               {
+                       indx[ i ] ->aIndex[ j ].qwOffset = 0;
+                       indx[ i ] ->aIndex[ j ].dwSize = 0;
+                       indx[ i ] ->aIndex[ j ].dwDuration = 0;
+               }
+       }
+
+       /* The ix00 and ix01 chunk will be added dynamically in avi_write_frame
+                 as needed */
+
+       /* Initialize the 'dmlh' chunk. I have no clue what this means
+          though */
+
+       for ( i = 0; i < 62; ++i )
+               dmlh[ i ] = 0;
+       //dmlh[0] = -1;            /* frame count + 1? */
+
+}
+
+
+/** Find position and size of a given frame in the file
+    Depending on which index is available, search one of them to
+    find position and frame size
+    \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
+    \todo all index related operations should be isolated 
+    \param offset the file offset to the start of the frame
+    \param size the size of the frame
+    \param frameNum the number of the frame we wish to find
+    \return 0 if the frame could be found, -1 otherwise
+*/
+
+int AVIFile::GetDVFrameInfo( off_t &offset, int &size, int frameNum )
+{
+       switch ( index_type )
+       {
+       case AVI_LARGE_INDEX:
+
+               /* find relevant index in indx0 */
+
+               int i;
+
+               for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
+                       ;
+
+               if ( i != current_ix00 )
+               {
+                       fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+                       fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
+                       current_ix00 = i;
+               }
+
+               if ( frameNum < ix[ 0 ] ->nEntriesInUse )
+               {
+                       offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
+                       size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
+                       return 0;
+               }
+               else
+                       return -1;
+               break;
+
+       case AVI_SMALL_INDEX:
+               int index = -1;
+               int frameNumIndex = 0;
+               for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+               {
+                       FOURCC chunkID1 = make_fourcc( "00dc" );
+                       FOURCC chunkID2 = make_fourcc( "00db" );
+                       if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
+                               idx1->aIndex[ i ].dwChunkId == chunkID2 )
+                       {
+                               if ( frameNumIndex == frameNum )
+                               {
+                                       index = i;
+                                       break;
+                               }
+                               ++frameNumIndex;
+                       }
+               }
+               if ( index != -1 )
+               {
+                       // compatibility check for broken dvgrab dv2 format
+                       if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
+                       {
+                               offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
+                       }
+                       else
+                       {
+                               // new, correct dv2 format
+                               offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
+                       }
+                       size = idx1->aIndex[ index ].dwSize;
+                       return 0;
+               }
+               else
+                       return -1;
+               break;
+       }
+       return -1;
+}
+
+/** Find position and size of a given frame in the file
+    Depending on which index is available, search one of them to
+    find position and frame size
+    \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
+    \todo all index related operations should be isolated 
+    \param offset the file offset to the start of the frame
+    \param size the size of the frame
+    \param frameNum the number of the frame we wish to find
+       \param chunkID the ID of the type of chunk we want
+    \return 0 if the frame could be found, -1 otherwise
+*/
+
+int AVIFile::GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID )
+{
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               int i;
+
+               for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
+                       ;
+
+               if ( i != current_ix00 )
+               {
+                       fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+                       fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
+                       current_ix00 = i;
+               }
+
+               if ( frameNum < ix[ 0 ] ->nEntriesInUse )
+               {
+                       if ( ( FOURCC ) ix[ 0 ] ->dwChunkId == chunkID )
+                       {
+                               offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
+                               size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
+                               return 0;
+                       }
+               }
+       }
+       if ( index_type & AVI_SMALL_INDEX )
+       {
+               int index = -1;
+               int frameNumIndex = 0;
+               for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+               {
+                       if ( idx1->aIndex[ i ].dwChunkId == chunkID )
+                       {
+                               if ( frameNumIndex == frameNum )
+                               {
+                                       index = i;
+                                       break;
+                               }
+                               ++frameNumIndex;
+                       }
+               }
+               if ( index != -1 )
+               {
+                       // compatibility check for broken dvgrab dv2 format
+                       if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
+                       {
+                               offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
+                       }
+                       else
+                       {
+                               // new, correct dv2 format
+                               offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
+                       }
+                       size = idx1->aIndex[ index ].dwSize;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/** Read in a frame
+    \todo we actually don't need the frame here, we could use just a void pointer
+    \param frame a reference to the frame object that will receive the frame data
+    \param frameNum the frame number to read
+    \return 0 if the frame could be read, -1 otherwise
+*/
+
+int AVIFile::GetDVFrame( uint8_t *data, int frameNum )
+{
+       off_t   offset;
+       int     size;
+
+       if ( GetDVFrameInfo( offset, size, frameNum ) != 0 || size < 0 )
+               return -1;
+       pthread_mutex_lock( &file_mutex );
+       fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+       fail_neg( read( fd, data, size ) );
+       pthread_mutex_unlock( &file_mutex );
+
+       return 0;
+}
+
+/** Read in a frame
+    \param data a pointer to the audio buffer
+    \param frameNum the frame number to read
+       \param chunkID the ID of the type of chunk we want
+    \return the size the of the frame data, 0 if could not be read
+*/
+
+int AVIFile::getFrame( void *data, int frameNum, FOURCC chunkID )
+{
+       off_t offset;
+       int     size;
+
+       if ( GetFrameInfo( offset, size, frameNum, chunkID ) != 0 )
+               return 0;
+       fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+       fail_neg( read( fd, data, size ) );
+
+       return size;
+}
+
+int AVIFile::GetTotalFrames() const
+{
+       return mainHdr.dwTotalFrames;
+}
+
+
+/** prints out a directory entry in text form
+    Every subclass of RIFFFile is supposed to override this function
+    and to implement it for the entry types it knows about. For all
+    other entry types it should call its parent::PrintDirectoryData.
+    \todo use 64 bit routines
+    \param entry the entry to print
+*/
+
+void AVIFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
+{
+       static FOURCC lastStreamType = make_fourcc( "    " );
+
+       if ( entry.type == make_fourcc( "avih" ) )
+       {
+
+               int i;
+               MainAVIHeader main_avi_header;
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, &main_avi_header, sizeof( MainAVIHeader ) ) );
+
+               cout << "    dwMicroSecPerFrame:    " << ( int ) main_avi_header.dwMicroSecPerFrame << endl
+               << "    dwMaxBytesPerSec:      " << ( int ) main_avi_header.dwMaxBytesPerSec << endl
+               << "    dwPaddingGranularity:  " << ( int ) main_avi_header.dwPaddingGranularity << endl
+               << "    dwFlags:               " << ( int ) main_avi_header.dwFlags << endl
+               << "    dwTotalFrames:         " << ( int ) main_avi_header.dwTotalFrames << endl
+               << "    dwInitialFrames:       " << ( int ) main_avi_header.dwInitialFrames << endl
+               << "    dwStreams:             " << ( int ) main_avi_header.dwStreams << endl
+               << "    dwSuggestedBufferSize: " << ( int ) main_avi_header.dwSuggestedBufferSize << endl
+               << "    dwWidth:               " << ( int ) main_avi_header.dwWidth << endl
+               << "    dwHeight:              " << ( int ) main_avi_header.dwHeight << endl;
+               for ( i = 0; i < 4; ++i )
+                       cout << "    dwReserved[" << i << "]:        " << ( int ) main_avi_header.dwReserved[ i ] << endl;
+
+       }
+       else if ( entry.type == make_fourcc( "strh" ) )
+       {
+
+               AVIStreamHeader avi_stream_header;
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, &avi_stream_header, sizeof( AVIStreamHeader ) ) );
+
+               lastStreamType = avi_stream_header.fccType;
+
+               cout << "    fccType:         '"
+               << ((char *)&avi_stream_header.fccType)[0]
+               << ((char *)&avi_stream_header.fccType)[1]
+               << ((char *)&avi_stream_header.fccType)[2]
+               << ((char *)&avi_stream_header.fccType)[3]
+               << '\'' << endl
+               << "    fccHandler:      '"
+               << ((char *)&avi_stream_header.fccHandler)[0]
+               << ((char *)&avi_stream_header.fccHandler)[1]
+               << ((char *)&avi_stream_header.fccHandler)[2]
+               << ((char *)&avi_stream_header.fccHandler)[3]
+               << '\'' << endl
+               << "    dwFlags:         " << ( int ) avi_stream_header.dwFlags << endl
+               << "    wPriority:       " << ( int ) avi_stream_header.wPriority << endl
+               << "    wLanguage:       " << ( int ) avi_stream_header.wLanguage << endl
+               << "    dwInitialFrames: " << ( int ) avi_stream_header.dwInitialFrames << endl
+               << "    dwScale:         " << ( int ) avi_stream_header.dwScale << endl
+               << "    dwRate:          " << ( int ) avi_stream_header.dwRate << endl
+               << "    dwLength:        " << ( int ) avi_stream_header.dwLength << endl
+               << "    dwQuality:       " << ( int ) avi_stream_header.dwQuality << endl
+               << "    dwSampleSize:    " << ( int ) avi_stream_header.dwSampleSize << endl;
+
+       }
+       else if ( entry.type == make_fourcc( "indx" ) )
+       {
+
+               int i;
+               AVISuperIndex avi_super_index;
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, &avi_super_index, sizeof( AVISuperIndex ) ) );
+
+               cout << "    wLongsPerEntry: " << ( int ) avi_super_index.wLongsPerEntry
+               << endl
+               << "    bIndexSubType:  " << ( int ) avi_super_index.bIndexSubType << endl
+               << "    bIndexType:     " << ( int ) avi_super_index.bIndexType << endl
+               << "    nEntriesInUse:  " << ( int ) avi_super_index.nEntriesInUse << endl
+               << "    dwChunkId:      '"
+               << ((char *)&avi_super_index.dwChunkId)[0]
+               << ((char *)&avi_super_index.dwChunkId)[1]
+               << ((char *)&avi_super_index.dwChunkId)[2]
+               << ((char *)&avi_super_index.dwChunkId)[3]
+               << '\'' << endl
+               << "    dwReserved[0]:  " << ( int ) avi_super_index.dwReserved[ 0 ] << endl
+               << "    dwReserved[1]:  " << ( int ) avi_super_index.dwReserved[ 1 ] << endl
+               << "    dwReserved[2]:  " << ( int ) avi_super_index.dwReserved[ 2 ] << endl;
+               for ( i = 0; i < avi_super_index.nEntriesInUse; ++i )
+               {
+                       cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
+                       << ": qwOffset    : 0x" << setw( 12 ) << setfill( '0' ) << hex << avi_super_index.aIndex[ i ].qwOffset << endl
+                       << "       dwSize      : 0x" << setw( 8 ) << avi_super_index.aIndex[ i ].dwSize << endl
+                       << "       dwDuration  : " << dec << avi_super_index.aIndex[ i ].dwDuration << endl;
+               }
+       }
+       else if ( entry.type == make_fourcc( "strf" ) )
+       {
+               if ( lastStreamType == make_fourcc( "auds" ) )
+               {
+                       WAVEFORMATEX waveformatex;
+                       fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+                       fail_neg( read( fd, &waveformatex, sizeof( WAVEFORMATEX ) ) );
+                       cout << "    waveformatex.wFormatTag     : " << waveformatex.wFormatTag << endl;
+                       cout << "    waveformatex.nChannels      : " << waveformatex.nChannels << endl;
+                       cout << "    waveformatex.nSamplesPerSec : " << waveformatex.nSamplesPerSec << endl;
+                       cout << "    waveformatex.nAvgBytesPerSec: " << waveformatex.nAvgBytesPerSec << endl;
+                       cout << "    waveformatex.nBlockAlign    : " << waveformatex.nBlockAlign << endl;
+                       cout << "    waveformatex.wBitsPerSample : " << waveformatex.wBitsPerSample << endl;
+                       cout << "    waveformatex.cbSize         : " << waveformatex.cbSize << endl;
+               }
+               else if ( lastStreamType == make_fourcc( "vids" ) )
+               {
+                       BITMAPINFOHEADER bitmapinfo;
+                       fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+                       fail_neg( read( fd, &bitmapinfo, sizeof( BITMAPINFOHEADER ) ) );
+                       cout << "    bitmapinfo.biSize         : " << bitmapinfo.biSize << endl;
+                       cout << "    bitmapinfo.biWidth        : " << bitmapinfo.biWidth << endl;
+                       cout << "    bitmapinfo.biHeight       : " << bitmapinfo.biHeight << endl;
+                       cout << "    bitmapinfo.biPlanes       : " << bitmapinfo.biPlanes << endl;
+                       cout << "    bitmapinfo.biBitCount     : " << bitmapinfo.biBitCount << endl;
+                       cout << "    bitmapinfo.biCompression  : " << bitmapinfo.biCompression << endl;
+                       cout << "    bitmapinfo.biSizeImage    : " << bitmapinfo.biSizeImage << endl;
+                       cout << "    bitmapinfo.biXPelsPerMeter: " << bitmapinfo.biXPelsPerMeter << endl;
+                       cout << "    bitmapinfo.biYPelsPerMeter: " << bitmapinfo.biYPelsPerMeter << endl;
+                       cout << "    bitmapinfo.biClrUsed      : " << bitmapinfo.biClrUsed << endl;
+                       cout << "    bitmapinfo.biClrImportant : " << bitmapinfo.biClrImportant << endl;
+               }
+               else if ( lastStreamType == make_fourcc( "iavs" ) )
+               {
+                       DVINFO dvinfo;
+                       fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+                       fail_neg( read( fd, &dvinfo, sizeof( DVINFO ) ) );
+                       cout << "    dvinfo.dwDVAAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc << endl;
+                       cout << "    dvinfo.dwDVAAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl << endl;
+                       cout << "    dvinfo.dwDVAAuxSrc1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc1 << endl;
+                       cout << "    dvinfo.dwDVAAuxCtl1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl1 << endl;
+                       cout << "    dvinfo.dwDVVAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxSrc << endl;
+                       cout << "    dvinfo.dwDVVAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxCtl << endl;
+               }
+       }
+
+       /* This is the Standard Index. It is an array of offsets and
+          sizes relative to some start offset. */
+
+       else if ( ( entry.type == make_fourcc( "ix00" ) ) || ( entry.type == make_fourcc( "ix01" ) ) )
+       {
+
+               int i;
+               AVIStdIndex avi_std_index;
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, &avi_std_index, sizeof( AVIStdIndex ) ) );
+
+               cout << "    wLongsPerEntry: " << ( int ) avi_std_index.wLongsPerEntry
+               << endl
+               << "    bIndexSubType:  " << ( int ) avi_std_index.bIndexSubType << endl
+               << "    bIndexType:     " << ( int ) avi_std_index.bIndexType << endl
+               << "    nEntriesInUse:  " << ( int ) avi_std_index.nEntriesInUse << endl
+               << "    dwChunkId:      '"
+               << ((char *)&avi_std_index.dwChunkId)[0]
+               << ((char *)&avi_std_index.dwChunkId)[1]
+               << ((char *)&avi_std_index.dwChunkId)[2]
+               << ((char *)&avi_std_index.dwChunkId)[3]
+               << '\'' << endl
+               << "    qwBaseOffset:   0x" << setw( 12 ) << hex << avi_std_index.qwBaseOffset << endl
+               << "    dwReserved:     " << dec << ( int ) avi_std_index.dwReserved << endl;
+               for ( i = 0; i < avi_std_index.nEntriesInUse; ++i )
+               {
+                       cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
+                       << ": dwOffset    : 0x" << setw( 8 ) << setfill( '0' ) << hex << avi_std_index.aIndex[ i ].dwOffset
+                       << " (0x" << setw( 12 ) << avi_std_index.qwBaseOffset + avi_std_index.aIndex[ i ].dwOffset << ')' << endl
+                       << "       dwSize      : 0x" << setw( 8 ) << avi_std_index.aIndex[ i ].dwSize << dec << endl;
+               }
+
+       }
+       else if ( entry.type == make_fourcc( "idx1" ) )
+       {
+
+               int i;
+               int numEntries = entry.length / sizeof( int ) / 4;
+               DWORD *idx1 = new DWORD[ numEntries * 4 ];
+               // FOURCC movi_list = FindDirectoryEntry(make_fourcc("movi"));
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, idx1, entry.length ) );
+
+               for ( i = 0; i < numEntries; ++i )
+               {
+
+                       cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": dwChunkId : '"
+                       << ((char *)&idx1[ i * 4 + 0 ])[0]
+                       << ((char *)&idx1[ i * 4 + 0 ])[1]
+                       << ((char *)&idx1[ i * 4 + 0 ])[2]
+                       << ((char *)&idx1[ i * 4 + 0 ])[3]
+                       << '\'' << endl
+                       << "       dwType    : 0x" << setw( 8 ) << hex << idx1[ i * 4 + 1 ] << endl
+                       << "       dwOffset  : 0x" << setw( 8 ) << idx1[ i * 4 + 2 ] << endl
+                       // << " (0x" << setw(8) << idx1[i * 4 + 2] + GetDirectoryEntry(movi_list).offset << ')' << endl
+                       << "       dwSize    : 0x" << setw( 8 ) << idx1[ i * 4 + 3 ] << dec << endl;
+               }
+
+               delete[] idx1;
+       }
+       else if ( entry.type == make_fourcc( "dmlh" ) )
+       {
+               int i;
+               int numEntries = entry.length / sizeof( int );
+               DWORD *dmlh = new DWORD[ numEntries ];
+
+               fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+               fail_neg( read( fd, dmlh, entry.length ) );
+
+               for ( i = 0; i < numEntries; ++i )
+               {
+                       cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": "
+                       << " dwTotalFrames: 0x" << setw( 8 ) << hex << dmlh[ i ]
+                       << " (" << dec << dmlh[ i ] << ")" << endl;
+               }
+               delete[] dmlh;
+       }
+}
+
+
+/** If this is not a movi list, read its contents
+*/
+
+void AVIFile::ParseList( int parent )
+{
+       FOURCC type;
+       FOURCC name;
+       DWORD length;
+       int list;
+       off_t pos;
+       off_t listEnd;
+
+       /* Read in the chunk header (type and length). */
+       fail_neg( read( fd, &type, sizeof( type ) ) );
+       fail_neg( read( fd, &length, sizeof( length ) ) );
+       if ( length & 1 )
+               length++;
+
+       /* The contents of the list starts here. Obtain its offset. The list
+          name (4 bytes) is already part of the contents). */
+
+       pos = lseek( fd, 0, SEEK_CUR );
+       fail_if( pos == ( off_t ) - 1 );
+       fail_neg( read( fd, &name, sizeof( name ) ) );
+
+       /* if we encounter a movi list, do not read it. It takes too much time
+          and we don't need it anyway. */
+
+       if ( name != make_fourcc( "movi" ) )
+       {
+               //    if (1) {
+
+               /* Add an entry for this list. */
+               list = AddDirectoryEntry( type, name, sizeof( name ), parent );
+
+               /* Read in any chunks contained in this list. This list is the
+                  parent for all chunks it contains. */
+
+               listEnd = pos + length;
+               while ( pos < listEnd )
+               {
+                       ParseChunk( list );
+                       pos = lseek( fd, 0, SEEK_CUR );
+                       fail_if( pos == ( off_t ) - 1 );
+               }
+       }
+       else
+       {
+               /* Add an entry for this list. */
+
+               movi_list = AddDirectoryEntry( type, name, length, parent );
+
+               pos = lseek( fd, length - 4, SEEK_CUR );
+               fail_if( pos == ( off_t ) - 1 );
+       }
+}
+
+
+void AVIFile::ParseRIFF()
+{
+       RIFFFile::ParseRIFF();
+
+       avih_chunk = FindDirectoryEntry( make_fourcc( "avih" ) );
+       if ( avih_chunk != -1 )
+               ReadChunk( avih_chunk, ( void* ) & mainHdr, sizeof( MainAVIHeader ) );
+}
+
+
+void AVIFile::ReadIndex()
+{
+       indx_chunk[ 0 ] = FindDirectoryEntry( make_fourcc( "indx" ) );
+       if ( indx_chunk[ 0 ] != -1 )
+       {
+               ReadChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ], sizeof( AVISuperIndex ) );
+               index_type = AVI_LARGE_INDEX;
+
+               /* recalc number of frames from each index */
+               mainHdr.dwTotalFrames = 0;
+               for ( int i = 0;
+                       i < indx[ 0 ] ->nEntriesInUse;
+                       mainHdr.dwTotalFrames += indx[ 0 ] ->aIndex[ i++ ].dwDuration )
+                       ;
+               return ;
+       }
+       idx1_chunk = FindDirectoryEntry( make_fourcc( "idx1" ) );
+       if ( idx1_chunk != -1 )
+       {
+               ReadChunk( idx1_chunk, ( void* ) idx1, sizeof( AVISuperIndex ) );
+               idx1->nEntriesInUse = GetDirectoryEntry( idx1_chunk ).length / 16;
+               index_type = AVI_SMALL_INDEX;
+
+               /* recalc number of frames from the simple index */
+               int frameNumIndex = 0;
+               FOURCC chunkID1 = make_fourcc( "00dc" );
+               FOURCC chunkID2 = make_fourcc( "00db" );
+               for ( int i = 0; i < idx1->nEntriesInUse; ++i )
+               {
+                       if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
+                               idx1->aIndex[ i ].dwChunkId == chunkID2 )
+                       {
+                               ++frameNumIndex;
+                       }
+               }
+               mainHdr.dwTotalFrames = frameNumIndex;
+               return ;
+       }
+}
+
+
+void AVIFile::FlushIndx( int stream )
+{
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+       int i;
+
+       /* Write out the previous index. When this function is
+          entered for the first time, there is no index to
+          write.  Note: this may be an expensive operation
+          because of a time consuming seek to the former file
+          position. */
+
+       if ( ix_chunk[ stream ] != -1 )
+               WriteChunk( ix_chunk[ stream ], ix[ stream ] );
+
+       /* make a new ix chunk. */
+
+       if ( stream == 0 )
+               type = make_fourcc( "ix00" );
+       else
+               type = make_fourcc( "ix01" );
+       ix_chunk[ stream ] = AddDirectoryEntry( type, 0, sizeof( AVIStdIndex ), movi_list );
+       GetDirectoryEntry( ix_chunk[ stream ], type, name, length, offset, parent );
+
+       /* fill out all required fields. The offsets in the
+          array are relative to qwBaseOffset, so fill in the
+          offset to the next free location in the file
+          there. */
+
+       ix[ stream ] ->wLongsPerEntry = 2;
+       ix[ stream ] ->bIndexSubType = 0;
+       ix[ stream ] ->bIndexType = KINO_AVI_INDEX_OF_CHUNKS;
+       ix[ stream ] ->nEntriesInUse = 0;
+       ix[ stream ] ->dwChunkId = indx[ stream ] ->dwChunkId;
+       ix[ stream ] ->qwBaseOffset = offset + length;
+       ix[ stream ] ->dwReserved = 0;
+
+       for ( i = 0; i < IX00_INDEX_SIZE; ++i )
+       {
+               ix[ stream ] ->aIndex[ i ].dwOffset = 0;
+               ix[ stream ] ->aIndex[ i ].dwSize = 0;
+       }
+
+       /* add a reference to this new index in our super
+          index. */
+
+       i = indx[ stream ] ->nEntriesInUse++;
+       indx[ stream ] ->aIndex[ i ].qwOffset = offset - RIFF_HEADERSIZE;
+       indx[ stream ] ->aIndex[ i ].dwSize = length + RIFF_HEADERSIZE;
+       indx[ stream ] ->aIndex[ i ].dwDuration = 0;
+}
+
+
+void AVIFile::UpdateIndx( int stream, int chunk, int duration )
+{
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+       int i;
+
+       /* update the appropiate entry in the super index. It reflects
+          the number of frames in the referenced index. */
+
+       i = indx[ stream ] ->nEntriesInUse - 1;
+       indx[ stream ] ->aIndex[ i ].dwDuration += duration;
+
+       /* update the standard index. Calculate the file position of
+          the new frame. */
+
+       GetDirectoryEntry( chunk, type, name, length, offset, parent );
+
+       indx[ stream ] ->dwChunkId = type;
+       i = ix[ stream ] ->nEntriesInUse++;
+       ix[ stream ] ->aIndex[ i ].dwOffset = offset - ix[ stream ] ->qwBaseOffset;
+       ix[ stream ] ->aIndex[ i ].dwSize = length;
+}
+
+
+void AVIFile::UpdateIdx1( int chunk, int flags )
+{
+       if ( idx1->nEntriesInUse < 20000 )
+       {
+               FOURCC type;
+               FOURCC name;
+               off_t length;
+               off_t offset;
+               int parent;
+
+               GetDirectoryEntry( chunk, type, name, length, offset, parent );
+
+               idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = type;
+               idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = flags;
+               idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = offset - GetDirectoryEntry( movi_list ).offset - RIFF_HEADERSIZE;
+               idx1->aIndex[ idx1->nEntriesInUse ].dwSize = length;
+               idx1->nEntriesInUse++;
+       }
+}
+
+bool AVIFile::verifyStreamFormat( FOURCC type )
+{
+       int i, j = 0;
+       AVIStreamHeader avi_stream_header;
+       BITMAPINFOHEADER bih;
+       FOURCC strh = make_fourcc( "strh" );
+       FOURCC strf = make_fourcc( "strf" );
+
+       while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+       {
+               ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+               if ( avi_stream_header.fccHandler == type )
+                       return true;
+       }
+       j = 0;
+       while ( ( i = FindDirectoryEntry( strf, j++ ) ) != -1 )
+       {
+               ReadChunk( i, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
+               if ( ( FOURCC ) bih.biCompression == type )
+                       return true;
+       }
+
+       return false;
+}
+
+bool AVIFile::verifyStream( FOURCC type )
+{
+       int i, j = 0;
+       AVIStreamHeader avi_stream_header;
+       FOURCC strh = make_fourcc( "strh" );
+
+       while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+       {
+               ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+               if ( avi_stream_header.fccType == type )
+                       return true;
+       }
+       return false;
+}
+
+bool AVIFile::isOpenDML( void )
+{
+       int i, j = 0;
+       FOURCC dmlh = make_fourcc( "dmlh" );
+
+       while ( ( i = FindDirectoryEntry( dmlh, j++ ) ) != -1 )
+       {
+               return true;
+       }
+       return false;
+}
+
+AVI1File::AVI1File() : AVIFile()
+{}
+
+
+AVI1File::~AVI1File()
+{}
+
+
+/* Initialize the AVI structure to its initial state, either for PAL
+   or NTSC format */
+
+void AVI1File::Init( int format, int sampleFrequency, int indexType )
+{
+       int num_blocks;
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+
+       assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+       AVIFile::Init( format, sampleFrequency, indexType );
+
+       switch ( format )
+       {
+       case AVI_PAL:
+               mainHdr.dwWidth = 720;
+               mainHdr.dwHeight = 576;
+
+               streamHdr[ 0 ].dwScale = 1;
+               streamHdr[ 0 ].dwRate = 25;
+               streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
+
+               /* initialize the 'strf' chunk */
+
+               /* Meaning of the DV stream format chunk per Microsoft
+                  dwDVAAuxSrc
+                     Specifies the Audio Auxiliary Data Source Pack for the first audio block
+                     (first 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of
+                     a frame. A DIF sequence is a data block that contains 150 DIF blocks. A DIF block consists
+                     of 80 bytes. The Audio Auxiliary Data Source Pack is defined in section D.7.1 of Part 2,
+                     Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+                     Consumer-use Digital VCRs.
+                  dwDVAAuxCtl
+                     Specifies the Audio Auxiliary Data Source Control Pack for the first audio block of a
+                     frame. The Audio Auxiliary Data Control Pack is defined in section D.7.2 of Part 2,
+                     Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+                     Consumer-use Digital VCRs.
+                  dwDVAAuxSrc1
+                     Specifies the Audio Auxiliary Data Source Pack for the second audio block
+                     (second 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of a frame.
+                  dwDVAAuxCtl1
+                     Specifies the Audio Auxiliary Data Source Control Pack for the second audio block of a frame.
+                  dwDVVAuxSrc
+                     Specifies the Video Auxiliary Data Source Pack as defined in section D.8.1 of Part 2,
+                     Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+                     Consumer-use Digital VCRs.
+                  dwDVVAuxCtl
+                     Specifies the Video Auxiliary Data Source Control Pack as defined in section D.8.2 of Part 2,
+                     Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
+                     Consumer-use Digital VCRs.
+                  dwDVReserved[2]
+                     Reserved. Set this array to zero.   
+               */
+
+               dvinfo.dwDVAAuxSrc = 0xd1e030d0;
+               dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
+               dvinfo.dwDVAAuxSrc1 = 0xd1e03fd0;
+               dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
+               dvinfo.dwDVVAuxSrc = 0xff20ffff;
+               dvinfo.dwDVVAuxCtl = 0xfffdc83f;
+               dvinfo.dwDVReserved[ 0 ] = 0;
+               dvinfo.dwDVReserved[ 1 ] = 0;
+               break;
+
+       case AVI_NTSC:
+               mainHdr.dwWidth = 720;
+               mainHdr.dwHeight = 480;
+
+               streamHdr[ 0 ].dwScale = 1001;
+               streamHdr[ 0 ].dwRate = 30000;
+               streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
+
+               /* initialize the 'strf' chunk */
+               dvinfo.dwDVAAuxSrc = 0xc0c000c0;
+               dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
+               dvinfo.dwDVAAuxSrc1 = 0xc0c001c0;
+               dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
+               dvinfo.dwDVVAuxSrc = 0xff80ffff;
+               dvinfo.dwDVVAuxCtl = 0xfffcc83f;
+               dvinfo.dwDVReserved[ 0 ] = 0;
+               dvinfo.dwDVReserved[ 1 ] = 0;
+               break;
+
+       default:   /* no default allowed */
+               assert( 0 );
+               break;
+       }
+
+       indx[ 0 ] ->dwChunkId = make_fourcc( "00__" );
+
+       /* Initialize the 'strh' chunk */
+
+       streamHdr[ 0 ].fccType = make_fourcc( "iavs" );
+       streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+       streamHdr[ 0 ].dwFlags = 0;
+       streamHdr[ 0 ].wPriority = 0;
+       streamHdr[ 0 ].wLanguage = 0;
+       streamHdr[ 0 ].dwInitialFrames = 0;
+       streamHdr[ 0 ].dwStart = 0;
+       streamHdr[ 0 ].dwLength = 0;
+       streamHdr[ 0 ].dwQuality = 0;
+       streamHdr[ 0 ].dwSampleSize = 0;
+       streamHdr[ 0 ].rcFrame.top = 0;
+       streamHdr[ 0 ].rcFrame.bottom = 0;
+       streamHdr[ 0 ].rcFrame.left = 0;
+       streamHdr[ 0 ].rcFrame.right = 0;
+
+       /* This is a simple directory structure setup. For details see the
+          "OpenDML AVI File Format Extensions" document.
+          
+          An AVI file contains basically two types of objects, a
+          "chunk" and a "list" object. The list object contains any
+          number of chunks. Since a list is also a chunk, it is
+          possible to create a hierarchical "list of lists"
+          structure.
+
+          Every AVI file starts with a "RIFF" object, which is a list
+          of several other required objects. The actual DV data is
+          contained in a "movi" list, each frame is in its own chunk.
+
+          Old AVI files (pre OpenDML V. 1.02) contain only one RIFF
+          chunk of less than 1 GByte size per file. The current
+          format which allow for almost arbitrary sizes can contain
+          several RIFF chunks of less than 1 GByte size. Old software
+          however would only deal with the first RIFF chunk.
+
+          Note that the first entry (FILE) isn�t actually part
+          of the AVI file. I use this (pseudo-) directory entry to
+          keep track of the RIFF chunks and their positions in the
+          AVI file.
+       */
+
+       /* Create the container directory entry */
+
+       file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+       /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
+
+       riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
+       hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
+       avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
+       strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+       strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
+       strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( dvinfo ), strl_list[ 0 ] );
+       if ( index_type & AVI_LARGE_INDEX )
+               indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
+
+       odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
+       dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
+
+       /* align movi list to block */
+       GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
+       num_blocks = length / PADDING_SIZE + 1;
+       length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5?
+       junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+
+       movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+
+       /* The ix00 chunk will be added dynamically to the movi_list in avi_write_frame
+                 as needed */
+
+       ix_chunk[ 0 ] = -1;
+}
+
+
+/* Write a DV video frame. This is somewhat complex... */
+
+#if 0
+bool AVI1File::WriteFrame( const Frame &frame )
+{
+       int frame_chunk;
+       int junk_chunk;
+       int num_blocks;
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+
+       /* exit if no large index and 1GB reached */
+       if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
+               return false;
+
+       /* Check if we need a new ix00 Standard Index. It has a
+          capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
+          number, we need a new index. The new ix00 chunk is also
+          part of the movi list. */
+
+       if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
+               FlushIndx( 0 );
+
+       /* Write the DV frame data.
+
+          Make a new 00__ chunk for the new frame, write out the
+          frame. */
+
+       frame_chunk = AddDirectoryEntry( make_fourcc( "00__" ), 0, frame.GetFrameSize(), movi_list );
+       if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+       {
+               GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
+               ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+       }
+       WriteChunk( frame_chunk, frame.data );
+       //    num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+       //    length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
+       //    junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+       //    WriteChunk(junk_chunk, g_zeroes);
+
+       if ( index_type & AVI_LARGE_INDEX )
+               UpdateIndx( 0, frame_chunk, 1 );
+       if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+               UpdateIdx1( frame_chunk, 0x10 );
+
+       /* update some variables with the new frame count. */
+
+       if ( isUpdateIdx1 )
+               ++mainHdr.dwTotalFrames;
+       ++streamHdr[ 0 ].dwLength;
+       ++dmlh[ 0 ];
+
+       /* Find out if the current riff list is close to 1 GByte in
+          size. If so, start a new (extended) RIFF. The only allowed
+          item in the new RIFF chunk is a movi list (with video
+          frames and indexes as usual). */
+
+       GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+       if ( length > 0x3f000000 )
+       {
+               /* write idx1 only once and before end of first GB */
+               if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+               {
+                       int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+                       WriteChunk( idx1_chunk, ( void* ) idx1 );
+               }
+               isUpdateIdx1 = false;
+
+               if ( index_type & AVI_LARGE_INDEX )
+               {
+                       /* pad out to 1GB */
+                       //GetDirectoryEntry(riff_list, type, name, length, offset, parent);
+                       //junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, PADDING_1GB - length - 5 * RIFF_HEADERSIZE, riff_list);
+                       //WriteChunk(junk_chunk, g_zeroes);
+
+                       /* padding for alignment */
+                       GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+                       num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
+                       length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
+                       if ( length > 0 )
+                       {
+                               junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+                               WriteChunk( junk_chunk, g_zeroes );
+                       }
+
+                       riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
+                       movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+               }
+       }
+       return true;
+}
+#endif
+
+void AVI1File::WriteRIFF()
+{
+
+       WriteChunk( avih_chunk, ( void* ) & mainHdr );
+       WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
+       WriteChunk( strf_chunk[ 0 ], ( void* ) & dvinfo );
+       WriteChunk( dmlh_chunk, ( void* ) & dmlh );
+
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
+               WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
+       }
+
+       if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+       {
+               int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+               WriteChunk( idx1_chunk, ( void* ) idx1 );
+       }
+
+       RIFFFile::WriteRIFF();
+}
+
+
+AVI2File::AVI2File() : AVIFile()
+{}
+
+
+AVI2File::~AVI2File()
+{}
+
+
+/* Initialize the AVI structure to its initial state, either for PAL
+   or NTSC format */
+
+void AVI2File::Init( int format, int sampleFrequency, int indexType )
+{
+       int num_blocks;
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+
+       assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
+
+       AVIFile::Init( format, sampleFrequency, indexType );
+
+       switch ( format )
+       {
+
+       case AVI_PAL:
+               mainHdr.dwStreams = 2;
+               mainHdr.dwWidth = 720;
+               mainHdr.dwHeight = 576;
+
+               /* Initialize the 'strh' chunk */
+
+               streamHdr[ 0 ].fccType = make_fourcc( "vids" );
+               streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+               streamHdr[ 0 ].dwFlags = 0;
+               streamHdr[ 0 ].wPriority = 0;
+               streamHdr[ 0 ].wLanguage = 0;
+               streamHdr[ 0 ].dwInitialFrames = 0;
+               streamHdr[ 0 ].dwScale = 1;
+               streamHdr[ 0 ].dwRate = 25;
+               streamHdr[ 0 ].dwStart = 0;
+               streamHdr[ 0 ].dwLength = 0;
+               streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
+               streamHdr[ 0 ].dwQuality = -1;
+               streamHdr[ 0 ].dwSampleSize = 0;
+               streamHdr[ 0 ].rcFrame.top = 0;
+               streamHdr[ 0 ].rcFrame.bottom = 0;
+               streamHdr[ 0 ].rcFrame.left = 0;
+               streamHdr[ 0 ].rcFrame.right = 0;
+
+               bitmapinfo.biSize = sizeof( bitmapinfo );
+               bitmapinfo.biWidth = 720;
+               bitmapinfo.biHeight = 576;
+               bitmapinfo.biPlanes = 1;
+               bitmapinfo.biBitCount = 24;
+               bitmapinfo.biCompression = make_fourcc( "dvsd" );
+               bitmapinfo.biSizeImage = 144000;
+               bitmapinfo.biXPelsPerMeter = 0;
+               bitmapinfo.biYPelsPerMeter = 0;
+               bitmapinfo.biClrUsed = 0;
+               bitmapinfo.biClrImportant = 0;
+
+               streamHdr[ 1 ].fccType = make_fourcc( "auds" );
+               streamHdr[ 1 ].fccHandler = 0;
+               streamHdr[ 1 ].dwFlags = 0;
+               streamHdr[ 1 ].wPriority = 0;
+               streamHdr[ 1 ].wLanguage = 0;
+               streamHdr[ 1 ].dwInitialFrames = 0;
+               streamHdr[ 1 ].dwScale = 2 * 2;
+               streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
+               streamHdr[ 1 ].dwStart = 0;
+               streamHdr[ 1 ].dwLength = 0;
+               streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
+               streamHdr[ 1 ].dwQuality = -1;
+               streamHdr[ 1 ].dwSampleSize = 2 * 2;
+               streamHdr[ 1 ].rcFrame.top = 0;
+               streamHdr[ 1 ].rcFrame.bottom = 0;
+               streamHdr[ 1 ].rcFrame.left = 0;
+               streamHdr[ 1 ].rcFrame.right = 0;
+
+               break;
+
+       case AVI_NTSC:
+               mainHdr.dwTotalFrames = 0;
+               mainHdr.dwStreams = 2;
+               mainHdr.dwWidth = 720;
+               mainHdr.dwHeight = 480;
+
+               /* Initialize the 'strh' chunk */
+
+               streamHdr[ 0 ].fccType = make_fourcc( "vids" );
+               streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
+               streamHdr[ 0 ].dwFlags = 0;
+               streamHdr[ 0 ].wPriority = 0;
+               streamHdr[ 0 ].wLanguage = 0;
+               streamHdr[ 0 ].dwInitialFrames = 0;
+               streamHdr[ 0 ].dwScale = 1001;
+               streamHdr[ 0 ].dwRate = 30000;
+               streamHdr[ 0 ].dwStart = 0;
+               streamHdr[ 0 ].dwLength = 0;
+               streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
+               streamHdr[ 0 ].dwQuality = -1;
+               streamHdr[ 0 ].dwSampleSize = 0;
+               streamHdr[ 0 ].rcFrame.top = 0;
+               streamHdr[ 0 ].rcFrame.bottom = 0;
+               streamHdr[ 0 ].rcFrame.left = 0;
+               streamHdr[ 0 ].rcFrame.right = 0;
+
+               bitmapinfo.biSize = sizeof( bitmapinfo );
+               bitmapinfo.biWidth = 720;
+               bitmapinfo.biHeight = 480;
+               bitmapinfo.biPlanes = 1;
+               bitmapinfo.biBitCount = 24;
+               bitmapinfo.biCompression = make_fourcc( "dvsd" );
+               bitmapinfo.biSizeImage = 120000;
+               bitmapinfo.biXPelsPerMeter = 0;
+               bitmapinfo.biYPelsPerMeter = 0;
+               bitmapinfo.biClrUsed = 0;
+               bitmapinfo.biClrImportant = 0;
+
+               streamHdr[ 1 ].fccType = make_fourcc( "auds" );
+               streamHdr[ 1 ].fccHandler = 0;
+               streamHdr[ 1 ].dwFlags = 0;
+               streamHdr[ 1 ].wPriority = 0;
+               streamHdr[ 1 ].wLanguage = 0;
+               streamHdr[ 1 ].dwInitialFrames = 1;
+               streamHdr[ 1 ].dwScale = 2 * 2;
+               streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
+               streamHdr[ 1 ].dwStart = 0;
+               streamHdr[ 1 ].dwLength = 0;
+               streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
+               streamHdr[ 1 ].dwQuality = 0;
+               streamHdr[ 1 ].dwSampleSize = 2 * 2;
+               streamHdr[ 1 ].rcFrame.top = 0;
+               streamHdr[ 1 ].rcFrame.bottom = 0;
+               streamHdr[ 1 ].rcFrame.left = 0;
+               streamHdr[ 1 ].rcFrame.right = 0;
+
+               break;
+       }
+       waveformatex.wFormatTag = 1;
+       waveformatex.nChannels = 2;
+       waveformatex.nSamplesPerSec = sampleFrequency;
+       waveformatex.nAvgBytesPerSec = sampleFrequency * 2 * 2;
+       waveformatex.nBlockAlign = 4;
+       waveformatex.wBitsPerSample = 16;
+       waveformatex.cbSize = 0;
+
+       file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+       /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
+
+       riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
+       hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
+       avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
+
+       strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+       strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
+       strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( BITMAPINFOHEADER ), strl_list[ 0 ] );
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
+               ix_chunk[ 0 ] = -1;
+               indx[ 0 ] ->dwChunkId = make_fourcc( "00dc" );
+       }
+
+       strl_list[ 1 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
+       strh_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 1 ] );
+       strf_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( WAVEFORMATEX ) - 2, strl_list[ 1 ] );
+       junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, 2, strl_list[ 1 ] );
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               indx_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 1 ] );
+               ix_chunk[ 1 ] = -1;
+               indx[ 1 ] ->dwChunkId = make_fourcc( "01wb" );
+
+               odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
+               dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
+       }
+
+       /* align movi list to block */
+       GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
+       num_blocks = length / PADDING_SIZE + 1;
+       length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5 headers?
+       junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+
+       movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+
+       idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = make_fourcc( "7Fxx" );
+       idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = 0;
+       idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = 0;
+       idx1->aIndex[ idx1->nEntriesInUse ].dwSize = 0;
+       idx1->nEntriesInUse++;
+}
+
+
+void AVI2File::WriteRIFF()
+{
+       WriteChunk( avih_chunk, ( void* ) & mainHdr );
+       WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
+       WriteChunk( strf_chunk[ 0 ], ( void* ) & bitmapinfo );
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               WriteChunk( dmlh_chunk, ( void* ) & dmlh );
+               WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
+               WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
+       }
+       WriteChunk( strh_chunk[ 1 ], ( void* ) & streamHdr[ 1 ] );
+       WriteChunk( strf_chunk[ 1 ], ( void* ) & waveformatex );
+       if ( index_type & AVI_LARGE_INDEX )
+       {
+               WriteChunk( indx_chunk[ 1 ], ( void* ) indx[ 1 ] );
+               WriteChunk( ix_chunk[ 1 ], ( void* ) ix[ 1 ] );
+       }
+
+       if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+       {
+               int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+               WriteChunk( idx1_chunk, ( void* ) idx1 );
+       }
+       RIFFFile::WriteRIFF();
+}
+
+
+/** Write a DV video frame
+    \param frame the frame to write
+*/
+
+#if 0
+bool AVI2File::WriteFrame( const Frame &frame )
+{
+       int audio_chunk;
+       int frame_chunk;
+       int junk_chunk;
+       char soundbuf[ 20000 ];
+       int     audio_size;
+       int num_blocks;
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+
+       /* exit if no large index and 1GB reached */
+       if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
+               return false;
+
+       /* Check if we need a new ix00 Standard Index. It has a
+          capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
+          number, we need a new index. The new ix00 chunk is also
+          part of the movi list. */
+
+       if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
+       {
+               FlushIndx( 0 );
+               FlushIndx( 1 );
+       }
+
+       /* Write audio data if we have it */
+
+       audio_size = frame.ExtractAudio( soundbuf );
+       if ( audio_size > 0 )
+       {
+               audio_chunk = AddDirectoryEntry( make_fourcc( "01wb" ), 0, audio_size, movi_list );
+               if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+               {
+                       GetDirectoryEntry( audio_chunk, type, name, length, offset, parent );
+                       ix[ 1 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+               }
+               WriteChunk( audio_chunk, soundbuf );
+               //        num_blocks = (audio_size + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+               //        length = num_blocks * PADDING_SIZE - audio_size - 2 * RIFF_HEADERSIZE;
+               //        junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+               //        WriteChunk(junk_chunk, g_zeroes);
+               if ( index_type & AVI_LARGE_INDEX )
+                       UpdateIndx( 1, audio_chunk, audio_size / waveformatex.nChannels / 2 );
+               if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+                       UpdateIdx1( audio_chunk, 0x00 );
+               streamHdr[ 1 ].dwLength += audio_size / waveformatex.nChannels / 2;
+
+       }
+
+       /* Write video data */
+
+       frame_chunk = AddDirectoryEntry( make_fourcc( "00dc" ), 0, frame.GetFrameSize(), movi_list );
+       if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
+       {
+               GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
+               ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
+       }
+       WriteChunk( frame_chunk, frame.data );
+       //    num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
+       //    length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
+       //    junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
+       //    WriteChunk(junk_chunk, g_zeroes);
+       if ( index_type & AVI_LARGE_INDEX )
+               UpdateIndx( 0, frame_chunk, 1 );
+       if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+               UpdateIdx1( frame_chunk, 0x10 );
+
+       /* update some variables with the new frame count. */
+
+       if ( isUpdateIdx1 )
+               ++mainHdr.dwTotalFrames;
+       ++streamHdr[ 0 ].dwLength;
+       ++dmlh[ 0 ];
+
+       /* Find out if the current riff list is close to 1 GByte in
+          size. If so, start a new (extended) RIFF. The only allowed
+          item in the new RIFF chunk is a movi list (with video
+          frames and indexes as usual). */
+
+       GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+       if ( length > 0x3f000000 )
+       {
+
+               /* write idx1 only once and before end of first GB */
+               if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
+               {
+                       int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
+                       WriteChunk( idx1_chunk, ( void* ) idx1 );
+               }
+               isUpdateIdx1 = false;
+
+               if ( index_type & AVI_LARGE_INDEX )
+               {
+                       /* padding for alignment */
+                       GetDirectoryEntry( riff_list, type, name, length, offset, parent );
+                       num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
+                       length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
+                       if ( length > 0 )
+                       {
+                               junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
+                               WriteChunk( junk_chunk, g_zeroes );
+                       }
+
+                       riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
+                       movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
+               }
+       }
+       return true;
+}
+#endif
+
+void AVI1File::setDVINFO( DVINFO &info )
+{
+       // do not do this until debugged audio against DirectShow
+       return ;
+
+       dvinfo.dwDVAAuxSrc = info.dwDVAAuxSrc;
+       dvinfo.dwDVAAuxCtl = info.dwDVAAuxCtl;
+       dvinfo.dwDVAAuxSrc1 = info.dwDVAAuxSrc1;
+       dvinfo.dwDVAAuxCtl1 = info.dwDVAAuxCtl1;
+       dvinfo.dwDVVAuxSrc = info.dwDVVAuxSrc;
+       dvinfo.dwDVVAuxCtl = info.dwDVVAuxCtl;
+}
+
+
+void AVI2File::setDVINFO( DVINFO &info )
+{}
+
+void AVIFile::setFccHandler( FOURCC type, FOURCC handler )
+{
+       for ( int i = 0; i < mainHdr.dwStreams; i++ )
+       {
+               if ( streamHdr[ i ].fccType == type )
+               {
+                       int k, j = 0;
+                       FOURCC strf = make_fourcc( "strf" );
+                       BITMAPINFOHEADER bih;
+
+                       streamHdr[ i ].fccHandler = handler;
+
+                       while ( ( k = FindDirectoryEntry( strf, j++ ) ) != -1 )
+                       {
+                               ReadChunk( k, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
+                               bih.biCompression = handler;
+                       }
+               }
+       }
+}
+
+bool AVIFile::getStreamFormat( void* data, FOURCC type )
+{
+       int i, j = 0;
+       FOURCC strh = make_fourcc( "strh" );
+       FOURCC strf = make_fourcc( "strf" );
+       AVIStreamHeader avi_stream_header;
+       bool result = false;
+
+       while ( ( result == false ) && ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
+       {
+               ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
+               if ( avi_stream_header.fccType == type )
+               {
+                       FOURCC chunkID;
+                       int size;
+
+                       pthread_mutex_lock( &file_mutex );
+                       fail_neg( read( fd, &chunkID, sizeof( FOURCC ) ) );
+                       if ( chunkID == strf )
+                       {
+                               fail_neg( read( fd, &size, sizeof( int ) ) );
+                               fail_neg( read( fd, data, size ) );
+                               result = true;
+                       }
+                       pthread_mutex_unlock( &file_mutex );
+               }
+       }
+       return result;
+}
diff --git a/src/modules/kino/avi.h b/src/modules/kino/avi.h
new file mode 100644 (file)
index 0000000..d22d5e6
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+* avi.h library for AVI file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Tag: $Name$
+*
+* Change log:
+* 
+* $Log$
+* Revision 1.4  2005/07/25 07:21:39  lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.3  2005/06/21 20:59:39  lilo_booter
+* src/framework/mlt_consumer.c src/framework/mlt_consumer.h
+* + Added a general profile handling for size, aspect ratio and display ratio
+*
+* src/framework/mlt_producer.c
+* + Correction to aspect ratio properties
+*
+* src/inigo/inigo.c
+* + Minimalist support for sdl_preview (still not very good)
+*
+* src/modules/avformat/consumer_avformat.c
+* + Takes consumer profile into account
+*
+* src/modules/core/filter_resize.c
+* + Corrections for synthesised producers and aspect ratio (inherits from consumer)
+*
+* src/modules/core/producer_colour.c
+* src/modules/core/producer_noise.c
+* src/modules/gtk2/producer_pango.c
+* + Ensures that resize picks up consumer aspect ratio
+*
+* src/modules/dv/consumer_libdv.c
+* + Honour wide screen output
+*
+* src/modules/gtk2/producer_pixbuf.c
+* + Correction for 1:1 aspect ratio
+*
+* src/modules/kino/Makefile
+* src/modules/kino/avi.cc
+* src/modules/kino/avi.h
+* src/modules/kino/configure
+* src/modules/kino/filehandler.cc
+* + Attempt to allow mov dv files to provide audio
+*
+* src/modules/sdl/consumer_sdl.c
+* src/modules/sdl/consumer_sdl_preview.c
+* src/modules/sdl/consumer_sdl_still.c
+* + Takes consumer profile into account
+*
+* Revision 1.2  2005/04/15 14:37:03  lilo_booter
+* Minor correction
+*
+* Revision 1.1  2005/04/15 14:28:26  lilo_booter
+* Initial version
+*
+* Revision 1.16  2005/04/01 23:43:10  ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.15  2004/10/11 01:37:11  ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.14  2003/11/25 23:00:52  ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.13  2003/10/21 16:34:32  ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.11.2.5  2003/07/24 14:13:57  ddennedy
+* support for distinct audio stream in type2 AVI and Quicktime; support for more DV FOURCCs
+*
+* Revision 1.11.2.4  2003/06/10 23:53:36  ddennedy
+* Daniel Kobras' WriteFrame error handling and automatic OpenDML, bugfixes in scene list updates, export AV/C Record
+*
+* Revision 1.11.2.3  2003/02/20 21:59:57  ddennedy
+* bugfixes to capture and AVI
+*
+* Revision 1.11.2.2  2003/01/13 05:15:31  ddennedy
+* added More Info panel and supporting methods
+*
+* Revision 1.11.2.1  2002/11/25 04:48:31  ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.11  2002/10/08 07:46:41  ddennedy
+* AVI bugfixes, compatibility, optimization, warn bad file in capture and export dv file, allow no mplex
+*
+* Revision 1.10  2002/05/17 08:04:25  ddennedy
+* revert const-ness of Frame references in Frame, FileHandler, and AVI classes
+*
+* Revision 1.9  2002/05/15 04:39:35  ddennedy
+* bugfixes to dv2 AVI write, audio export, Xv init
+*
+* Revision 1.8  2002/04/29 05:09:22  ddennedy
+* raw dv file support, Frame::ExtractAudio uses libdv, audioScrub prefs
+*
+* Revision 1.7  2002/04/09 06:53:42  ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.7  2002/03/25 21:34:25  arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.6  2002/03/10 13:29:41  arne
+* more changes for 64 bit access
+*
+* Revision 1.5  2002/03/09 17:59:28  arne
+* moved index routines to AVIFile
+*
+* Revision 1.4  2002/03/09 10:26:26  arne
+* improved constructors and assignment operator
+*
+* Revision 1.3  2002/03/09 08:55:57  arne
+* moved a few variables to AVIFile
+*
+* Revision 1.2  2002/03/04 19:22:43  arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1  2002/03/03 19:08:08  arne
+* import of version 1.01
+*
+*/
+
+/** Common AVI declarations
+    Some of this comes from the public domain AVI specification, which
+    explains the microsoft-style definitions.
+    \file avi.h
+*/
+
+#ifndef _AVI_H
+#define _AVI_H 1
+
+#include <stdint.h>
+#include "riff.h"
+
+#define PACKED(x)      __attribute__((packed)) x
+
+#define AVI_SMALL_INDEX (0x01)
+#define AVI_LARGE_INDEX (0x02)
+#define KINO_AVI_INDEX_OF_INDEXES (0x00)
+#define KINO_AVI_INDEX_OF_CHUNKS (0x01)
+#define AVI_INDEX_2FIELD (0x01)
+
+enum { AVI_PAL, AVI_NTSC, AVI_AUDIO_48KHZ, AVI_AUDIO_44KHZ, AVI_AUDIO_32KHZ };
+
+/** Declarations of the main AVI file header
+    The contents of this struct goes into the 'avih' chunk.  */
+
+typedef struct
+{
+       /// frame display rate (or 0L)
+       DWORD dwMicroSecPerFrame;
+
+       /// max. transfer rate
+       DWORD dwMaxBytesPerSec;
+
+       /// pad to multiples of this size, normally 2K
+       DWORD dwPaddingGranularity;
+
+       /// the ever-present flags
+       DWORD dwFlags;
+
+       /// # frames in file
+       DWORD dwTotalFrames;
+       DWORD dwInitialFrames;
+       DWORD dwStreams;
+       DWORD dwSuggestedBufferSize;
+
+       DWORD dwWidth;
+       DWORD dwHeight;
+
+       DWORD dwReserved[ 4 ];
+}
+PACKED(MainAVIHeader);
+
+typedef struct
+{
+       WORD top, bottom, left, right;
+}
+PACKED(RECT);
+
+/** Declaration of a stream header
+    The contents of this struct goes into the 'strh' header. */
+
+typedef struct
+{
+       FOURCC fccType;
+       FOURCC fccHandler;
+       DWORD dwFlags;                /* Contains AVITF_* flags */
+       WORD wPriority;
+       WORD wLanguage;
+       DWORD dwInitialFrames;
+       DWORD dwScale;
+       DWORD dwRate;                 /* dwRate / dwScale == samples/second */
+       DWORD dwStart;
+       DWORD dwLength;               /* In units above... */
+       DWORD dwSuggestedBufferSize;
+       DWORD dwQuality;
+       DWORD dwSampleSize;
+       RECT rcFrame;
+}
+PACKED(AVIStreamHeader);
+
+typedef struct
+{
+       DWORD dwDVAAuxSrc;
+       DWORD dwDVAAuxCtl;
+       DWORD dwDVAAuxSrc1;
+       DWORD dwDVAAuxCtl1;
+       DWORD dwDVVAuxSrc;
+       DWORD dwDVVAuxCtl;
+       DWORD dwDVReserved[ 2 ];
+}
+PACKED(DVINFO);
+
+typedef struct
+{
+       DWORD biSize;
+       LONG biWidth;
+       LONG biHeight;
+       WORD biPlanes;
+       WORD biBitCount;
+       DWORD biCompression;
+       DWORD biSizeImage;
+       LONG biXPelsPerMeter;
+       LONG biYPelsPerMeter;
+       DWORD biClrUsed;
+       DWORD biClrImportant;
+       char dummy[ 1040 ];
+}
+PACKED(BITMAPINFOHEADER);
+
+typedef struct
+{
+       WORD wFormatTag;
+       WORD nChannels;
+       DWORD nSamplesPerSec;
+       DWORD nAvgBytesPerSec;
+       WORD nBlockAlign;
+       WORD wBitsPerSample;
+       WORD cbSize;
+       WORD dummy;
+}
+PACKED(WAVEFORMATEX);
+
+typedef struct
+{
+       WORD wLongsPerEntry;
+       BYTE bIndexSubType;
+       BYTE bIndexType;
+       DWORD nEntriesInUse;
+       FOURCC dwChunkId;
+       DWORD dwReserved[ 3 ];
+       struct avisuperindex_entry
+       {
+               QUADWORD qwOffset;
+               DWORD dwSize;
+               DWORD dwDuration;
+       }
+       aIndex[ 3198 ];
+}
+PACKED(AVISuperIndex);
+
+typedef struct
+{
+       WORD wLongsPerEntry;
+       BYTE bIndexSubType;
+       BYTE bIndexType;
+       DWORD nEntriesInUse;
+       FOURCC dwChunkId;
+       QUADWORD qwBaseOffset;
+       DWORD dwReserved;
+       struct avifieldindex_entry
+       {
+               DWORD dwOffset;
+               DWORD dwSize;
+       }
+       aIndex[ 17895 ];
+}
+PACKED(AVIStdIndex);
+
+typedef struct
+{
+       struct avisimpleindex_entry
+       {
+               FOURCC  dwChunkId;
+               DWORD   dwFlags;
+               DWORD   dwOffset;
+               DWORD   dwSize;
+       }
+       aIndex[ 20000 ];
+       DWORD   nEntriesInUse;
+}
+PACKED(AVISimpleIndex);
+
+typedef struct
+{
+       DWORD dirEntryType;
+       DWORD dirEntryName;
+       DWORD dirEntryLength;
+       size_t dirEntryOffset;
+       int dirEntryWrittenFlag;
+       int dirEntryParentList;
+}
+AviDirEntry;
+
+
+/** base class for all AVI type files
+    It contains methods and members which are the same in all AVI type files regardless of the particular compression, number
+    of streams etc. 
+    The AVIFile class also contains methods for handling several indexes to the video frame content. */
+
+class AVIFile : public RIFFFile
+{
+public:
+       AVIFile();
+       AVIFile( const AVIFile& );
+       virtual ~AVIFile();
+       virtual AVIFile& operator=( const AVIFile& );
+
+       virtual void Init( int format, int sampleFrequency, int indexType );
+       virtual int GetDVFrameInfo( off_t &offset, int &size, int frameNum );
+       virtual int GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID );
+       virtual int GetDVFrame( uint8_t *data, int frameNum );
+       virtual int getFrame( void *data, int frameNum, FOURCC chunkID );
+       virtual int GetTotalFrames() const;
+       virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const;
+       //virtual bool WriteFrame( const Frame &frame ) { return false; }
+       virtual void ParseList( int parent );
+       virtual void ParseRIFF( void );
+       virtual void ReadIndex( void );
+       virtual void WriteRIFF( void )
+       { }
+       virtual void FlushIndx( int stream );
+       virtual void UpdateIndx( int stream, int chunk, int duration );
+       virtual void UpdateIdx1( int chunk, int flags );
+       virtual bool verifyStreamFormat( FOURCC type );
+       virtual bool verifyStream( FOURCC type );
+       virtual bool isOpenDML( void );
+       virtual void setDVINFO( DVINFO& )
+       { }
+       virtual void setFccHandler( FOURCC type, FOURCC handler );
+       virtual bool getStreamFormat( void* data, FOURCC type );
+
+protected:
+       MainAVIHeader mainHdr;
+       AVISimpleIndex *idx1;
+       int file_list;
+       int riff_list;
+       int hdrl_list;
+       int avih_chunk;
+       int movi_list;
+       int junk_chunk;
+       int idx1_chunk;
+
+       AVIStreamHeader streamHdr[ 2 ];
+       AVISuperIndex *indx[ 2 ];
+       AVIStdIndex *ix[ 2 ];
+       int indx_chunk[ 2 ];
+       int ix_chunk[ 2 ];
+       int strl_list[ 2 ];
+       int strh_chunk[ 2 ];
+       int strf_chunk[ 2 ];
+
+       int index_type;
+       int current_ix00;
+
+       DWORD dmlh[ 62 ];
+       int odml_list;
+       int dmlh_chunk;
+       bool isUpdateIdx1;
+
+};
+
+
+/** writing Type 1 DV AVIs
+*/
+
+class AVI1File : public AVIFile
+{
+public:
+       AVI1File();
+       virtual ~AVI1File();
+
+       virtual void Init( int format, int sampleFrequency, int indexType );
+       //virtual bool WriteFrame( const Frame &frame );
+       virtual void WriteRIFF( void );
+       virtual void setDVINFO( DVINFO& );
+
+private:
+       DVINFO dvinfo;
+
+       AVI1File( const AVI1File& );
+       AVI1File& operator=( const AVI1File& );
+};
+
+
+/** writing Type 2 (separate audio data) DV AVIs
+This file type contains both audio and video tracks. It is therefore more compatible
+to certain Windows programs, which expect any AVI having both audio and video tracks.
+The video tracks contain the raw DV data (as in type 1) and the extracted audio tracks.
+Note that because the DV data contains audio information anyway, this means duplication
+of data and a slight increase of file size.
+*/
+
+class AVI2File : public AVIFile
+{
+public:
+       AVI2File();
+       virtual ~AVI2File();
+
+       virtual void Init( int format, int sampleFrequency, int indexType );
+       //virtual bool WriteFrame( const Frame &frame );
+       virtual void WriteRIFF( void );
+       virtual void setDVINFO( DVINFO& );
+
+private:
+       BITMAPINFOHEADER bitmapinfo;
+       WAVEFORMATEX waveformatex;
+
+       AVI2File( const AVI2File& );
+       AVI2File& operator=( const AVI2File& );
+};
+#endif
diff --git a/src/modules/kino/configure b/src/modules/kino/configure
new file mode 100755 (executable)
index 0000000..f6524f3
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       if [ "$targetos" = "Darwin" ]
+       then
+               echo "- does not build on Darwin: disabling"
+               touch ../disable-kino
+               exit 0
+       fi
+
+       # Entirely optional...
+       pkg-config libquicktime 2> /dev/null
+       lqt_disabled=$?
+
+       pkg-config libdv 2> /dev/null
+       libdv_disabled=$?
+
+       echo > config.h
+       [ "$lqt_disabled" = "0" ] && echo "#define HAVE_LIBQUICKTIME" >> config.h
+       [ "$libdv_disabled" = "0" ] && echo "#define HAVE_LIBDV" >> config.h
+       echo > config.mak
+       [ "$lqt_disabled" = "0" ] && echo "HAVE_LIBQUICKTIME=1" >> config.mak
+       [ "$libdv_disabled" = "0" ] && echo "HAVE_LIBDV=1" >> config.mak
+
+       [ "$lqt_disabled" != "0" ] && echo "- libquicktime not found: only enabling dv avi support"
+       [ "$libdv_disabled" != "0" -a "$lqt_disabled" = "0" ] && echo "- libdv not found: mov dv may not have audio"
+       
+       exit 0
+fi
diff --git a/src/modules/kino/endian_types.h b/src/modules/kino/endian_types.h
new file mode 100644 (file)
index 0000000..d66cdb6
--- /dev/null
@@ -0,0 +1,279 @@
+/* <endian_types.h>
+ *
+ * Quick hack to handle endianness and word length issues.
+ * Defines _le, _be, and _ne variants to standard ISO types
+ * like int32_t, that are stored in little-endian, big-endian,
+ * and native-endian byteorder in memory, respectively.
+ * Caveat: int32_le_t and friends cannot be used in vararg
+ * functions like printf() without an explicit cast.
+ *
+ * Copyright (c) 2003-2005 Daniel Kobras <kobras@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ENDIAN_TYPES_H
+#define _ENDIAN_TYPES_H
+
+/* Needed for BYTE_ORDER and BIG/LITTLE_ENDIAN macros. */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#ifndef __FreeBSD__
+# include <endian.h>
+#else
+# include <sys/endian.h>
+#endif /* __FreeBSD__ */
+# undef  _BSD_SOURCE
+#else
+#ifndef __FreeBSD__
+# include <endian.h>
+#else
+# include <sys/endian.h>
+#endif /* __FreeBSD__ */
+#endif
+
+#include <sys/types.h>
+#ifndef __FreeBSD__
+#include <byteswap.h>
+#else
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif /* __FreeBSD__ */
+
+static inline int8_t bswap(const int8_t& x)
+{
+       return x;
+}
+
+static inline u_int8_t bswap(const u_int8_t& x)
+{
+       return x;
+}
+
+static inline int16_t bswap(const int16_t& x)
+{
+       return bswap_16(x);
+}
+
+static inline u_int16_t bswap(const u_int16_t& x)
+{
+       return bswap_16(x);
+}
+
+static inline int32_t bswap(const int32_t& x)
+{
+       return bswap_32(x);
+}
+
+static inline u_int32_t bswap(const u_int32_t& x)
+{
+       return bswap_32(x);
+}
+
+static inline int64_t bswap(const int64_t& x)
+{
+       return bswap_64(x);
+}
+
+static inline u_int64_t bswap(const u_int64_t& x)
+{
+       return bswap_64(x);
+}
+
+#define le_to_cpu      cpu_to_le
+#define be_to_cpu      cpu_to_be
+
+template <class T> static inline T cpu_to_le(const T& x)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       return x;
+#else
+       return bswap(x);
+#endif
+}
+
+template <class T> static inline T cpu_to_be(const T& x)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+       return bswap(x);
+#else
+       return x;
+#endif
+}
+
+template <class T> class le_t {
+       T       m;
+       T       read() const {
+               return le_to_cpu(m);
+       };
+       void    write(const T& n) {
+               m = cpu_to_le(n);
+       };
+public:
+       le_t(void) {
+               m = 0;
+       };
+       le_t(const T& o) {
+               write(o);
+       };
+       operator T() const {
+               return read();
+       };
+       le_t<T> operator++() {
+               write(read() + 1);
+               return *this;
+       };
+       le_t<T> operator++(int) {
+               write(read() + 1);
+               return *this;
+       };
+       le_t<T> operator--() {
+               write(read() - 1);
+               return *this;
+       };
+       le_t<T> operator--(int) {
+               write(read() - 1);
+               return *this;
+       };
+       le_t<T>& operator+=(const T& t) {
+               write(read() + t);
+               return *this;
+       };
+       le_t<T>& operator-=(const T& t) {
+               write(read() - t);
+               return *this;
+       };
+       le_t<T>& operator&=(const le_t<T>& t) {
+               m &= t.m;
+               return *this;
+       };
+       le_t<T>& operator|=(const le_t<T>& t) {
+               m |= t.m;
+               return *this;
+       };
+} __attribute__((packed));
+
+/* Just copy-and-pasted from le_t. Too lazy to do it right. */
+
+template <class T> class be_t {
+       T       m;
+       T       read() const {
+               return be_to_cpu(m);
+       };
+       void    write(const T& n) {
+               m = cpu_to_be(n);
+       };
+public:
+       be_t(void) {
+               m = 0;
+       };
+       be_t(const T& o) {
+               write(o);
+       };
+       operator T() const {
+               return read();
+       };
+       be_t<T> operator++() {
+               write(read() + 1);
+               return *this;
+       };
+       be_t<T> operator++(int) {
+               write(read() + 1);
+               return *this;
+       };
+       be_t<T> operator--() {
+               write(read() - 1);
+               return *this;
+       };
+       be_t<T> operator--(int) {
+               write(read() - 1);
+               return *this;
+       };
+       be_t<T>& operator+=(const T& t) {
+               write(read() + t);
+               return *this;
+       };
+       be_t<T>& operator-=(const T& t) {
+               write(read() - t);
+               return *this;
+       };
+       be_t<T>& operator&=(const be_t<T>& t) {
+               m &= t.m;
+               return *this;
+       };
+       be_t<T>& operator|=(const be_t<T>& t) {
+               m |= t.m;
+               return *this;
+       };
+} __attribute__((packed));
+
+/* Define types of native endianness similar to the little and big endian
+ * versions below. Not really necessary but useful occasionally to emphasize
+ * endianness of data.
+ */
+
+typedef        int8_t          int8_ne_t;
+typedef        int16_t         int16_ne_t;
+typedef        int32_t         int32_ne_t;
+typedef        int64_t         int64_ne_t;
+typedef        u_int8_t        u_int8_ne_t;
+typedef        u_int16_t       u_int16_ne_t;
+typedef        u_int32_t       u_int32_ne_t;
+typedef        u_int64_t       u_int64_ne_t;
+
+
+/* The classes work on their native endianness as well, but obviously
+ * introduce some overhead.  Use the faster typedefs to native types
+ * therefore, unless you're debugging.
+ */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+typedef        int8_ne_t       int8_le_t;
+typedef        int16_ne_t      int16_le_t;
+typedef        int32_ne_t      int32_le_t;
+typedef        int64_ne_t      int64_le_t;
+typedef        u_int8_ne_t     u_int8_le_t;
+typedef        u_int16_ne_t    u_int16_le_t;
+typedef        u_int32_ne_t    u_int32_le_t;
+typedef        u_int64_ne_t    u_int64_le_t;
+typedef int8_t         int8_be_t;
+typedef be_t<int16_t>  int16_be_t;
+typedef be_t<int32_t>  int32_be_t;
+typedef be_t<int64_t>  int64_be_t;
+typedef u_int8_t       u_int8_be_t;
+typedef be_t<u_int16_t>        u_int16_be_t;
+typedef be_t<u_int32_t>        u_int32_be_t;
+typedef be_t<u_int64_t>        u_int64_be_t;
+#else
+typedef        int8_ne_t       int8_be_t;
+typedef        int16_ne_t      int16_be_t;
+typedef        int32_ne_t      int32_be_t;
+typedef        int64_ne_t      int64_be_t;
+typedef        u_int8_ne_t     u_int8_be_t;
+typedef        u_int16_ne_t    u_int16_be_t;
+typedef        u_int32_ne_t    u_int32_be_t;
+typedef        u_int64_ne_t    u_int64_be_t;
+typedef int8_t         int8_le_t;
+typedef le_t<int16_t>  int16_le_t;
+typedef le_t<int32_t>  int32_le_t;
+typedef le_t<int64_t>  int64_le_t;
+typedef u_int8_t       u_int8_le_t;
+typedef le_t<u_int16_t>        u_int16_le_t;
+typedef le_t<u_int32_t>        u_int32_le_t;
+typedef le_t<u_int64_t>        u_int64_le_t;
+#endif
+
+#endif
diff --git a/src/modules/kino/error.cc b/src/modules/kino/error.cc
new file mode 100644 (file)
index 0000000..e2b8510
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+* error.cc Error handling
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// C++ includes
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+using std::ostringstream;
+using std::string;
+using std::endl;
+using std::ends;
+using std::cerr;
+
+// C includes
+
+#include <errno.h>
+#include <string.h>
+
+// local includes
+
+#include "error.h"
+
+void real_fail_neg( int eval, const char *eval_str, const char *func, const char *file, int line )
+{
+       if ( eval < 0 )
+       {
+               string exc;
+               ostringstream sb;
+
+               sb << file << ":" << line << ": In function \"" << func << "\": \"" << eval_str << "\" evaluated to " << eval;
+               if ( errno != 0 )
+                       sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")";
+               sb << ends;
+               exc = sb.str();
+               cerr << exc << endl;
+               throw exc;
+       }
+}
+
+
+/** error handler for NULL result codes
+    Whenever this is called with a NULL argument, it will throw an
+    exception. Typically used with functions like malloc() and new().
+*/
+
+void real_fail_null( const void *eval, const char *eval_str, const char *func, const char *file, int line )
+{
+       if ( eval == NULL )
+       {
+
+               string exc;
+               ostringstream sb;
+
+               sb << file << ":" << line << ": In function \"" << func << "\": " << eval_str << " is NULL" << ends;
+               exc = sb.str();
+               cerr << exc << endl;
+               throw exc;
+       }
+}
+
+
+void real_fail_if( bool eval, const char *eval_str, const char *func, const char *file, int line )
+{
+       if ( eval == true )
+       {
+
+               string exc;
+               ostringstream sb;
+
+               sb << file << ":" << line << ": In function \"" << func << "\": condition \"" << eval_str << "\" is true";
+               if ( errno != 0 )
+                       sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")";
+               sb << ends;
+               exc = sb.str();
+               cerr << exc << endl;
+               throw exc;
+       }
+}
diff --git a/src/modules/kino/error.h b/src/modules/kino/error.h
new file mode 100644 (file)
index 0000000..9c04894
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+* error.h Error handling
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _ERROR_H
+#define _ERROR_H 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+       /*
+        * Should check for gcc/g++ and version > 2.6 I suppose
+        */
+#ifndef        __ASSERT_FUNCTION
+#   define __ASSERT_FUNCTION   __PRETTY_FUNCTION__
+#endif
+
+#define fail_neg(eval)  real_fail_neg  (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+#define fail_null(eval) real_fail_null (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+#define fail_if(eval)   real_fail_if   (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__)
+
+       void real_fail_neg ( int eval, const char * eval_str, const char * func, const char * file, int line );
+       void real_fail_null ( const void * eval, const char * eval_str, const char * func, const char * file, int line );
+       void real_fail_if ( bool eval, const char * eval_str, const char * func, const char * file, int line );
+
+       extern void sigpipe_clear( );
+       extern int sigpipe_get( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/modules/kino/factory.c b/src/modules/kino/factory.c
new file mode 100644 (file)
index 0000000..13e8def
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( producer_type, "kino", producer_kino_init );
+}
diff --git a/src/modules/kino/filehandler.cc b/src/modules/kino/filehandler.cc
new file mode 100644 (file)
index 0000000..e5e552c
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+* filehandler.cc -- saving DV data into different file formats
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+
+extern "C" {
+#include <framework/mlt_frame.h>
+}
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+using std::cerr;
+using std::endl;
+using std::ostringstream;
+using std::setw;
+using std::setfill;
+
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+
+// libdv header files
+#ifdef HAVE_LIBDV
+#include <libdv/dv.h>
+#endif
+
+#include "filehandler.h"
+#include "error.h"
+#include "riff.h"
+#include "avi.h"
+
+FileTracker *FileTracker::instance = NULL;
+
+FileTracker::FileTracker( ) : mode( CAPTURE_MOVIE_APPEND )
+{
+       cerr << ">> Constructing File Capture tracker" << endl;
+}
+
+FileTracker::~FileTracker( )
+{
+       cerr << ">> Destroying File Capture tracker" << endl;
+}
+
+FileTracker &FileTracker::GetInstance( )
+{
+       if ( instance == NULL )
+               instance = new FileTracker();
+
+       return *instance;
+}
+
+void FileTracker::SetMode( FileCaptureMode mode )
+{
+       this->mode = mode;
+}
+
+FileCaptureMode FileTracker::GetMode( )
+{
+       return this->mode;
+}
+
+char *FileTracker::Get( int index )
+{
+       return list[ index ];
+}
+
+void FileTracker::Add( const char *file )
+{
+       if ( this->mode != CAPTURE_IGNORE )
+       {
+               cerr << ">>>> Registering " << file << " with the tracker" << endl;
+               list.push_back( strdup( file ) );
+       }
+}
+
+unsigned int FileTracker::Size( )
+{
+       return list.size();
+}
+
+void FileTracker::Clear( )
+{
+       while ( Size() > 0 )
+       {
+               free( list[ Size() - 1 ] );
+               list.pop_back( );
+       }
+       this->mode = CAPTURE_MOVIE_APPEND;
+}
+
+FileHandler::FileHandler() : done( false ), autoSplit( false ), maxFrameCount( 999999 ),
+               framesWritten( 0 ), filename( "" )
+{
+       /* empty body */
+}
+
+
+FileHandler::~FileHandler()
+{
+       /* empty body */
+}
+
+
+bool FileHandler::GetAutoSplit() const
+{
+       return autoSplit;
+}
+
+
+bool FileHandler::GetTimeStamp() const
+{
+       return timeStamp;
+}
+
+
+string FileHandler::GetBaseName() const
+{
+       return base;
+}
+
+
+string FileHandler::GetExtension() const
+{
+       return extension;
+}
+
+
+int FileHandler::GetMaxFrameCount() const
+{
+       return maxFrameCount;
+}
+
+off_t FileHandler::GetMaxFileSize() const
+{
+       return maxFileSize;
+}
+
+string FileHandler::GetFilename() const
+{
+       return filename;
+}
+
+
+void FileHandler::SetAutoSplit( bool flag )
+{
+       autoSplit = flag;
+}
+
+
+void FileHandler::SetTimeStamp( bool flag )
+{
+       timeStamp = flag;
+}
+
+
+void FileHandler::SetBaseName( const string& s )
+{
+       base = s;
+}
+
+
+void FileHandler::SetMaxFrameCount( int count )
+{
+       assert( count >= 0 );
+       maxFrameCount = count;
+}
+
+
+void FileHandler::SetEveryNthFrame( int every )
+{
+       assert ( every > 0 );
+
+       everyNthFrame = every;
+}
+
+
+void FileHandler::SetMaxFileSize( off_t size )
+{
+       assert ( size >= 0 );
+       maxFileSize = size;
+}
+
+
+#if 0
+void FileHandler::SetSampleFrame( const Frame& sample )
+{
+       /* empty body */
+}
+#endif
+
+bool FileHandler::Done()
+{
+       return done;
+}
+
+#if 0
+bool FileHandler::WriteFrame( const Frame& frame )
+{
+       static TimeCode prevTimeCode;
+       TimeCode timeCode;
+
+       /* If the user wants autosplit, start a new file if a
+          new recording is detected. */
+       prevTimeCode.sec = -1;
+       frame.GetTimeCode( timeCode );
+       int time_diff = timeCode.sec - prevTimeCode.sec;
+       bool discontinuity = prevTimeCode.sec != -1 && ( time_diff > 1 || ( time_diff < 0 && time_diff > -59 ) );
+       if ( FileIsOpen() && GetAutoSplit() == true && ( frame.IsNewRecording() || discontinuity ) )
+       {
+               Close();
+       }
+
+       if ( FileIsOpen() == false )
+       {
+
+               string filename;
+               static int counter = 0;
+
+               if ( GetTimeStamp() == true )
+               {
+                       ostringstream sb, sb2;
+                       struct tm       date;
+                       string  recDate;
+
+                       if ( ! frame.GetRecordingDate( date ) )
+                       {
+                               struct timeval tv;
+                               struct timezone tz;
+                               gettimeofday( &tv, &tz );
+                               localtime_r( static_cast< const time_t * >( &tv.tv_sec ), &date );
+                       }
+                       sb << setfill( '0' )
+                       << setw( 4 ) << date.tm_year + 1900 << '.'
+                       << setw( 2 ) << date.tm_mon + 1 << '.'
+                       << setw( 2 ) << date.tm_mday << '_'
+                       << setw( 2 ) << date.tm_hour << '-'
+                       << setw( 2 ) << date.tm_min << '-'
+                       << setw( 2 ) << date.tm_sec;
+                       recDate = sb.str();
+                       sb2 << GetBaseName() << recDate << GetExtension();
+                       filename = sb2.str();
+                       cerr << ">>> Trying " << filename << endl;
+               }
+               else
+               {
+                       struct stat stats;
+                       do
+                       {
+                               ostringstream sb;
+                               sb << GetBaseName() << setfill( '0' ) << setw( 3 ) << ++ counter << GetExtension();
+                               filename = sb.str();
+                               cerr << ">>> Trying " << filename << endl;
+                       }
+                       while ( stat( filename.c_str(), &stats ) == 0 );
+               }
+
+               SetSampleFrame( frame );
+               if ( Create( filename ) == false )
+               {
+                       cerr << ">>> Error creating file!" << endl;
+                       return false;
+               }
+               framesWritten = 0;
+               framesToSkip = 0;
+       }
+
+       /* write frame */
+
+       if ( framesToSkip == 0 )
+       {
+               if ( 0 > Write( frame ) )
+               {
+                       cerr << ">>> Error writing frame!" << endl;
+                       return false;
+               }
+               framesToSkip = everyNthFrame;
+               ++framesWritten;
+       }
+       framesToSkip--;
+
+       /* If the frame count is exceeded, close the current file.
+          If the autosplit flag is set, a new file will be created in the next iteration.
+          If the flag is not set, we are done. */
+
+       if ( ( GetMaxFrameCount() > 0 ) &&
+               ( framesWritten >= GetMaxFrameCount() ) )
+       {
+               Close();
+               done = !GetAutoSplit();
+       }
+
+       /* If the file size could be exceeded by another frame, close the current file.
+          If the autosplit flag is set, a new file will be created on the next iteration.
+          If the flag is not set, we are done. */
+       /* not exact, but should be good enough to prevent going over. */
+       if ( FileIsOpen() )
+       {
+               AudioInfo info;
+               frame.GetAudioInfo( info );
+               if ( ( GetFileSize() > 0 ) && ( GetMaxFileSize() > 0 ) &&
+                       ( GetFileSize() + frame.GetFrameSize() + info.samples * 4 + 12 )
+                       >= GetMaxFileSize() )
+               {                     // 12 = sizeof chunk metadata
+                       Close();
+                       done = !GetAutoSplit();
+               }
+       }
+    prevTimeCode.sec = timeCode.sec;
+       return true;
+}
+#endif
+
+RawHandler::RawHandler() : fd( -1 )
+{
+       extension = ".dv";
+}
+
+
+RawHandler::~RawHandler()
+{
+       Close();
+}
+
+
+bool RawHandler::FileIsOpen()
+{
+       return fd != -1;
+}
+
+
+bool RawHandler::Create( const string& filename )
+{
+       fd = open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 );
+       if ( fd != -1 )
+       {
+               FileTracker::GetInstance().Add( filename.c_str() );
+               this->filename = filename;
+       }
+       return ( fd != -1 );
+}
+
+
+#if 0
+int RawHandler::Write( const Frame& frame )
+{
+       int result = write( fd, frame.data, frame.GetFrameSize() );
+       return result;
+}
+#endif
+
+int RawHandler::Close()
+{
+       if ( fd != -1 )
+       {
+               close( fd );
+               fd = -1;
+       }
+       return 0;
+}
+
+
+off_t RawHandler::GetFileSize()
+{
+       struct stat file_status;
+       fstat( fd, &file_status );
+       return file_status.st_size;
+}
+
+int RawHandler::GetTotalFrames()
+{
+       return GetFileSize() / ( 480 * numBlocks );
+}
+
+
+bool RawHandler::Open( const char *s )
+{
+       unsigned char data[ 4 ];
+       assert( fd == -1 );
+       fd = open( s, O_RDONLY | O_NONBLOCK );
+       if ( fd < 0 )
+               return false;
+       if ( read( fd, data, 4 ) < 0 )
+               return false;
+       lseek( fd, 0, SEEK_SET );
+       numBlocks = ( ( data[ 3 ] & 0x80 ) == 0 ) ? 250 : 300;
+       filename = s;
+       return true;
+
+}
+
+int RawHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       assert( fd != -1 );
+       int size = 480 * numBlocks;
+       if ( frameNum < 0 )
+               return -1;
+       off_t offset = ( ( off_t ) frameNum * ( off_t ) size );
+       fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+       if ( read( fd, data, size ) > 0 )
+               return 0;
+       else
+               return -1;
+}
+
+
+/***************************************************************************/
+
+
+AVIHandler::AVIHandler( int format ) : avi( NULL ), aviFormat( format ), isOpenDML( false ),
+               fccHandler( make_fourcc( "dvsd" ) ), channels( 2 ), isFullyInitialized( false ),
+               audioBuffer( NULL )
+{
+       extension = ".avi";
+       for ( int c = 0; c < 4; c++ )
+               audioChannels[ c ] = NULL;
+}
+
+
+AVIHandler::~AVIHandler()
+{
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       for ( int c = 0; c < 4; c++ )
+       {
+               if ( audioChannels[ c ] != NULL )
+               {
+                       delete audioChannels[ c ];
+                       audioChannels[ c ] = NULL;
+               }
+       }
+
+       delete avi;
+}
+
+#if 0
+void AVIHandler::SetSampleFrame( const Frame& sample )
+{
+       Pack pack;
+       sample.GetAudioInfo( audioInfo );
+       sample.GetVideoInfo( videoInfo );
+
+       sample.GetAAUXPack( 0x50, pack );
+       dvinfo.dwDVAAuxSrc = *( DWORD* ) ( pack.data + 1 );
+       sample.GetAAUXPack( 0x51, pack );
+       dvinfo.dwDVAAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+       sample.GetAAUXPack( 0x52, pack );
+       dvinfo.dwDVAAuxSrc1 = *( DWORD* ) ( pack.data + 1 );
+       sample.GetAAUXPack( 0x53, pack );
+       dvinfo.dwDVAAuxCtl1 = *( DWORD* ) ( pack.data + 1 );
+
+       sample.GetVAUXPack( 0x60, pack );
+       dvinfo.dwDVVAuxSrc = *( DWORD* ) ( pack.data + 1 );
+       sample.GetVAUXPack( 0x61, pack );
+       dvinfo.dwDVVAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+#ifdef WITH_LIBDV
+
+       if ( sample.decoder->std == e_dv_std_smpte_314m )
+               fccHandler = make_fourcc( "dv25" );
+#endif
+}
+#endif
+
+bool AVIHandler::FileIsOpen()
+{
+       return avi != NULL;
+}
+
+
+bool AVIHandler::Create( const string& filename )
+{
+       assert( avi == NULL );
+
+       switch ( aviFormat )
+       {
+
+       case AVI_DV1_FORMAT:
+               fail_null( avi = new AVI1File );
+               if ( avi->Create( filename.c_str() ) == false )
+                       return false;
+               //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency, AVI_LARGE_INDEX );
+               break;
+
+       case AVI_DV2_FORMAT:
+               fail_null( avi = new AVI2File );
+               if ( avi->Create( filename.c_str() ) == false )
+                       return false;
+               //if ( GetOpenDML() )
+                       //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+                                  //( AVI_SMALL_INDEX | AVI_LARGE_INDEX ) );
+               //else
+                       //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+                                  //( AVI_SMALL_INDEX ) );
+               break;
+
+       default:
+               assert( aviFormat == AVI_DV1_FORMAT || aviFormat == AVI_DV2_FORMAT );
+       }
+
+       avi->setDVINFO( dvinfo );
+       avi->setFccHandler( make_fourcc( "iavs" ), fccHandler );
+       avi->setFccHandler( make_fourcc( "vids" ), fccHandler );
+       this->filename = filename;
+       FileTracker::GetInstance().Add( filename.c_str() );
+       return ( avi != NULL );
+}
+
+#if 0
+int AVIHandler::Write( const Frame& frame )
+{
+       assert( avi != NULL );
+       try
+       {
+               return avi->WriteFrame( frame ) ? 0 : -1;
+       }
+       catch (...)
+       {
+               return -1;
+       }
+}
+#endif
+
+int AVIHandler::Close()
+{
+       if ( avi != NULL )
+       {
+               avi->WriteRIFF();
+               delete avi;
+               avi = NULL;
+       }
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       for ( int c = 0; c < 4; c++ )
+       {
+               if ( audioChannels[ c ] != NULL )
+               {
+                       delete audioChannels[ c ];
+                       audioChannels[ c ] = NULL;
+               }
+       }
+       isFullyInitialized = false;
+       return 0;
+}
+
+off_t AVIHandler::GetFileSize()
+{
+       return avi->GetFileSize();
+}
+
+int AVIHandler::GetTotalFrames()
+{
+       return avi->GetTotalFrames();
+}
+
+
+bool AVIHandler::Open( const char *s )
+{
+       assert( avi == NULL );
+       fail_null( avi = new AVI1File );
+       if ( avi->Open( s ) )
+       {
+               avi->ParseRIFF();
+               if ( ! (
+                           avi->verifyStreamFormat( make_fourcc( "dvsd" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVSD" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dvcs" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVCS" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dvcp" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVCP" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "CDVC" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "cdvc" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DV25" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dv25" ) ) ) )
+                       return false;
+               avi->ReadIndex();
+               if ( avi->verifyStream( make_fourcc( "auds" ) ) )
+                       aviFormat = AVI_DV2_FORMAT;
+               else
+                       aviFormat = AVI_DV1_FORMAT;
+               isOpenDML = avi->isOpenDML();
+               filename = s;
+               return true;
+       }
+       else
+               return false;
+
+}
+
+int AVIHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       int result = avi->GetDVFrame( data, frameNum );
+#if 0
+       if ( result == 0 )
+       {
+               /* get the audio from the audio stream, if available */
+               if ( aviFormat == AVI_DV2_FORMAT )
+               {
+                       WAVEFORMATEX wav;
+
+                       if ( ! isFullyInitialized && 
+                                avi->getStreamFormat( ( void* ) &wav, make_fourcc( "auds" ) ) )
+                       {
+                               if ( channels > 0 && channels < 5 )
+                               {
+                                       // Allocate interleaved audio buffer
+                                       audioBuffer = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES * channels ];
+
+                                       // Allocate non-interleaved audio buffers
+                                       for ( int c = 0; c < channels; c++ )
+                                               audioChannels[ c ] = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES ];
+                                       
+                                       // Get the audio parameters from AVI for subsequent calls to method
+                                       audioInfo.channels = wav.nChannels;
+                                       audioInfo.frequency = wav.nSamplesPerSec;
+
+                                       // Skip initialization on subsequent calls to method
+                                       isFullyInitialized = true;
+                                       cerr << ">>> using audio from separate AVI audio stream" << endl;
+                               }
+                       }
+
+                       // Get the frame from AVI
+                       int n = avi->getFrame( audioBuffer, frameNum, make_fourcc( "01wb" ) );
+                       if ( n > 0 )
+                       {
+                               // Temporary pointer to audio scratch buffer
+                               int16_t * s = audioBuffer;
+
+                               // Determine samples in this frame
+                               audioInfo.samples = n / audioInfo.channels / sizeof( int16_t );
+                               
+                               // Convert interleaved audio into non-interleaved
+                               for ( int n = 0; n < audioInfo.samples; ++n )
+                                       for ( int i = 0; i < audioInfo.channels; i++ )
+                                               audioChannels[ i ][ n ] = *s++;
+
+                               // Write interleaved audio into frame
+                               frame.EncodeAudio( audioInfo, audioChannels );
+                       }
+               }
+
+               // Parse important metadata in DV bitstream
+               frame.ExtractHeader();
+       }
+#endif
+       return result;
+}
+
+
+void AVIHandler::SetOpenDML( bool flag )
+{
+       isOpenDML = flag;
+}
+
+
+bool AVIHandler::GetOpenDML() const
+{
+       return isOpenDML;
+}
+
+
+/***************************************************************************/
+
+#ifdef HAVE_LIBQUICKTIME
+
+#ifndef HAVE_LIBDV
+#define DV_AUDIO_MAX_SAMPLES 1944
+#endif
+
+// Missing fourcc's in libquicktime (allows compilation)
+#ifndef QUICKTIME_DV_AVID
+#define QUICKTIME_DV_AVID "AVdv"
+#endif
+
+#ifndef QUICKTIME_DV_AVID_A
+#define QUICKTIME_DV_AVID_A "dvcp"
+#endif
+
+#ifndef QUICKTIME_DVCPRO
+#define QUICKTIME_DVCPRO "dvpp"
+#endif
+
+QtHandler::QtHandler() : fd( NULL )
+{
+       extension = ".mov";
+       Init();
+}
+
+
+QtHandler::~QtHandler()
+{
+       Close();
+}
+
+void QtHandler::Init()
+{
+       if ( fd != NULL )
+               Close();
+
+       fd = NULL;
+       samplingRate = 0;
+       samplesPerBuffer = 0;
+       channels = 2;
+       audioBuffer = NULL;
+       audioChannelBuffer = NULL;
+       isFullyInitialized = false;
+}
+
+
+bool QtHandler::FileIsOpen()
+{
+       return fd != NULL;
+}
+
+
+bool QtHandler::Create( const string& filename )
+{
+       Init();
+
+       if ( open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 ) != -1 )
+       {
+               fd = quicktime_open( const_cast<char*>( filename.c_str() ), 0, 1 );
+               if ( fd != NULL )
+                       FileTracker::GetInstance().Add( filename.c_str() );
+       }
+       else
+               return false;
+       this->filename = filename;
+       return true;
+}
+
+void QtHandler::AllocateAudioBuffers()
+{
+       if ( channels > 0 && channels < 5 )
+       {
+               audioBufferSize = DV_AUDIO_MAX_SAMPLES * 2;
+               audioBuffer = new int16_t[ audioBufferSize * channels ];
+
+               audioChannelBuffer = new short int * [ channels ];
+               for ( int c = 0; c < channels; c++ )
+                       audioChannelBuffer[ c ] = new short int[ audioBufferSize ];
+               isFullyInitialized = true;
+       }
+}
+
+inline void QtHandler::DeinterlaceStereo16( void* pInput, int iBytes,
+        void* pLOutput, void* pROutput )
+{
+       short int * piSampleInput = ( short int* ) pInput;
+       short int* piSampleLOutput = ( short int* ) pLOutput;
+       short int* piSampleROutput = ( short int* ) pROutput;
+
+       while ( ( char* ) piSampleInput < ( ( char* ) pInput + iBytes ) )
+       {
+               *piSampleLOutput++ = *piSampleInput++;
+               *piSampleROutput++ = *piSampleInput++;
+       }
+}
+
+#if 0
+int QtHandler::Write( const Frame& frame )
+{
+       if ( ! isFullyInitialized )
+       {
+               AudioInfo audio;
+
+               if ( frame.GetAudioInfo( audio ) )
+               {
+                       channels = 2;
+                       quicktime_set_audio( fd, channels, audio.frequency, 16, QUICKTIME_TWOS );
+               }
+               else
+               {
+                       channels = 0;
+               }
+
+               quicktime_set_video( fd, 1, 720, frame.IsPAL() ? 576 : 480,
+                                    frame.GetFrameRate(), QUICKTIME_DV );
+               AllocateAudioBuffers();
+       }
+
+       int result = quicktime_write_frame( fd, const_cast<unsigned char*>( frame.data ),
+                                           frame.GetFrameSize(), 0 );
+
+       if ( channels > 0 )
+       {
+               AudioInfo audio;
+               if ( frame.GetAudioInfo( audio ) && ( unsigned int ) audio.samples < audioBufferSize )
+               {
+                       long bytesRead = frame.ExtractAudio( audioBuffer );
+
+                       DeinterlaceStereo16( audioBuffer, bytesRead,
+                                            audioChannelBuffer[ 0 ],
+                                            audioChannelBuffer[ 1 ] );
+
+                       quicktime_encode_audio( fd, audioChannelBuffer, NULL, audio.samples );
+               }
+       }
+       return result;
+}
+#endif
+
+int QtHandler::Close()
+{
+       if ( fd != NULL )
+       {
+               quicktime_close( fd );
+               fd = NULL;
+       }
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       if ( audioChannelBuffer != NULL )
+       {
+               for ( int c = 0; c < channels; c++ )
+                       delete audioChannelBuffer[ c ];
+               delete audioChannelBuffer;
+               audioChannelBuffer = NULL;
+       }
+       return 0;
+}
+
+
+off_t QtHandler::GetFileSize()
+{
+       struct stat file_status;
+       stat( filename.c_str(), &file_status );
+       return file_status.st_size;
+}
+
+
+int QtHandler::GetTotalFrames()
+{
+       return ( int ) quicktime_video_length( fd, 0 );
+}
+
+
+bool QtHandler::Open( const char *s )
+{
+       Init();
+
+       fd = quicktime_open( s, 1, 0 );
+       if ( fd == NULL )
+       {
+               fprintf( stderr, "Error opening: %s\n", s );
+               return false;
+       }
+
+       if ( quicktime_has_video( fd ) <= 0 )
+       {
+               fprintf( stderr, "There must be at least one video track in the input file (%s).\n",
+                        s );
+               Close();
+               return false;
+       }
+       char * fcc = quicktime_video_compressor( fd, 0 );
+       if ( strncmp( fcc, QUICKTIME_DV, 4 ) != 0 &&
+            strncmp( fcc, QUICKTIME_DV_AVID, 4 ) != 0 &&
+            strncmp( fcc, QUICKTIME_DV_AVID_A, 4 ) != 0 && 
+            strncmp( fcc, QUICKTIME_DVCPRO, 4 ) != 0 )
+       {
+               Close();
+               return false;
+       }
+       if ( quicktime_has_audio( fd ) )
+               channels = quicktime_track_channels( fd, 0 );
+       filename = s;
+       return true;
+}
+
+int QtHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       assert( fd != NULL );
+
+       quicktime_set_video_position( fd, frameNum, 0 );
+       quicktime_read_frame( fd, data, 0 );
+
+#ifdef HAVE_LIBDV
+       if ( quicktime_has_audio( fd ) )
+       {
+               if ( ! isFullyInitialized )
+                       AllocateAudioBuffers();
+
+               // Fetch the frequency of the audio track and calc number of samples needed
+               int frequency = quicktime_sample_rate( fd, 0 );
+               float fps = ( data[ 3 ] & 0x80 ) ? 25.0f : 29.97f;
+               int samples = mlt_sample_calculator( fps, frequency, frameNum );
+               int64_t seek = mlt_sample_calculator_to_now( fps, frequency, frameNum );
+
+               // Obtain a dv encoder and initialise it with minimal info
+               dv_encoder_t *encoder = dv_encoder_new( 0, 0, 0 );
+               encoder->isPAL = ( data[ 3 ] & 0x80 );
+               encoder->samples_this_frame = samples;
+
+               // Seek to the calculated position and decode
+               quicktime_set_audio_position( fd, seek, 0 );
+               lqt_decode_audio( fd, audioChannelBuffer, NULL, (long) samples );
+
+               // Encode the audio on the frame and done
+               dv_encode_full_audio( encoder, audioChannelBuffer, channels, frequency, data );
+               dv_encoder_free( encoder );
+       }
+#endif
+
+       return 0;
+}
+#endif
diff --git a/src/modules/kino/filehandler.h b/src/modules/kino/filehandler.h
new file mode 100644 (file)
index 0000000..d7292ab
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+* filehandler.h
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _FILEHANDLER_H
+#define _FILEHANDLER_H
+
+// enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED };
+
+#include <vector>
+using std::vector;
+
+#include <string>
+using std::string;
+
+#include "riff.h"
+#include "avi.h"
+#include <sys/types.h>
+#include <stdint.h>
+
+enum { AVI, PLAYLIST, RAW_DV, QT, UNKNOWN_FORMAT };
+enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED };
+enum { DISPLAY_XX, DISPLAY_GDKRGB, DISPLAY_GDKRGB32, DISPLAY_XV, DISPLAY_SDL };
+
+enum { NORM_UNSPECIFIED=0, NORM_PAL=1, NORM_NTSC=2 };
+enum { AUDIO_32KHZ=0, AUDIO_44KHZ=1, AUDIO_48KHZ=2 };
+enum { ASPECT_43=0, ASPECT_169=1 };
+
+enum FileCaptureMode {
+    CAPTURE_IGNORE,
+    CAPTURE_FRAME_APPEND,
+    CAPTURE_FRAME_INSERT,
+    CAPTURE_MOVIE_APPEND
+};
+
+class FileTracker
+{
+protected:
+       FileTracker();
+       ~FileTracker();
+public:
+       static FileTracker &GetInstance( );
+       void SetMode( FileCaptureMode );
+       FileCaptureMode GetMode( );
+       unsigned int Size();
+       char *Get( int );
+       void Add( const char * );
+       void Clear( );
+private:
+       static FileTracker *instance;
+       vector <char *> list;
+       FileCaptureMode mode;
+};
+
+class FileHandler
+{
+public:
+
+       FileHandler();
+       virtual ~FileHandler();
+
+       virtual bool GetAutoSplit() const;
+       virtual bool GetTimeStamp() const;
+       virtual string GetBaseName() const;
+       virtual string GetExtension() const;
+       virtual int GetMaxFrameCount() const;
+       virtual off_t GetMaxFileSize() const;
+       virtual off_t GetFileSize() = 0;
+       virtual int GetTotalFrames() = 0;
+       virtual string GetFilename() const;
+
+       virtual void SetAutoSplit( bool );
+       virtual void SetTimeStamp( bool );
+       virtual void SetBaseName( const string& base );
+       virtual void SetMaxFrameCount( int );
+       virtual void SetEveryNthFrame( int );
+       virtual void SetMaxFileSize( off_t );
+       //virtual void SetSampleFrame( const Frame& sample );
+
+       //virtual bool WriteFrame( const Frame& frame );
+       virtual bool FileIsOpen() = 0;
+       virtual bool Create( const string& filename ) = 0;
+       //virtual int Write( const Frame& frame ) = 0;
+       virtual int Close() = 0;
+       virtual bool Done( void );
+
+       virtual bool Open( const char *s ) = 0;
+       virtual int GetFrame( uint8_t *data, int frameNum ) = 0;
+       int GetFramesWritten() const
+       {
+               return framesWritten;
+       }
+
+protected:
+       bool done;
+       bool autoSplit;
+       bool timeStamp;
+       int maxFrameCount;
+       int framesWritten;
+       int everyNthFrame;
+       int framesToSkip;
+       off_t maxFileSize;
+       string base;
+       string extension;
+       string filename;
+};
+
+
+class RawHandler: public FileHandler
+{
+public:
+       int fd;
+
+       RawHandler();
+       ~RawHandler();
+
+       bool FileIsOpen();
+       bool Create( const string& filename );
+       //int Write( const Frame& frame );
+       int Close();
+       off_t GetFileSize();
+       int GetTotalFrames();
+       bool Open( const char *s );
+       int GetFrame( uint8_t *data, int frameNum );
+private:
+       int numBlocks;
+};
+
+
+class AVIHandler: public FileHandler
+{
+public:
+       AVIHandler( int format = AVI_DV1_FORMAT );
+       ~AVIHandler();
+
+       //void SetSampleFrame( const Frame& sample );
+       bool FileIsOpen();
+       bool Create( const string& filename );
+       //int Write( const Frame& frame );
+       int Close();
+       off_t GetFileSize();
+       int GetTotalFrames();
+       bool Open( const char *s );
+       int GetFrame( uint8_t *data, int frameNum );
+       bool GetOpenDML() const;
+       void SetOpenDML( bool );
+       int GetFormat() const
+       {
+               return aviFormat;
+       }
+
+protected:
+       AVIFile *avi;
+       int aviFormat;
+       //AudioInfo audioInfo;
+       //VideoInfo videoInfo;
+       bool isOpenDML;
+       DVINFO dvinfo;
+       FOURCC  fccHandler;
+       int channels;
+       bool isFullyInitialized;
+       int16_t *audioBuffer;
+       int16_t *audioChannels[ 4 ];
+};
+
+
+#ifdef HAVE_LIBQUICKTIME
+#include <lqt.h>
+
+class QtHandler: public FileHandler
+{
+public:
+       QtHandler();
+       ~QtHandler();
+
+       bool FileIsOpen();
+       bool Create( const string& filename );
+       //int Write( const Frame& frame );
+       int Close();
+       off_t GetFileSize();
+       int GetTotalFrames();
+       bool Open( const char *s );
+       int GetFrame( uint8_t *data, int frameNum );
+       void AllocateAudioBuffers();
+
+private:
+       quicktime_t *fd;
+       long samplingRate;
+       int samplesPerBuffer;
+       int channels;
+       bool isFullyInitialized;
+       unsigned int audioBufferSize;
+       int16_t *audioBuffer;
+       short int** audioChannelBuffer;
+
+       void Init();
+       inline void DeinterlaceStereo16( void* pInput, int iBytes, void* pLOutput, void* pROutput );
+
+};
+#endif
+
+#endif
diff --git a/src/modules/kino/gpl b/src/modules/kino/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/kino/kino_wrapper.cc b/src/modules/kino/kino_wrapper.cc
new file mode 100644 (file)
index 0000000..dd0e5a5
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * kino_wrapper.cc -- c wrapper for kino file handler
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "kino_wrapper.h"
+#include "filehandler.h"
+
+extern "C" 
+{
+
+#include <framework/mlt_pool.h>
+
+struct kino_wrapper_s
+{
+       FileHandler *handler;
+       int is_pal;
+};
+
+kino_wrapper kino_wrapper_init( )
+{
+       kino_wrapper self = ( kino_wrapper )malloc( sizeof( kino_wrapper_s ) );
+       if ( self != NULL )
+               self->handler = NULL;
+       return self;
+}
+
+int kino_wrapper_open( kino_wrapper self, char *src )
+{
+       if ( self != NULL )
+       {
+               // Rough file determination based on file type
+               if ( strncasecmp( strrchr( src, '.' ), ".avi", 4 ) == 0 )
+                       self->handler = new AVIHandler( );
+               else if ( strncasecmp( strrchr( src, '.' ), ".dv", 3 ) == 0 || strncasecmp( strrchr( src, '.' ), ".dif", 4 ) == 0 )
+                       self->handler = new RawHandler( );
+               #ifdef HAVE_LIBQUICKTIME
+               else if ( strncasecmp( strrchr( src, '.' ), ".mov", 4 ) == 0 )
+                       self->handler = new QtHandler( );
+               #endif
+
+               // Open the file if we have a handler
+               if ( self->handler != NULL )
+                       if ( !self->handler->Open( src ) )
+                               self = NULL;
+
+               // Check the first frame to see if it's PAL or NTSC
+               if ( self != NULL && self->handler != NULL )
+               {
+                       uint8_t *data = ( uint8_t * )mlt_pool_alloc( 144000 );
+                       if ( self->handler->GetFrame( data, 0 ) == 0 )
+                               self->is_pal = data[3] & 0x80;
+                       else
+                               self = NULL;
+                       mlt_pool_release( data );
+               }
+       }
+
+       return kino_wrapper_is_open( self );
+}
+
+int kino_wrapper_get_frame_count( kino_wrapper self )
+{
+       return self != NULL && self->handler != NULL ? self->handler->GetTotalFrames( ) : 0;
+}
+
+int kino_wrapper_is_open( kino_wrapper self )
+{
+       return self != NULL && self->handler != NULL ? self->handler->FileIsOpen( ) : 0;
+}
+
+int kino_wrapper_is_pal( kino_wrapper self )
+{
+       return self != NULL ? self->is_pal : 0;
+}
+
+int kino_wrapper_get_frame( kino_wrapper self, uint8_t *data, int index )
+{
+       return self != NULL && self->handler != NULL ? !self->handler->GetFrame( data, index ) : 0;
+}
+
+void kino_wrapper_close( kino_wrapper self )
+{
+       if ( self )
+               delete self->handler;
+       free( self );
+}
+
+}
+
+
diff --git a/src/modules/kino/kino_wrapper.h b/src/modules/kino/kino_wrapper.h
new file mode 100644 (file)
index 0000000..0e73b21
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * kino_wrapper.h -- c wrapper for kino file handler
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MLT_PRODUCER_KINO_WRAPPER_H_
+#define MLT_PRODUCER_KINO_WRAPPER_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+typedef struct kino_wrapper_s *kino_wrapper;
+
+extern kino_wrapper kino_wrapper_init( );
+extern int kino_wrapper_open( kino_wrapper, char * );
+extern int kino_wrapper_is_open( kino_wrapper );
+extern int kino_wrapper_is_pal( kino_wrapper );
+extern int kino_wrapper_get_frame_count( kino_wrapper );
+extern int kino_wrapper_get_frame( kino_wrapper, uint8_t *, int );
+extern void kino_wrapper_close( kino_wrapper );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/modules/kino/producer_kino.c b/src/modules/kino/producer_kino.c
new file mode 100644 (file)
index 0000000..7f94a9e
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * producer_kino.c -- a DV file format parser
+ * Copyright (C) 2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include "kino_wrapper.h"
+
+/* NB: This is an abstract producer - it provides no codec support whatsoever. */
+
+#define FRAME_SIZE_525_60      10 * 150 * 80
+#define FRAME_SIZE_625_50      12 * 150 * 80
+
+typedef struct producer_kino_s *producer_kino;
+
+struct producer_kino_s
+{
+       struct mlt_producer_s parent;
+       kino_wrapper wrapper;
+};
+
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+       kino_wrapper wrapper = kino_wrapper_init( );
+
+       if ( kino_wrapper_open( wrapper, filename ) )
+       {
+               producer_kino this = calloc( sizeof( struct producer_kino_s ), 1 );
+
+               if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+               {
+                       mlt_producer producer = &this->parent;
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+                       double fps = kino_wrapper_is_pal( wrapper ) ? 25 : 30000.0 / 1001.0;
+       
+                       // Assign the wrapper
+                       this->wrapper = wrapper;
+
+                       // Pass wrapper properties (frame rate, count etc)
+                       mlt_properties_set_position( properties, "length", kino_wrapper_get_frame_count( wrapper ) );
+                       mlt_properties_set_position( properties, "in", 0 );
+                       mlt_properties_set_position( properties, "out", kino_wrapper_get_frame_count( wrapper ) - 1 );
+                       mlt_properties_set_double( properties, "real_fps", fps );
+                       mlt_properties_set( properties, "resource", filename );
+
+                       // Register transport implementation with the producer
+                       producer->close = ( mlt_destructor )producer_close;
+       
+                       // Register our get_frame implementation with the producer
+                       producer->get_frame = producer_get_frame;
+       
+                       // Return the producer
+                       return producer;
+               }
+               free( this );
+       }
+
+       kino_wrapper_close( wrapper );
+
+       return NULL;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       producer_kino this = producer->child;
+       uint8_t *data = mlt_pool_alloc( FRAME_SIZE_625_50 );
+       
+       // Obtain the current frame number
+       uint64_t position = mlt_producer_frame( producer );
+       
+       // Create an empty frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       // Seek and fetch
+       if ( kino_wrapper_get_frame( this->wrapper, data, position ) )
+       {
+               // Get the frames properties
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Determine if we're PAL or NTSC
+               int is_pal = kino_wrapper_is_pal( this->wrapper );
+
+               // Pass the dv data
+               mlt_properties_set_data( properties, "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL );
+
+               // Update other info on the frame
+               mlt_properties_set_int( properties, "width", 720 );
+               mlt_properties_set_int( properties, "height", is_pal ? 576 : 480 );
+               mlt_properties_set_int( properties, "top_field_first", is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
+       }
+       else
+       {
+               mlt_pool_release( data );
+       }
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       if ( parent != NULL )
+       {
+               // Obtain this
+               producer_kino this = parent->child;
+
+               // Close the file
+               if ( this != NULL )
+                       kino_wrapper_close( this->wrapper );
+
+               // Close the parent
+               parent->close = NULL;
+               mlt_producer_close( parent );
+
+               // Free the memory
+               free( this );
+       }
+}
diff --git a/src/modules/kino/riff.cc b/src/modules/kino/riff.cc
new file mode 100644 (file)
index 0000000..44a082c
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+* riff.cc library for RIFF file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Tag: $Name$
+*
+* Change log:
+* 
+* $Log$
+* Revision 1.3  2005/07/25 14:41:29  lilo_booter
+* + Minor correction for entry length being less than the data length
+*
+* Revision 1.2  2005/07/25 07:21:39  lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.1  2005/04/15 14:28:26  lilo_booter
+* Initial version
+*
+* Revision 1.18  2005/04/01 23:43:10  ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.17  2004/10/11 01:37:11  ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.16  2003/11/25 23:01:24  ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.15  2003/10/21 16:34:34  ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.13.2.3  2003/08/26 20:39:00  ddennedy
+* relocate mutex unlock and add assert includes
+*
+* Revision 1.13.2.2  2003/01/28 12:54:13  lilo_booter
+* New 'no change' image transition
+*
+* Revision 1.13.2.1  2002/11/25 04:48:31  ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.13  2002/09/13 06:49:49  ddennedy
+* build update, cleanup, bugfixes
+*
+* Revision 1.12  2002/04/21 06:36:40  ddennedy
+* kindler avc and 1394 bus reset support in catpure page, honor max file size
+*
+* Revision 1.11  2002/04/09 06:53:42  ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.4  2002/03/25 21:34:25  arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.3  2002/03/10 21:28:29  arne
+* release 1.1b1, 64 bit support for type 1 avis
+*
+* Revision 1.2  2002/03/04 19:22:43  arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1  2002/03/03 19:08:08  arne
+* import of version 1.01
+*
+*/
+
+#include "config.h"
+
+// C++ includes
+
+#include <string> 
+//#include <stdio.h>
+#include <iostream>
+#include <iomanip>
+#ifndef __FreeBSD__
+#include <byteswap.h>
+#endif /* __FreeBSD__ */
+
+using std::cout;
+using std::hex;
+using std::dec;
+using std::setw;
+using std::setfill;
+using std::endl;
+
+// C includes
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+// local includes
+
+#include "error.h"
+#include "riff.h"
+
+
+/** make a 32 bit "string-id"
+    \param s a pointer to 4 chars
+    \return the 32 bit "string id"
+    \bugs It is not checked whether we really have 4 characters
+    Some compilers understand constants like int id = 'ABCD'; but I
+    could not get it working on the gcc compiler so I had to use this
+    workaround. We can now use id = make_fourcc("ABCD") instead. */
+
+FOURCC make_fourcc( const char *s )
+{
+       if ( s[ 0 ] == 0 )
+               return 0;
+       else
+               return *( ( FOURCC* ) s );
+}
+
+
+RIFFDirEntry::RIFFDirEntry()
+{}
+
+
+RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 )
+{}
+
+
+/** Creates the object without an output file.
+*/
+
+RIFFFile::RIFFFile() : fd( -1 )
+{
+       pthread_mutex_init( &file_mutex, NULL );
+}
+
+
+/* Copy constructor
+   Duplicate the file descriptor
+*/
+
+RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 )
+{
+       if ( riff.fd != -1 )
+       {
+               fd = dup( riff.fd );
+       }
+       directory = riff.directory;
+}
+
+
+/** Destroys the object.
+    If it has an associated opened file, close it. */
+
+RIFFFile::~RIFFFile()
+{
+       Close();
+       pthread_mutex_destroy( &file_mutex );
+}
+
+
+RIFFFile& RIFFFile::operator=( const RIFFFile& riff )
+{
+       if ( fd != riff.fd )
+       {
+               Close();
+               if ( riff.fd != -1 )
+               {
+                       fd = dup( riff.fd );
+               }
+               directory = riff.directory;
+       }
+       return *this;
+}
+
+
+/** Creates or truncates the file.
+    \param s the filename
+*/
+
+bool RIFFFile::Create( const char *s )
+{
+       fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 );
+
+       if ( fd == -1 )
+               return false;
+       else
+               return true;
+}
+
+
+/** Opens the file read only.
+    \param s the filename
+*/
+
+bool RIFFFile::Open( const char *s )
+{
+       fd = open( s, O_RDONLY | O_NONBLOCK );
+
+       if ( fd == -1 )
+               return false;
+       else
+               return true;
+}
+
+
+/** Destroys the object.
+    If it has an associated opened file, close it. */
+
+void RIFFFile::Close()
+{
+       if ( fd != -1 )
+       {
+               close( fd );
+               fd = -1;
+       }
+}
+
+
+/** Adds an entry to the list of containers.
+    \param type the type of this entry
+    \param name the name
+    \param length the length of the data in the container
+    \param list the container in which this object is contained. 
+    \return the ID of the newly created entry
+    The topmost object is not contained in any other container. Use
+    the special ID RIFF_NO_PARENT to create the topmost object. */
+
+int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list )
+{
+       /* Put all parameters in an RIFFDirEntry object. The offset is
+          currently unknown. */
+
+       RIFFDirEntry entry( type, name, length, 0 /* offset */, list );
+
+       /* If the new chunk is in a list, then get the offset and size
+          of that list. The offset of this chunk is the end of the list
+          (parent_offset + parent_length) plus the size of the chunk
+          header. */
+
+       if ( list != RIFF_NO_PARENT )
+       {
+               RIFFDirEntry parent = GetDirectoryEntry( list );
+               entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE;
+       }
+
+       /* The list which this new chunk is a member of has now increased in
+          size. Get that directory entry and bump up its length by the size
+          of the chunk. Since that list may also be contained in another
+          list, walk up to the top of the tree. */
+
+       while ( list != RIFF_NO_PARENT )
+       {
+               RIFFDirEntry parent = GetDirectoryEntry( list );
+               parent.length += RIFF_HEADERSIZE + length;
+               SetDirectoryEntry( list, parent );
+               list = parent.parent;
+       }
+
+       directory.insert( directory.end(), entry );
+
+       return directory.size() - 1;
+}
+
+
+/** Modifies an entry.
+    \param i the ID of the entry which is to modify
+    \param type the type of this entry
+    \param name the name
+    \param length the length of the data in the container
+    \param list the container in which this object is contained.
+    \note Do not change length, offset, or the parent container.
+    \note Do not change an empty name ("") to a name and vice versa */
+
+void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list )
+{
+       RIFFDirEntry entry( type, name, length, offset, list );
+
+       assert( i >= 0 && i < ( int ) directory.size() );
+
+       directory[ i ] = entry;
+}
+
+
+/** Modifies an entry.
+    The entry.written flag is set to false because the contents has been modified
+    \param i the ID of the entry which is to modify
+    \param entry the new entry 
+    \note Do not change length, offset, or the parent container.
+    \note Do not change an empty name ("") to a name and vice versa */
+
+void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry )
+{
+       assert( i >= 0 && i < ( int ) directory.size() );
+
+       entry.written = false;
+       directory[ i ] = entry;
+}
+
+
+/** Retrieves an entry.
+    Gets the most important member variables.
+    \param i the ID of the entry to retrieve
+    \param type
+    \param name
+    \param length
+    \param offset
+    \param list */
+
+void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const
+{
+       RIFFDirEntry entry;
+
+       assert( i >= 0 && i < ( int ) directory.size() );
+
+       entry = directory[ i ];
+       type = entry.type;
+       name = entry.name;
+       length = entry.length;
+       offset = entry.offset;
+       list = entry.parent;
+}
+
+
+/** Retrieves an entry.
+    Gets the whole RIFFDirEntry object.
+    \param i the ID of the entry to retrieve
+    \return the entry */
+
+RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const
+{
+       assert( i >= 0 && i < ( int ) directory.size() );
+
+       return directory[ i ];
+}
+
+
+/** Calculates the total size of the file
+    \return the size the file in bytes
+*/
+
+off_t RIFFFile::GetFileSize( void ) const
+{
+
+       /* If we have at least one entry, return the length field
+          of the FILE entry, which is the length of its contents,
+          which is the actual size of whatever is currently in the
+          AVI directory structure. 
+
+          Note that the first entry does not belong to the AVI
+          file.
+
+          If we don't have any entry, the file size is zero. */
+
+       if ( directory.size() > 0 )
+               return directory[ 0 ].length;
+       else
+               return 0;
+}
+
+
+/** prints the attributes of the entry
+    \param i the ID of the entry to print
+*/
+
+void RIFFFile::PrintDirectoryEntry ( int i ) const
+{
+       RIFFDirEntry entry;
+       RIFFDirEntry parent;
+       FOURCC entry_name;
+       FOURCC list_name;
+
+       /* Get all attributes of the chunk object. If it is contained
+          in a list, get the name of the list too (otherwise the name of
+          the list is blank). If the chunk object doesn´t have a name (only
+          LISTs and RIFFs have a name), the name is blank. */
+
+       entry = GetDirectoryEntry( i );
+       if ( entry.parent != RIFF_NO_PARENT )
+       {
+               parent = GetDirectoryEntry( entry.parent );
+               list_name = parent.name;
+       }
+       else
+       {
+               list_name = make_fourcc( "    " );
+       }
+       if ( entry.name != 0 )
+       {
+               entry_name = entry.name;
+       }
+       else
+       {
+               entry_name = make_fourcc( "    " );
+       }
+
+       /* Print out the ascii representation of type and name, as well as
+          length and file offset. */
+
+       cout << hex << setfill( '0' ) << "type: "
+       << ((char *)&entry.type)[0]
+       << ((char *)&entry.type)[1]
+       << ((char *)&entry.type)[2]
+       << ((char *)&entry.type)[3]
+       << " name: "
+       << ((char *)&entry_name)[0]
+       << ((char *)&entry_name)[1]
+       << ((char *)&entry_name)[2]
+       << ((char *)&entry_name)[3]
+       << " length: 0x" << setw( 12 ) << entry.length
+       << " offset: 0x" << setw( 12 ) << entry.offset
+       << " list: "
+       << ((char *)&list_name)[0]
+       << ((char *)&list_name)[1]
+       << ((char *)&list_name)[2]
+       << ((char *)&list_name)[3] << dec << endl;
+
+       /* print the content itself */
+
+       PrintDirectoryEntryData( entry );
+}
+
+
+/** prints the contents of the entry
+    Prints a readable representation of the contents of an index.
+    Override this to print out any objects you store in the RIFF file.
+    \param entry the entry to print */
+
+void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
+       {}
+
+
+/** prints the contents of the whole directory
+    Prints a readable representation of the contents of an index.
+    Override this to print out any objects you store in the RIFF file.
+    \param entry the entry to print */
+
+void RIFFFile::PrintDirectory() const
+{
+       int i;
+       int count = directory.size();
+
+       for ( i = 0; i < count; ++i )
+               PrintDirectoryEntry( i );
+}
+
+
+/** finds the index
+    finds the index of a given directory entry type 
+    \todo inefficient if the directory has lots of items
+    \param type the type of the entry to find
+    \param n    the zero-based instance of type to locate
+    \return the index of the found object in the directory, or -1 if not found */
+
+int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const
+{
+       int i, j = 0;
+       int count = directory.size();
+
+       for ( i = 0; i < count; ++i )
+               if ( directory[ i ].type == type )
+               {
+                       if ( j == n )
+                               return i;
+                       j++;
+               }
+
+       return -1;
+}
+
+
+/** Reads all items that are contained in one list
+    Read in one chunk and add it to the directory. If the chunk
+    happens to be of type LIST, then call ParseList recursively for
+    it.
+    \param parent The id of the item to process
+*/
+
+void RIFFFile::ParseChunk( int parent )
+{
+       FOURCC type;
+       DWORD length;
+       int typesize;
+
+       /* Check whether it is a LIST. If so, let ParseList deal with it */
+
+       fail_if( read( fd, &type, sizeof( type ) ) != sizeof( type ));
+       if ( type == make_fourcc( "LIST" ) )
+       {
+               typesize = (int) -sizeof( type );
+               fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 );
+               ParseList( parent );
+       }
+
+       /* it is a normal chunk, create a new directory entry for it */
+
+       else
+       {
+               fail_neg( read( fd, &length, sizeof( length ) ) );
+               if ( length & 1 )
+                       length++;
+               AddDirectoryEntry( type, 0, length, parent );
+               fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 );
+       }
+}
+
+
+/** Reads all items that are contained in one list
+    \param parent The id of the list to process
+*/
+
+void RIFFFile::ParseList( int parent )
+{
+       FOURCC type;
+       FOURCC name;
+       int list;
+       DWORD length;
+       off_t pos;
+       off_t   listEnd;
+
+       /* Read in the chunk header (type and length). */
+       fail_neg( read( fd, &type, sizeof( type ) ) );
+       fail_neg( read( fd, &length, sizeof( length ) ) );
+
+       if ( length & 1 )
+               length++;
+
+       /* The contents of the list starts here. Obtain its offset. The list
+          name (4 bytes) is already part of the contents). */
+
+       pos = lseek( fd, 0, SEEK_CUR );
+       fail_if( pos == ( off_t ) - 1 );
+       fail_neg( read( fd, &name, sizeof( name ) ) );
+
+       /* Add an entry for this list. */
+
+       list = AddDirectoryEntry( type, name, sizeof( name ), parent );
+
+       /* Read in any chunks contained in this list. This list is the
+          parent for all chunks it contains. */
+
+       listEnd = pos + length;
+       while ( pos < listEnd )
+       {
+               ParseChunk( list );
+               pos = lseek( fd, 0, SEEK_CUR );
+               fail_if( pos == ( off_t ) - 1 );
+       }
+}
+
+
+/** Reads the directory structure of the whole RIFF file
+*/
+
+void RIFFFile::ParseRIFF( void )
+{
+       FOURCC type;
+       DWORD length;
+       off_t filesize;
+       off_t pos;
+       int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
+
+       pos = lseek( fd, 0, SEEK_SET );
+
+       /* calculate file size from RIFF header instead from physical file. */
+
+       while ( ( read( fd, &type, sizeof( type ) ) > 0 ) &&
+               ( read( fd, &length, sizeof( length ) ) > 0 ) &&
+               ( type == make_fourcc( "RIFF" ) ) )
+       {
+
+               filesize += length + RIFF_HEADERSIZE;
+
+               fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 );
+               ParseList( container );
+               pos = lseek( fd, 0, SEEK_CUR );
+               fail_if( pos == ( off_t ) - 1 );
+       }
+}
+
+
+/** Reads one item including its contents from the RIFF file
+    \param chunk_index The index of the item to write
+    \param data A pointer to the data
+*/
+
+void RIFFFile::ReadChunk( int chunk_index, void *data, off_t data_len )
+{
+       RIFFDirEntry entry;
+
+       entry = GetDirectoryEntry( chunk_index );
+       pthread_mutex_lock( &file_mutex );
+       fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
+       fail_neg( read( fd, data, entry.length > data_len ? data_len : entry.length ) );
+       pthread_mutex_unlock( &file_mutex );
+}
+
+
+/** Writes one item including its contents to the RIFF file
+    \param chunk_index The index of the item to write
+    \param data A pointer to the data
+*/
+
+void RIFFFile::WriteChunk( int chunk_index, const void *data )
+{
+       RIFFDirEntry entry;
+
+       entry = GetDirectoryEntry( chunk_index );
+       pthread_mutex_lock( &file_mutex );
+       fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
+       fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
+       DWORD length = entry.length;
+       fail_neg( write( fd, &length, sizeof( length ) ) );
+       fail_neg( write( fd, data, entry.length ) );
+       pthread_mutex_unlock( &file_mutex );
+
+       /* Remember that this entry already has been written. */
+
+       directory[ chunk_index ].written = true;
+}
+
+
+/** Writes out the directory structure
+    For all items in the directory list that have not been written
+    yet, it seeks to the file position where that item should be
+    stored and writes the type and length field. If the item has a
+    name, it will also write the name field.
+    \note It does not write the contents of any item. Use WriteChunk to do that. */
+
+void RIFFFile::WriteRIFF( void )
+{
+       int i;
+       RIFFDirEntry entry;
+       int count = directory.size();
+
+       /* Start at the second entry (RIFF), since the first entry (FILE)
+          is needed only for internal purposes and is not written to the
+          file. */
+
+       for ( i = 1; i < count; ++i )
+       {
+
+               /* Only deal with entries that haven´t been written */
+
+               entry = GetDirectoryEntry( i );
+               if ( entry.written == false )
+               {
+
+                       /* A chunk entry consist of its type and length, a list
+                          entry has an additional name. Look up the entry, seek
+                          to the start of the header, which is at the offset of
+                          the data start minus the header size and write out the
+                          items. */
+
+                       fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ;
+                       fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
+                       DWORD length = entry.length;
+                       fail_neg( write( fd, &length, sizeof( length ) ) );
+
+                       /* If it has a name, it is a list. Write out the extra name
+                          field. */
+
+                       if ( entry.name != 0 )
+                       {
+                               fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) );
+                       }
+
+                       /* Remember that this entry already has been written. */
+
+                       directory[ i ].written = true;
+               }
+       }
+}
diff --git a/src/modules/kino/riff.h b/src/modules/kino/riff.h
new file mode 100644 (file)
index 0000000..72021fb
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+* riff.h library for RIFF file format i/o
+* Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Tag: $Name$
+*
+* Change log:
+* 
+* $Log$
+* Revision 1.2  2005/07/25 07:21:39  lilo_booter
+* + fixes for opendml dv avi
+*
+* Revision 1.1  2005/04/15 14:28:26  lilo_booter
+* Initial version
+*
+* Revision 1.14  2005/04/01 23:43:10  ddennedy
+* apply endian fixes from Daniel Kobras
+*
+* Revision 1.13  2004/10/11 01:37:11  ddennedy
+* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
+*
+* Revision 1.12  2004/01/06 22:53:42  ddennedy
+* metadata editing tweaks and bugfixes, new ui elements in preparation for publish functions
+*
+* Revision 1.11  2003/11/25 23:01:25  ddennedy
+* cleanup and a few bugfixes
+*
+* Revision 1.10  2003/10/21 16:34:34  ddennedy
+* GNOME2 port phase 1: initial checkin
+*
+* Revision 1.8.4.1  2002/11/25 04:48:31  ddennedy
+* bugfix to report errors when loading files
+*
+* Revision 1.8  2002/04/21 06:36:40  ddennedy
+* kindler avc and 1394 bus reset support in catpure page, honor max file size
+*
+* Revision 1.7  2002/04/09 06:53:42  ddennedy
+* cleanup, new libdv 0.9.5, large AVI, dnd storyboard
+*
+* Revision 1.3  2002/03/25 21:34:25  arne
+* Support for large (64 bit) files mostly completed
+*
+* Revision 1.2  2002/03/04 19:22:43  arne
+* updated to latest Kino avi code
+*
+* Revision 1.1.1.1  2002/03/03 19:08:08  arne
+* import of version 1.01
+*
+*/
+
+#ifndef _RIFF_H
+#define _RIFF_H 1
+
+#include <vector>
+using std::vector;
+
+#include <pthread.h>
+
+#include "endian_types.h"
+
+#define QUADWORD int64_le_t
+#define DWORD int32_le_t
+#define LONG u_int32_le_t
+#define WORD int16_le_t
+#define BYTE u_int8_le_t
+#define FOURCC u_int32_t      // No endian conversion needed.
+
+#define RIFF_NO_PARENT (-1)
+#define RIFF_LISTSIZE (4)
+#define RIFF_HEADERSIZE (8)
+
+#ifdef __cplusplus
+extern "C"
+{
+       FOURCC make_fourcc( const char * s );
+}
+#endif
+
+class RIFFDirEntry
+{
+public:
+       FOURCC type;
+       FOURCC name;
+       off_t length;
+       off_t offset;
+       int parent;
+       int written;
+
+       RIFFDirEntry();
+       RIFFDirEntry( FOURCC t, FOURCC n, int l, int o, int p );
+};
+
+
+class RIFFFile
+{
+public:
+       RIFFFile();
+       RIFFFile( const RIFFFile& );
+       virtual ~RIFFFile();
+       RIFFFile& operator=( const RIFFFile& );
+
+       virtual bool Open( const char *s );
+       virtual bool Create( const char *s );
+       virtual void Close();
+       virtual int AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list );
+       virtual void SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list );
+       virtual void SetDirectoryEntry( int i, RIFFDirEntry &entry );
+       virtual void GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const;
+       virtual RIFFDirEntry GetDirectoryEntry( int i ) const;
+       virtual off_t GetFileSize( void ) const;
+       virtual void PrintDirectoryEntry( int i ) const;
+       virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const;
+       virtual void PrintDirectory( void ) const;
+       virtual int FindDirectoryEntry( FOURCC type, int n = 0 ) const;
+       virtual void ParseChunk( int parent );
+       virtual void ParseList( int parent );
+       virtual void ParseRIFF( void );
+       virtual void ReadChunk( int chunk_index, void *data, off_t data_len );
+       virtual void WriteChunk( int chunk_index, const void *data );
+       virtual void WriteRIFF( void );
+
+protected:
+       int fd;
+       pthread_mutex_t file_mutex;
+
+private:
+       vector<RIFFDirEntry> directory;
+};
+#endif
diff --git a/src/modules/lumas/Makefile b/src/modules/lumas/Makefile
new file mode 100644 (file)
index 0000000..14079e0
--- /dev/null
@@ -0,0 +1,23 @@
+include ../../../config.mak
+LDFLAGS=
+
+all:   luma create_lumas
+       @./create_lumas 
+
+luma:  luma.c
+
+create_lumas:
+
+depend:
+
+distclean:
+       rm -rf PAL NTSC luma 
+
+clean:
+       rm -f luma 
+
+install:       all
+       install -d $(DESTDIR)$(prefix)/share/mlt/lumas/PAL
+       install -d $(DESTDIR)$(prefix)/share/mlt/lumas/NTSC
+       install -m 644 PAL/* $(DESTDIR)$(prefix)/share/mlt/lumas/PAL
+       install -m 644 NTSC/* $(DESTDIR)$(prefix)/share/mlt/lumas/NTSC
diff --git a/src/modules/lumas/configure b/src/modules/lumas/configure
new file mode 100755 (executable)
index 0000000..e9ce870
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+if [ "$help" = "1" ]
+then
+       cat << EOF
+Luma options:
+
+  --luma-compress         - Produce compressed (png) lumas
+  --luma-8bpp             - Produce 8 bit pgm lumas (defaut is 16 bit)
+
+EOF
+
+else
+
+       rm -f .8bit .compress .executed
+
+       for i in "$@"
+       do
+               case $i in
+                       --luma-compress )       touch .compress ;;
+                       --luma-8bit )           touch .8bit ;;
+               esac
+       done
+
+fi
+
diff --git a/src/modules/lumas/create_lumas b/src/modules/lumas/create_lumas
new file mode 100755 (executable)
index 0000000..05a9c06
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+[ \( -d PAL \) -a \( ! $0 -nt .executed \) ] && exit 0
+
+bpp=16
+[ -f .8bit ] && bpp=8
+
+for i in PAL NTSC
+do
+       mkdir -p $i
+       rm -f $i/*.pgm $i/*.png
+
+       [ "$i" = "PAL" ] && h=576 || h=480
+       ./luma -h $h -bpp $bpp > $i/luma01.pgm
+       ./luma -h $h -bpp $bpp -bands $h > $i/luma02.pgm
+       ./luma -h $h -bpp $bpp -hmirror 1 > $i/luma03.pgm
+       ./luma -h $h -bpp $bpp -bands $h -vmirror 1 > $i/luma04.pgm
+       ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 > $i/luma05.pgm
+       ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -flip 1 > $i/luma06.pgm
+       ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 > $i/luma07.pgm
+       ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 -flip 1 > $i/luma08.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 0 > $i/luma09.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 0 -rotate 1 -flop 1 > $i/luma10.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 1 > $i/luma11.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 1 -vmirror 1 > $i/luma12.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -flop 1 > $i/luma13.pgm
+       ./luma -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -vmirror 1 > $i/luma14.pgm
+       ./luma -h $h -bpp $bpp -offset 32768 -dmirror 1 -hmirror 1 > $i/luma15.pgm
+       ./luma -h $h -bpp $bpp -type 1 > $i/luma16.pgm
+       ./luma -h $h -bpp $bpp -type 1 -bands 2 -rband 1 > $i/luma17.pgm
+       ./luma -h $h -bpp $bpp -type 2 > $i/luma18.pgm
+       ./luma -h $h -bpp $bpp -type 2 -quart 1 > $i/luma19.pgm
+       ./luma -h $h -bpp $bpp -type 2 -quart 1 -flip 1 > $i/luma20.pgm
+       ./luma -h $h -bpp $bpp -type 2 -quart 1 -bands 2 > $i/luma21.pgm
+       ./luma -h $h -bpp $bpp -type 3 > $i/luma22.pgm
+
+       if [ -f .compress ]
+       then 
+               for f in $i/*.pgm 
+               do
+                       convert $f $f.png
+                       rm -f $f
+               done
+       fi
+done
+
+touch .executed
+
diff --git a/src/modules/lumas/luma.c b/src/modules/lumas/luma.c
new file mode 100644 (file)
index 0000000..0889612
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * luma.c -- image generator for transition_luma
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef struct 
+{
+       int type;
+       int w;
+       int h;
+       int bands;
+       int rband;
+       int vmirror;
+       int hmirror;
+       int dmirror;
+       int invert;
+       int offset;
+       int flip;
+       int flop;
+       int pflip;
+       int pflop;
+       int quart;
+       int rotate;
+}
+luma;
+
+void luma_init( luma *this )
+{
+       memset( this, 0, sizeof( luma ) );
+       this->type = 0;
+       this->w = 720;
+       this->h = 576;
+       this->bands = 1;
+       this->rband = 0;
+       this->vmirror = 0;
+       this->hmirror = 0;
+       this->dmirror = 0;
+       this->invert = 0;
+       this->offset = 0;
+       this->flip = 0;
+       this->flop = 0;
+       this->quart = 0;
+       this->pflop = 0;
+       this->pflip = 0;
+}
+
+static inline int sqrti( int n )
+{
+    int p = 0;
+       int q = 1;
+       int r = n;
+       int h = 0;
+
+    while( q <= n ) 
+               q = 4 * q;
+
+    while( q != 1 )
+    {
+        q = q / 4;
+        h = p + q;
+        p = p / 2;
+        if ( r >= h )
+        {
+            p = p + q;
+            r = r - h;
+        } 
+    }
+
+    return p;
+}
+
+uint16_t *luma_render( luma *this )
+{
+       int i = 0;
+       int j = 0;
+       int k = 0;
+
+       if ( this->quart )
+       {
+               this->w *= 2;
+               this->h *= 2;
+       }
+
+       if ( this->rotate )
+       {
+               int t = this->w;
+               this->w = this->h;
+               this->h = t;
+       }
+
+       int max = ( 1 << 16 ) - 1;
+       uint16_t *image = malloc( this->w * this->h * sizeof( uint16_t ) );
+       uint16_t *end = image + this->w * this->h;
+       uint16_t *p = image;
+       uint16_t *r = image;
+       int lower = 0;
+       int lpb = this->h / this->bands;
+       int rpb = max / this->bands;
+       int direction = 1;
+
+       int half_w = this->w / 2;
+       int half_h = this->h / 2;
+
+       if ( !this->dmirror && ( this->hmirror || this->vmirror ) )
+               rpb *= 2;
+
+       for ( i = 0; i < this->bands; i ++ )
+       {
+               lower = i * rpb;
+               direction = 1;
+
+               if ( this->rband && i % 2 == 1 )
+               {
+                       direction = -1;
+                       lower += rpb;
+               }
+
+               switch( this->type )
+               {
+                       case 1:
+                               {
+                                       int length = sqrti( half_w * half_w + lpb * lpb / 4 );
+                                       int value;
+                                       int x = 0;
+                                       int y = 0;
+                                       for ( j = 0; j < lpb; j ++ )
+                                       {
+                                               y = j - lpb / 2;
+                                               for ( k = 0; k < this->w; k ++ )
+                                               {
+                                                       x = k - half_w;
+                                                       value = sqrti( x * x + y * y );
+                                                       *p ++ = lower + ( direction * rpb * ( ( max * value ) / length ) / max ) + ( j * this->offset * 2 / lpb ) + ( j * this->offset / lpb );
+                                               }
+                                       }
+                               }
+                               break;
+
+                       case 2:
+                               {
+                                       for ( j = 0; j < lpb; j ++ )
+                                       {
+                                               int value = ( ( j * this->w ) / lpb ) - half_w;
+                                               if ( value > 0 )
+                                                       value = - value;
+                                               for ( k = - half_w; k < value; k ++ )
+                                                       *p ++ = lower + ( direction * rpb * ( ( max * abs( k ) ) / half_w ) / max );
+                                               for ( k = value; k < abs( value ); k ++ )
+                                                       *p ++ = lower + ( direction * rpb * ( ( max * abs( value ) ) / half_w ) / max ) + ( j * this->offset * 2 / lpb ) + ( j * this->offset / lpb );
+                                               for ( k = abs( value ); k < half_w; k ++ )
+                                                       *p ++ = lower + ( direction * rpb * ( ( max * abs( k ) ) / half_w ) / max );
+                                       }
+                               }
+                               break;
+
+                       case 3:
+                               {
+                                       int length;
+                                       for ( j = -half_h; j < half_h; j ++ )
+                                       {
+                                               if ( j < 0 )
+                                               {
+                                                       for ( k = - half_w; k < half_w; k ++ )
+                                                       {
+                                                               length = sqrti( k * k + j * j );
+                                                               *p ++ = ( max / 4 * k ) / ( length + 1 );
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       for ( k = half_w; k > - half_w; k -- )
+                                                       {
+                                                               length = sqrti( k * k + j * j );
+                                                               *p ++ = ( max / 2 ) + ( max / 4 * k ) / ( length + 1 );
+                                                       }
+                                               }
+                                       }
+                               }
+                               break;
+
+                       default:
+                               for ( j = 0; j < lpb; j ++ )
+                                       for ( k = 0; k < this->w; k ++ )
+                                               *p ++ = lower + ( direction * ( rpb * ( ( k * max ) / this->w ) / max ) ) + ( j * this->offset * 2 / lpb );
+                               break;
+               }
+       }
+
+       if ( this->quart )
+       {
+               this->w /= 2;
+               this->h /= 2;
+               for ( i = 1; i < this->h; i ++ )
+               {
+                       p = image + i * this->w;
+                       r = image + i * 2 * this->w;
+                       j = this->w;
+                       while ( j -- > 0 )
+                               *p ++ = *r ++;
+               }
+       }
+
+       if ( this->dmirror )
+       {
+               for ( i = 0; i < this->h; i ++ )
+               {
+                       p = image + i * this->w;
+                       r = end - i * this->w;
+                       j = ( this->w * ( this->h - i ) ) / this->h;
+                       while ( j -- )
+                               *( -- r ) = *p ++;
+               }
+       }
+
+       if ( this->flip )
+       {
+               uint16_t t;
+               for ( i = 0; i < this->h; i ++ )
+               {
+                       p = image + i * this->w;
+                       r = p + this->w;
+                       while( p != r )
+                       {
+                               t = *p;
+                               *p ++ = *( -- r );
+                               *r = t;
+                       }
+               }
+       }
+
+       if ( this->flop )
+       {
+               uint16_t t;
+               r = end;
+               for ( i = 1; i < this->h / 2; i ++ )
+               {
+                       p = image + i * this->w;
+                       j = this->w;
+                       while( j -- )
+                       {
+                               t = *( -- p );
+                               *p = *( -- r );
+                               *r = t;
+                       }
+               }
+       }
+
+       if ( this->hmirror )
+       {
+               p = image;
+               while ( p < end )
+               {
+                       r = p + this->w;
+                       while ( p != r )
+                               *( -- r ) = *p ++;
+                       p += this->w / 2;
+               }
+       }
+
+       if ( this->vmirror )
+       {
+               p = image;
+               r = end;
+               while ( p != r )
+                       *( -- r ) = *p ++;
+       }
+
+       if ( this->invert )
+       {
+               p = image;
+               r = image;
+               while ( p < end )
+                       *p ++ = max - *r ++;
+       }
+
+       if ( this->pflip )
+       {
+               uint16_t t;
+               for ( i = 0; i < this->h; i ++ )
+               {
+                       p = image + i * this->w;
+                       r = p + this->w;
+                       while( p != r )
+                       {
+                               t = *p;
+                               *p ++ = *( -- r );
+                               *r = t;
+                       }
+               }
+       }
+
+       if ( this->pflop )
+       {
+               uint16_t t;
+               end = image + this->w * this->h;
+               r = end;
+               for ( i = 1; i < this->h / 2; i ++ )
+               {
+                       p = image + i * this->w;
+                       j = this->w;
+                       while( j -- )
+                       {
+                               t = *( -- p );
+                               *p = *( -- r );
+                               *r = t;
+                       }
+               }
+       }
+
+       if ( this->rotate )
+       {
+               uint16_t *image2 = malloc( this->w * this->h * sizeof( uint16_t ) );
+               for ( i = 0; i < this->h; i ++ )
+               {
+                       p = image + i * this->w;
+                       r = image2 + this->h - i - 1;
+                       for ( j = 0; j < this->w; j ++ )
+                       {
+                               *r = *( p ++ );
+                               r += this->h;
+                       }
+               }
+               i = this->w;
+               this->w = this->h;
+               this->h = i;
+               free( image );
+               image = image2;
+       }
+
+       return image;
+}
+
+int main( int argc, char **argv )
+{
+       int arg = 1;
+       int bpp = 8;
+
+       luma this;
+       uint16_t *image = NULL;
+
+       luma_init( &this );
+
+       for ( arg = 1; arg < argc - 1; arg ++ )
+       {
+               if ( !strcmp( argv[ arg ], "-bpp" ) )
+                       bpp = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-type" ) )
+                       this.type = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-w" ) )
+                       this.w = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-h" ) )
+                       this.h = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-bands" ) )
+                       this.bands = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-rband" ) )
+                       this.rband = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-hmirror" ) )
+                       this.hmirror = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-vmirror" ) )
+                       this.vmirror = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-dmirror" ) )
+                       this.dmirror = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-offset" ) )
+                       this.offset = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-invert" ) )
+                       this.invert = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-flip" ) )
+                       this.flip = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-flop" ) )
+                       this.flop = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-pflip" ) )
+                       this.pflip = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-pflop" ) )
+                       this.pflop = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-quart" ) )
+                       this.quart = atoi( argv[ ++ arg ] );
+               else if ( !strcmp( argv[ arg ], "-rotate" ) )
+                       this.rotate = atoi( argv[ ++ arg ] );
+               else
+                       fprintf( stderr, "ignoring %s\n", argv[ arg ] );
+       }
+
+       if ( bpp != 8 && bpp != 16 )
+       {
+               fprintf( stderr, "Invalid bpp %d\n", bpp );
+               return 1;
+       }
+
+       image = luma_render( &this );
+
+       if ( bpp == 16 )
+       {
+               uint16_t *end = image + this.w * this.h;
+               uint16_t *p = image;
+               uint8_t *q = ( uint8_t * )image;
+               while ( p < end )
+               {
+                       *p ++ = ( *q << 8 ) + *( q + 1 );
+                       q += 2;
+               }
+               printf( "P5\n" );
+               printf( "%d %d\n", this.w, this.h );
+               printf( "65535\n" );
+               fwrite( image, this.w * this.h * sizeof( uint16_t ), 1, stdout );
+       }
+       else
+       {
+               uint16_t *end = image + this.w * this.h;
+               uint16_t *p = image;
+               uint8_t *q = ( uint8_t * )image;
+               while ( p < end )
+                       *q ++ = ( uint8_t )( *p ++ >> 8 );
+               printf( "P5\n" );
+               printf( "%d %d\n", this.w, this.h );
+               printf( "255\n" );
+               fwrite( image, this.w * this.h, 1, stdout );
+       }
+
+       return 0;
+}
+
diff --git a/src/modules/motion_est/Makefile b/src/modules/motion_est/Makefile
new file mode 100644 (file)
index 0000000..953f898
--- /dev/null
@@ -0,0 +1,55 @@
+include ../../../config.mak
+
+TARGET = ../libmltmotion_est.so
+
+OBJS = factory.o \
+          filter_motion_est.o \
+          filter_crop_detect.o \
+          filter_autotrack_rectangle.o \
+          arrow_code.o \
+          filter_vismv.o \
+          producer_slowmotion.o
+
+CFLAGS += -I../.. 
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+test: $(TARGET)
+       ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter vismv -filter benchmark -consumer sdl rescale=none real_time=0 audio_off=1 silent=1 /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=50000
+
+hist: $(TARGET)
+       ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter histogram -consumer sdl rescale=none real_time=0 audio_off=1 silent=1 /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=40000
+
+
+test2: $(TARGET)
+       inigo colour:black -filter watermark:"+mello.txt" composite.geometry="0,0:10%x10%;99=90%,90%" composite.out=99 -filter crop_detect -filter motion_est -filter vismv
+
+realtime: $(TARGET)
+       ~/mlt-devel/mlt/src/inigo/inigo -filter motion_est -filter vismv -consumer sdl rescale=none /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=30000
+
+testhist: $(TARGET)
+       ~/mlt-devel/mlt/src/inigo/inigo -consumer sdl rescale=none silent=1 -filter motion_est -filter histogram  -filter vismv /media/cdrecorder/BBC.The.Private.Life.Of.Plants.Pt5.Living.Together.DivX505.AC3.www.MVGroup.org.uk.avi in=10000
+
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/motion_est/README b/src/modules/motion_est/README
new file mode 100644 (file)
index 0000000..5542d3f
--- /dev/null
@@ -0,0 +1,97 @@
+INTRO:
+
+This module is designed to provide application agnostic motion estimation.
+I wrote it from scratch because I found the other Open Source code to be
+limited by their difficulty of reuse.
+
+
+COMPILE:
+
+To compile this module, you must supply these options to the root configure script:
+
+--enable-gpl --enable-motion-est
+
+
+EXAMPLES:
+
+Estimate the motion:
+
+       > inigo -filter motion_est <movie_file>
+
+To display the motion vectors as pretty arrows:
+
+       > inigo -filter motion_est -filter vismv <movie_file>
+
+If your using a movie file that contains a crop, you will get better results with this:
+
+       > inigo -filter crop_detect -filter motion_est -filter vismv <movie_file>
+
+If your computer is unable to do the above examples in real time, try this:
+
+       > inigo -filter motion_est -filter vismv -consumer inigo real_time=0 <movie_file>
+
+If you'd like to see the motion vectors without the median denoising function, do this:
+
+       > inigo -filter motion_est denoise=0 -filter vismv <movie_file>
+
+To reconstruct each frame by applying the motion to the previous frame:
+
+       > inigo -filter motion_est show_reconstruction=1 <movie_file>
+
+To compare the reconstructed frame and the real frame (while paused):
+
+       > inigo -filter motion_est show_reconstruction=1 toggle_when_paused=1 <movie_file>
+
+To show the difference (residual) between the reconstructed frame the real frame:
+
+       > inigo -filter motion_est show_residual=1 <movie_file>
+
+To automatically track an object in the frame, try this:
+
+       > inigo -filter autotrack_rectangle:X,Y:WxH debug=1 <movie_file>
+
+(Where X,Y is the origin of the rectangle indexed from upper left and WxH is the dimensions of the rectangle.)
+
+To obscure that same object in the frame, try this:
+
+       > inigo -filter autotrack_rectangle:X,Y:WxH obscure=1 <movie_file>
+
+There is now a slow motion producer that does interpolation based on the motion vectors:
+
+       > inigo slowmotion:<movie_file> _speed=0.1 method=1 debug=1
+
+NOTES (and deficiencies):
+
+1.  Ignore shot change detection when your using the autotrack_rectangle filter.
+
+2.  Don't assume motion vectors displayed while stepping backwards and forward are that same vectors
+    that would be calculated while playing the footage from start to finish, nonstop. Stepping forward
+    should be fine after a few frames, however.
+
+3.  SSE instructions are lazily assumed. MMX, Altivec, and SIMD-less would be good too.
+
+4.  Motion estimation is only performed in the luma color space.
+
+5.  Motion vectors should have sub-pixel accuracy.
+
+6.  Motion vectors are not serializable yet.
+
+7.  A diligent test suite is needed. (show_reconstruction & show_residual are a start)
+
+8.  Multithreaded code will see HUGE benefits on multi-CPU systems. Donations of a multi-core cpu or a
+    multi-cpu system to the author will encourage development.
+
+9.  Macroblock sizes are not dynamic (Though settable at runtime.)
+
+10. Notes (5), (7), and (9) would go a long ways to making this code suitable for a modern video encoder.
+
+11. Shot change works well but arbitrarily chosen thresholds need to be tuned.
+
+12. Given the documentation of other motion estimation code bases, I will GLADLY clarify and
+    document any piece of code upon request.
+
+13. Considerable effort has been put into the speed. I usually experience 10ms or less per frame for PAL on 2.8GHZ p4.
+
+Zachary Drew
+drew0054@tc.umn.edu
+
diff --git a/src/modules/motion_est/arrow_code.c b/src/modules/motion_est/arrow_code.c
new file mode 100644 (file)
index 0000000..08c0f69
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *     /brief Draw arrows
+ *     /author Zachary Drew, Copyright 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_frame.h>
+#include "arrow_code.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#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)))
+
+
+static int w;
+static int h;
+static int xstride;
+static int ystride;
+static mlt_image_format format;
+
+int init_arrows( mlt_image_format *image_format, int width, int height )
+{
+       w = width;
+       h = height;
+       format = *image_format;
+       switch( *image_format ) {
+               case mlt_image_yuv422:
+                       xstride = 2;
+                       ystride = xstride * w;
+                       break; 
+               default:
+                       // I don't know
+                       return 0;
+       }
+       return 1;
+
+}
+
+// ffmpeg borrowed
+static inline int clip(int a, int amin, int amax)
+{
+    if (a < amin)
+        return amin;
+    else if (a > amax)
+        return amax;
+    else
+        return a;
+}
+
+
+/**
+ * draws an line from (ex, ey) -> (sx, sy).
+ * Credits: modified from ffmpeg project
+ * @param ystride stride/linesize of the image
+ * @param xstride stride/element size of the image
+ * @param color color of the arrow
+ */
+void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey, int color)
+{
+    int t, x, y, fr, f;
+
+
+    sx= clip(sx, 0, w-1);
+    sy= clip(sy, 0, h-1);
+    ex= clip(ex, 0, w-1);
+    ey= clip(ey, 0, h-1);
+
+    buf[sy*ystride + sx*xstride]+= color;
+
+    if(ABS(ex - sx) > ABS(ey - sy)){
+        if(sx > ex){
+            t=sx; sx=ex; ex=t;
+            t=sy; sy=ey; ey=t;
+        }
+        buf+= sx*xstride + sy*ystride;
+        ex-= sx;
+        f= ((ey-sy)<<16)/ex;
+        for(x= 0; x <= ex; x++){
+            y = (x*f)>>16;
+            fr= (x*f)&0xFFFF;
+            buf[ y   *ystride + x*xstride]+= (color*(0x10000-fr))>>16;
+            buf[(y+1)*ystride + x*xstride]+= (color*         fr )>>16;
+        }
+    }else{
+        if(sy > ey){
+            t=sx; sx=ex; ex=t;
+            t=sy; sy=ey; ey=t;
+        }
+        buf+= sx*xstride + sy*ystride;
+        ey-= sy;
+        if(ey) f= ((ex-sx)<<16)/ey;
+        else   f= 0;
+        for(y= 0; y <= ey; y++){
+            x = (y*f)>>16;
+            fr= (y*f)&0xFFFF;
+            buf[y*ystride + x    *xstride]+= (color*(0x10000-fr))>>16;;
+            buf[y*ystride + (x+1)*xstride]+= (color*         fr )>>16;;
+        }
+    }
+}
+
+void draw_rectangle_fill(uint8_t *buf, int x, int y, int w, int h, int color)
+{
+       int i,j;
+       for ( i = 0; i < w; i++ ) 
+               for ( j = 0; j < h; j++ )
+                       buf[ (y+j)*ystride + (x+i)*xstride] = color; 
+}
+
+void draw_rectangle_outline(uint8_t *buf, int x, int y, int w, int h, int color)
+{
+       int i,j;
+       for ( i = 0; i < w; i++ ) {
+               buf[ y*ystride + (x+i)*xstride ] += color; 
+               buf[ (y+h)*ystride + (x+i)*xstride ] += color; 
+       }
+       for ( j = 1; j < h+1; j++ ) {
+               buf[ (y+j)*ystride + x*xstride ] += color;
+               buf[ (y+j)*ystride + (x+w)*xstride ] += color; 
+       }
+}
+/**
+ * draws an arrow from (ex, ey) -> (sx, sy).
+ * Credits: modified from ffmpeg project
+ * @param stride stride/linesize of the image
+ * @param color color of the arrow
+ */
+void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int color){
+
+       int dx,dy;
+       dx= ex - sx;
+       dy= ey - sy;
+
+       if(dx*dx + dy*dy > 3*3){
+               int rx=  dx + dy;
+               int ry= -dx + dy;
+               int length= sqrt((rx*rx + ry*ry)<<8);
+
+               rx= ROUNDED_DIV(rx*3<<4, length);
+               ry= ROUNDED_DIV(ry*3<<4, length);
+
+               draw_line(buf, sx, sy, sx + rx, sy + ry, color);
+               draw_line(buf, sx, sy, sx - ry, sy + rx, color);
+       }
+       draw_line(buf, sx, sy, ex, ey, color);
+}
diff --git a/src/modules/motion_est/arrow_code.h b/src/modules/motion_est/arrow_code.h
new file mode 100644 (file)
index 0000000..0fe5219
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ *     file: arrow_code.h
+ *
+ *     /brief Misc functions to draw arrows
+ *     /author Zachary Drew, Copyright 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+extern int init_arrows( mlt_image_format *image_format, int width, int height );
+extern void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey, int color);
+extern void draw_arrow(uint8_t *buf, int sx, int sy, int ex, int ey, int color);
+extern void draw_rectangle_fill(uint8_t *buf, int x, int y, int w, int h, int color);
+extern void draw_rectangle_outline(uint8_t *buf, int x, int y, int w, int h, int color);
diff --git a/src/modules/motion_est/factory.c b/src/modules/motion_est/factory.c
new file mode 100644 (file)
index 0000000..1df0929
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_vismv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_crop_detect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_slowmotion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "motion_est", filter_motion_est_init );
+       MLT_REGISTER( filter_type, "vismv", filter_vismv_init );
+       MLT_REGISTER( filter_type, "crop_detect", filter_crop_detect_init );
+       MLT_REGISTER( filter_type, "autotrack_rectangle", filter_autotrack_rectangle_init );
+       MLT_REGISTER( producer_type, "slowmotion", producer_slowmotion_init );
+}
diff --git a/src/modules/motion_est/filter_autotrack_rectangle.c b/src/modules/motion_est/filter_autotrack_rectangle.c
new file mode 100644 (file)
index 0000000..0d526e4
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ *     filter_autotrack_rectangle.c
+ *
+ *     /brief 
+ *     /author Zachary Drew, Copyright 2005
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "filter_motion_est.h"
+#include "arrow_code.h"
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#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)))
+
+void caculate_motion( struct motion_vector_s *vectors,
+                     mlt_geometry_item boundry,
+                     int macroblock_width,
+                     int macroblock_height,
+                     int mv_buffer_width,
+                     int method,
+                     int width,
+                     int height )
+{
+
+
+       // translate pixel units (from bounds) to macroblock units
+       // make sure whole macroblock stay within bounds
+       int left_mb = ( boundry->x + macroblock_width - 1 ) / macroblock_width;
+        int top_mb = ( boundry->y + macroblock_height - 1 ) / macroblock_height;
+        int right_mb = ( boundry->x + boundry->w ) / macroblock_width - 1;
+        int bottom_mb = ( boundry->y + boundry->h ) / macroblock_height - 1;
+
+       int i, j, n = 0;
+
+       int average_x = 0, average_y = 0;
+
+       #define CURRENT         ( vectors + j*mv_buffer_width + i )
+
+       for( i = left_mb; i <= right_mb; i++ ){
+               for( j = top_mb; j <= bottom_mb; j++ )
+               {
+                       n++;
+                       average_x += CURRENT->dx;
+                       average_y += CURRENT->dy;
+               }
+       }
+
+       if ( n == 0 ) return;
+
+       average_x /= n;
+       average_y /= n;
+
+       n = 0;
+       int average2_x = 0, average2_y = 0;
+       for( i = left_mb; i <= right_mb; i++ ){
+               for( j = top_mb; j <= bottom_mb; j++ ){
+
+                       if( ABS(CURRENT->dx - average_x) < 3 &&
+                           ABS(CURRENT->dy - average_y) < 3 )
+                       {
+                               n++;
+                               average2_x += CURRENT->dx;
+                               average2_y += CURRENT->dy;
+                       }
+               }
+       }
+
+       if ( n == 0 ) return;
+
+       boundry->x -= (double)average2_x / (double)n;
+       boundry->y -= (double)average2_y / (double)n;
+
+       if ( boundry->x < 0 )
+               boundry->x = 0;
+
+       if ( boundry->y < 0 )
+               boundry->y = 0;
+
+       if ( boundry->x + boundry->w > width )
+               boundry->x = width - boundry->w;
+
+       if ( boundry->y + boundry->h > height )
+               boundry->y = height - boundry->h;
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       // Get the filter object
+       mlt_filter filter = mlt_frame_pop_service( frame );
+
+       // Get the filter's property object
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
+
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
+
+       // Get the frame position
+       mlt_position position = mlt_frame_get_position( frame );
+
+       // Get the new image
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       if( error != 0 )
+               mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr );
+
+       // Get the geometry object
+       mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+
+       // Get the current geometry item
+       struct mlt_geometry_item_s boundry;
+       mlt_geometry_fetch(geometry, &boundry, position);
+
+       // Get the motion vectors
+       struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL );
+
+       // How did the rectangle move?
+       if( vectors != NULL &&
+           boundry.key != 1 ) // Paused?
+       {
+
+               int method = mlt_properties_get_int( filter_properties, "method" );
+
+               // Get the size of macroblocks in pixel units
+               int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" );
+               int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" );
+               int mv_buffer_width = *width / macroblock_width;
+
+               caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method, *width, *height );
+
+
+               // Make the geometry object a real boy
+               boundry.key = 1;
+               boundry.f[0] = 1;
+               boundry.f[1] = 1;
+               boundry.f[2] = 1;
+               boundry.f[3] = 1;
+               boundry.f[4] = 1;
+               mlt_geometry_insert(geometry, &boundry);
+       }
+
+               if( mlt_properties_get_int( filter_properties, "debug" ) == 1 )
+       {
+               init_arrows( format, *width, *height );
+               draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100);
+       }        
+
+       if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 )
+       {
+               mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL );
+
+               mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out");
+
+               // Because filter_obscure needs to be rewritten to use mlt_geometry
+               char geom[100];
+               sprintf( geom, "%d,%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom );
+       }
+               
+       if( mlt_properties_get_int( filter_properties, "collect" ) == 1 )
+       {
+               printf( "%d,%d,%d,%d\n", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h );
+               fflush( stdout );
+       }
+
+       return error;
+}
+
+static int attach_boundry_to_frame( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter object
+       mlt_filter filter = mlt_frame_pop_service( frame );
+
+       // Get the filter's property object
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter);
+
+       // Get the frame properties
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame);
+
+       // Get the frame position
+       mlt_position position = mlt_frame_get_position( frame );
+       
+       // Get the geometry object
+       mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+       if (geometry == NULL) {
+               mlt_geometry geom = mlt_geometry_init();
+               char *arg = mlt_properties_get(filter_properties, "geometry");
+
+               // Initialize with the supplied geometry
+               struct mlt_geometry_item_s item;
+               mlt_geometry_parse_item( geom, &item, arg  );
+
+               item.frame = 0;
+               item.key = 1;
+               item.mix = 100;
+
+               mlt_geometry_insert( geom, &item );
+               mlt_properties_set_data( filter_properties, "filter_geometry", geom, 0, (mlt_destructor)mlt_geometry_close, (mlt_serialiser)mlt_geometry_serialise );
+               geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL);
+       }
+
+       // Get the current geometry item
+       mlt_geometry_item geometry_item = mlt_pool_alloc( sizeof( struct mlt_geometry_item_s ) );
+       mlt_geometry_fetch(geometry, geometry_item, position);
+
+       mlt_properties_set_data( frame_properties, "bounds", geometry_item, sizeof( struct mlt_geometry_item_s ), mlt_pool_release, NULL );
+
+       // Get the new image
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       if( error != 0 )
+               mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle attach_boundry_to_frame", stderr );
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+        /* modify the frame with the current geometry */
+       mlt_frame_push_service( frame, this);
+       mlt_frame_push_get_image( frame, attach_boundry_to_frame );
+
+
+
+       /* apply the motion estimation filter */
+       mlt_filter motion_est = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_motion_est", NULL ); 
+       mlt_filter_process( motion_est, frame);
+
+
+
+       /* calculate the new geometry based on the motion */
+       mlt_frame_push_service( frame, this);
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+
+       /* visualize the motion vectors */
+       if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "debug" ) == 1 )
+       {
+               mlt_filter vismv = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_vismv", NULL );
+               if( vismv == NULL )
+               {
+                       mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+                       vismv = mlt_factory_filter( profile, "vismv", NULL );
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_vismv", vismv, 0, (mlt_destructor)mlt_filter_close, NULL );
+               }
+
+               mlt_filter_process( vismv, frame );
+       }
+
+       if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(this), "obscure" ) == 1 )
+       {
+               mlt_filter obscure = mlt_properties_get_data( MLT_FILTER_PROPERTIES(this), "_obscure", NULL );
+               if( obscure == NULL )
+               {
+                       mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+                       obscure = mlt_factory_filter( profile, "obscure", NULL );
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_obscure", obscure, 0, (mlt_destructor)mlt_filter_close, NULL );
+               }
+
+               mlt_filter_process( obscure, frame );
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+
+mlt_filter filter_autotrack_rectangle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+
+               // Initialize with the supplied geometry if ther is one
+               if( arg != NULL ) 
+                       mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", arg );
+               else
+                       mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "100,100:100x100" );
+
+               // create an instance of the motion_est and obscure filter
+               mlt_filter motion_est = mlt_factory_filter( profile, "motion_est", NULL );
+               if( motion_est != NULL )
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES(this), "_motion_est", motion_est, 0, (mlt_destructor)mlt_filter_close, NULL );
+               else {
+                       mlt_filter_close( this );
+                       return NULL;
+               }
+
+
+       }
+
+       return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
diff --git a/src/modules/motion_est/filter_crop_detect.c b/src/modules/motion_est/filter_crop_detect.c
new file mode 100644 (file)
index 0000000..8165141
--- /dev/null
@@ -0,0 +1,244 @@
+/**
+ *     /brief Crop Detection filter
+ *
+ *     /author Zachary Drew, Copyright 2005
+ *
+ *     inspired by mplayer's cropdetect filter
+ *
+ *     Note: The goemetry generated is zero-indexed and is inclusive of the end values 
+ *
+ *     Options:
+ *     -filter crop_detect debug=1                     // Visualize crop
+ *     -filter crop_detect frequency=25                // Detect the crop once a second
+ *     -filter crop_detect frequency=0                 // Never detect unless the producer changes
+ *     -filter crop_detect thresh=100                  // Changes the threshold (default = 25)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define DEBUG
+#define DEFAULT_THRESH 20
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "arrow_code.h"
+
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       // Get the filter object and properties
+       mlt_filter filter = mlt_frame_pop_service( this );
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the new image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if( error != 0 ) {
+               mlt_properties_debug( MLT_FRAME_PROPERTIES(this), "error after mlt_frame_get_image()", stderr );
+               return error;
+       }
+
+       // Parameter that describes how often to check for the crop
+       int frequency = mlt_properties_get_int( properties, "frequency");
+
+       // Producers may start with blank footage, by default we will skip, oh, 5 frames unless overridden
+       int skip = mlt_properties_get_int( properties, "skip");
+
+       // The result
+       mlt_geometry_item bounds = mlt_properties_get_data( properties, "bounds", NULL );
+
+       // Initialize if needed
+       if( bounds == NULL ) {
+               bounds = calloc( 1, sizeof( struct mlt_geometry_item_s ) );
+               bounds->w = *width;
+               bounds->h = *height;
+               mlt_properties_set_data( properties, "bounds", bounds, sizeof( struct mlt_geometry_item_s ), free, NULL );
+       }
+
+       // For periodic detection (with offset of 'skip')
+       if( frequency == 0 || (int)(mlt_frame_get_position(this)+skip) % frequency  != 0)
+       {
+               // Inject in stream 
+               mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
+
+               return 0;
+       }
+       
+
+       // There is no way to detect a crop for sure, so make up an arbitrary one
+       int thresh = mlt_properties_get_int( properties, "thresh" );
+
+       int xstride, ystride;
+
+       switch( *format ) {
+               case mlt_image_yuv422:
+                       xstride = 2;
+                       ystride = 2 * *width;
+                       break;
+               default:
+                       fprintf(stderr, "image format not supported by filter_crop_detect\n");
+                       return -1;
+       }
+
+       int x, y, average_brightness, deviation; // Scratch variables
+       uint8_t *q;
+
+       // Top crop
+       for( y = 0; y < *height/2; y++ ) {
+               bounds->y = y;
+               average_brightness = 0;
+               deviation = 0;
+               q = *image + y*ystride;
+               for( x = 0; x < *width; x++ )
+                       average_brightness += q[x*xstride];
+
+               average_brightness /= *width;
+
+               for( x = 0; x < *width; x++ )
+                       deviation += abs(average_brightness - q[x*xstride]);
+
+               if( deviation*10 >= thresh * *width )
+                       break;
+       }
+
+       // Bottom crop
+       for( y = *height - 1; y >= *height/2; y-- ) {
+               bounds->h = y;
+               average_brightness = 0;
+               deviation = 0;
+               q = *image + y*ystride;
+               for( x = 0; x < *width; x++ )
+                       average_brightness += q[x*xstride];
+
+               average_brightness /= *width;
+
+               for( x = 0; x < *width; x++ )
+                       deviation += abs(average_brightness - q[x*xstride]);
+
+               if( deviation*10 >= thresh * *width)
+                       break;
+       }
+
+       // Left crop    
+       for( x = 0; x < *width/2; x++ ) {
+               bounds->x = x;
+               average_brightness = 0;
+               deviation = 0;
+               q = *image + x*xstride;
+               for( y = 0; y < *height; y++ )
+                       average_brightness += q[y*ystride];
+
+               average_brightness /= *height;
+
+               for( y = 0; y < *height; y++ )
+                       deviation += abs(average_brightness - q[y*ystride]);
+
+               if( deviation*10 >= thresh * *width )
+                       break;
+       }
+
+       // Right crop
+       for( x = *width - 1; x >= *width/2; x-- ) {
+               bounds->w = x;
+               average_brightness = 0;
+               deviation = 0;
+               q = *image + x*xstride;
+               for( y = 0; y < *height; y++ )
+                       average_brightness += q[y*ystride];
+
+               average_brightness /= *height;
+
+               for( y = 0; y < *height; y++ )
+                       deviation += abs(average_brightness - q[y*ystride]);
+
+               if( deviation*10 >= thresh * *width )
+                       break;
+       }
+
+       /* Debug: Draw arrows to show crop */
+       if( mlt_properties_get_int( properties, "debug") == 1 )
+       {
+               init_arrows( format, *width, *height );
+
+               draw_arrow(*image, bounds->x, *height/2, bounds->x+50, *height/2, 100);
+               draw_arrow(*image, *width/2, bounds->y, *width/2, bounds->y+50, 100);
+               draw_arrow(*image, bounds->w, *height/2, bounds->w-50, *height/2, 100);
+               draw_arrow(*image, *width/2, bounds->h, *width/2, bounds->h-50, 100);
+               draw_arrow(*image, bounds->x, bounds->y, bounds->x+40, bounds->y+30, 100);
+               draw_arrow(*image, bounds->x, bounds->h, bounds->x+40, bounds->h-30, 100);
+               draw_arrow(*image, bounds->w, bounds->y, bounds->w-40, bounds->y+30, 100);
+               draw_arrow(*image, bounds->w, bounds->h, bounds->w-40, bounds->h-30, 100);
+       }
+
+       // Convert to width and correct indexing
+       bounds->w -= bounds->x - 1;
+       bounds->h -= bounds->y - 1;
+
+       if( mlt_properties_get_int( properties, "debug") == 1 )
+               fprintf(stderr, "Top:%f Left:%f Width:%f Height:%f\n", bounds->y, bounds->x, bounds->w, bounds->h);
+
+       /* inject into frame */
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES(this), "bounds", bounds, sizeof( struct mlt_geometry_item_s ), NULL, NULL );
+
+       return error;
+}
+
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       // Put the filter object somewhere we can find it
+       mlt_frame_push_service( frame, this);
+
+       // Push the frame filter
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+mlt_filter filter_crop_detect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+
+               /* defaults */
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "frequency", 1);
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "thresh", 5);
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "clip", 5);
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES(this), "former_producer_id", -1);
+
+       }
+
+       return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
+
diff --git a/src/modules/motion_est/filter_motion_est.c b/src/modules/motion_est/filter_motion_est.c
new file mode 100644 (file)
index 0000000..6e8b021
--- /dev/null
@@ -0,0 +1,1115 @@
+/*
+ *     /brief fast motion estimation filter
+ *     /author Zachary Drew, Copyright 2005
+ *
+ *     Currently only uses Gamma data for comparisonon (bug or feature?)
+ *     SSE optimized where available.
+ *
+ *     Vector orientation: The vector data that is generated for the current frame specifies
+ *     the motion from the previous frame to the current frame. To know how a macroblock
+ *     in the current frame will move in the future, the next frame is needed.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "filter_motion_est.h"
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#ifdef USE_SSE
+#include "sad_sse.h"
+#endif
+
+#define NDEBUG
+#include <assert.h>
+
+#undef DEBUG
+#undef DEBUG_ASM
+#undef BENCHMARK
+#undef COUNT_COMPARES
+
+#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)))
+
+
+struct motion_est_context_s
+{
+       int initialized;                                // true if filter has been initialized
+
+#ifdef COUNT_COMPARES
+       int compares;
+#endif
+
+       /* same as mlt_frame's parameters */
+       int width, height;
+
+       /* Operational details */
+       int mb_w, mb_h;
+       int xstride, ystride;
+       uint8_t *cache_image;                   // Copy of current frame
+       uint8_t *former_image;                  // Copy of former frame
+       int search_method;
+       int skip_prediction;
+       int shot_change;
+       int limit_x, limit_y;                   // max x and y of a motion vector
+       int initial_thresh;
+       int check_chroma;                       // if check_chroma == 1 then compare chroma
+       int denoise;
+       int previous_msad;
+       int show_reconstruction;
+       int toggle_when_paused;
+       int show_residual;
+
+       /* bounds */
+       struct mlt_geometry_item_s bounds;      // Current bounds (from filters crop_detect, autotrack rectangle, or other)
+
+       /* bounds in macroblock units; macroblocks are completely contained within the boundry */
+       int left_mb, prev_left_mb, right_mb, prev_right_mb;
+       int top_mb, prev_top_mb, bottom_mb, prev_bottom_mb;
+
+       /* size of our vector buffers */
+       int mv_buffer_height, mv_buffer_width, mv_size;
+
+       /* vector buffers */
+       int former_vectors_valid;               //<! true if the previous frame's buffered motion vector data is valid
+       motion_vector *former_vectors;
+       motion_vector *current_vectors;
+       motion_vector *denoise_vectors;
+       mlt_position former_frame_position, current_frame_position;
+
+       /* diagnostic metrics */
+       float predictive_misses;                // How often do the prediction motion vectors fail?
+       int comparison_average;                 // How far does the best estimation deviate from a perfect comparison?
+       int bad_comparisons;
+       int average_length;
+       int average_x, average_y;
+
+       /* run-time configurable comparison functions */
+       int (*compare_reference)(uint8_t *, uint8_t *, int, int, int, int);
+       int (*compare_optimized)(uint8_t *, uint8_t *, int, int, int, int);
+
+};
+
+// This is used to constrains pixel operations between two blocks to be within the image boundry
+inline static int constrain(   int *x, int *y, int *w, int *h,
+                               const int dx, const int dy,
+                               const int left, const int right,
+                               const int top, const int bottom)
+{
+       uint32_t penalty = 1 << SHIFT;                  // Retain a few extra bits of precision
+       int x2 = *x + dx;
+       int y2 = *y + dy;
+       int w_remains = *w;
+       int h_remains = *h;
+
+       // Origin of macroblock moves left of image boundy
+       if( *x < left || x2 < left ) {
+               w_remains = *w - left + ((*x < x2) ?  *x : x2);
+               *x += *w - w_remains;
+       }
+       // Portion of macroblock moves right of image boundry
+       else if( *x + *w > right || x2 + *w > right )
+               w_remains = right - ((*x > x2) ? *x : x2);
+
+       // Origin of macroblock moves above image boundy
+       if( *y < top || y2 < top ) {
+               h_remains = *h - top + ((*y < y2) ? *y : y2);
+               *y += *h - h_remains;
+       }
+       // Portion of macroblock moves bellow image boundry
+       else if( *y + *h > bottom || y2 + *h > bottom )
+               h_remains = bottom - ((*y > y2) ?  *y : y2);
+
+       if( w_remains == *w && h_remains == *h ) return penalty;
+       if( w_remains <= 0 || h_remains <= 0) return 0; // Block is clipped out of existance
+       penalty = (*w * *h * penalty)
+               / ( w_remains * h_remains);             // Recipricol of the fraction of the block that remains
+
+       assert(*x >= left); assert(x2 + *w - w_remains >= left);
+       assert(*y >= top); assert(y2 + *h - h_remains >= top);
+       assert(*x + w_remains <= right); assert(x2 + w_remains <= right);
+       assert(*y + h_remains <= bottom); assert(y2 + h_remains <= bottom);
+
+       *w = w_remains;                                 // Update the width and height
+       *h = h_remains;
+
+       return penalty;
+}
+
+/** /brief Reference Sum of Absolute Differences comparison function
+*
+*/
+static int sad_reference( uint8_t *block1, uint8_t *block2, const int xstride, const int ystride, const int w, const int h )
+{
+       int i, j, score = 0;
+       for ( j = 0; j < h; j++ ){
+               for ( i = 0; i < w; i++ ){
+                       score += ABS( block1[i*xstride] - block2[i*xstride] );
+               }
+               block1 += ystride;
+               block2 += ystride;
+       }
+
+       return score;
+}
+
+
+/** /brief Abstracted block comparison function
+*/
+inline static int block_compare( uint8_t *block1,
+                          uint8_t *block2,
+                          int x,
+                          int y,
+                          int dx,
+                          int dy,
+                          struct motion_est_context_s *c)
+{
+
+#ifdef COUNT_COMPARES
+       c->compares++;
+#endif
+
+       int score;
+
+       // Default comparison may be overridden by the slower, more capable reference comparison
+       int (*cmp)(uint8_t *, uint8_t *, int, int, int, int) = c->compare_optimized;
+
+       // vector displacement limited has been exceeded
+       if( ABS( dx ) >= c->limit_x || ABS( dy ) >= c->limit_y )
+               return MAX_MSAD;
+
+       int mb_w = c->mb_w;     // Some writeable local copies
+       int mb_h = c->mb_h;
+
+       // Determine if either macroblock got clipped
+       int penalty = constrain( &x, &y, &mb_w, &mb_h, dx, dy, 0, c->width, 0, c->height);
+
+       // Some gotchas
+       if( penalty == 0 )                      // Clipped out of existance: Return worst score
+               return MAX_MSAD;
+       else if( penalty != 1<<SHIFT )          // Nonstandard macroblock dimensions: Disable SIMD optimizizations.
+               cmp = c->compare_reference;
+
+       // Calculate the memory locations of the macroblocks
+       block1 += x      * c->xstride + y       * c->ystride;
+       block2 += (x+dx) * c->xstride + (y+dy)  * c->ystride;
+
+       #ifdef DEBUG_ASM
+       if( penalty == 1<<SHIFT ){
+               score = c->compare_reference( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+               int score2 = c->compare_optimized( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+               if ( score != score2 )
+                       fprintf(stderr, "Your assembly doesn't work! Reference: %d Asm: %d\n", score, score2);
+       }
+       else
+       #endif
+
+       score = cmp( block1, block2, c->xstride, c->ystride, mb_w, mb_h );
+
+       return ( score * penalty ) >> SHIFT;                    // Ditch the extra precision
+}
+
+static inline void check_candidates (   uint8_t *ref,
+                                       uint8_t *candidate_base,
+                                       const int x,
+                                       const int y,
+                                       const motion_vector *candidates,// Contains to_x & to_y
+                                       const int count,                // Number of candidates
+                                       const int unique,               // Sometimes we know the candidates are unique
+                                       motion_vector *result,
+                                       struct motion_est_context_s *c )
+{
+               int score, i, j;
+               /* Scan for the best candidate */
+               for ( i = 0; i < count; i++ )
+               {
+                       // this little dohicky ignores duplicate candidates, if they are possible
+                       if ( unique == 0 ) {
+                               j = 0;
+                               while ( j < i )
+                               {
+                                       if ( candidates[j].dx == candidates[i].dx &&
+                                            candidates[j].dy == candidates[i].dy )
+                                                       goto next_for_loop;
+
+                                       j++;
+                               }
+                       }
+
+                       // Luma
+                       score = block_compare( ref, candidate_base,
+                                               x, y,
+                                               candidates[i].dx,       // from
+                                               candidates[i].dy,
+                                               c);
+
+                       if ( score < result->msad ) {   // New minimum
+                               result->dx = candidates[i].dx;
+                               result->dy = candidates[i].dy;
+                               result->msad = score;
+                       }
+                       next_for_loop:;
+               }
+}
+
+/* /brief Diamond search
+* Operates on a single macroblock
+*/
+static inline void diamond_search(
+                       uint8_t *ref,                           //<! Image data from previous frame
+                       uint8_t *candidate_base,                //<! Image data in current frame
+                       const int x,                            //<! X upper left corner of macroblock
+                       const int y,                            //<! U upper left corner of macroblock
+                       struct motion_vector_s *result,         //<! Best predicted mv and eventual result
+                       struct motion_est_context_s *c)         //<! motion estimation context
+{
+
+       // diamond search pattern
+       motion_vector candidates[4];
+
+       // Keep track of best and former best candidates
+       motion_vector best, former;
+       best.dx = 0;
+       best.dy = 0;
+       former.dx = 0;
+       former.dy = 0;
+
+       // The direction of the refinement needs to be known
+       motion_vector current;
+
+       int i, first = 1;
+
+       // Loop through the search pattern
+       while( 1 ) {
+
+               current.dx = result->dx;
+               current.dy = result->dy;
+
+               if ( first == 1 )       // Set the initial pattern
+               {
+                       candidates[0].dx = result->dx + 1; candidates[0].dy = result->dy + 0;
+                       candidates[1].dx = result->dx + 0; candidates[1].dy = result->dy + 1;
+                       candidates[2].dx = result->dx - 1; candidates[2].dy = result->dy + 0;
+                       candidates[3].dx = result->dx + 0; candidates[3].dy = result->dy - 1;
+                       i = 4;
+               }
+               else     // Construct the next portion of the search pattern
+               {
+                       candidates[0].dx = result->dx + best.dx;
+                       candidates[0].dy = result->dy + best.dy;
+                       if (best.dx == former.dx && best.dy == former.dy) {
+                               candidates[1].dx = result->dx + best.dy;
+                               candidates[1].dy = result->dy + best.dx;                //      Yes, the wires
+                               candidates[2].dx = result->dx - best.dy;                //      are crossed
+                               candidates[2].dy = result->dy - best.dx;
+                               i = 3;
+                       } else {
+                               candidates[1].dx = result->dx + former.dx;
+                               candidates[1].dy = result->dy + former.dy;
+                               i = 2;
+                       }
+
+                       former.dx = best.dx; former.dy = best.dy;       // Keep track of new former best
+               }
+
+               check_candidates ( ref, candidate_base, x, y, candidates, i, 1, result, c );
+
+               // Which candidate was the best?
+               best.dx = result->dx - current.dx;
+               best.dy = result->dy - current.dy;
+
+               // A better canidate was not found
+               if ( best.dx == 0 && best.dy == 0 )
+                       return;
+
+               if ( first == 1 ){
+                       first = 0;
+                       former.dx = best.dx; former.dy = best.dy; // First iteration, sensible value for former.d*
+               }
+       }
+}
+
+/* /brief Full (brute) search
+* Operates on a single macroblock
+*/
+__attribute__((used))
+static void full_search(
+                       uint8_t *ref,                           //<! Image data from previous frame
+                       uint8_t *candidate_base,                //<! Image data in current frame
+                       int x,                                  //<! X upper left corner of macroblock
+                       int y,                                  //<! U upper left corner of macroblock
+                       struct motion_vector_s *result,         //<! Best predicted mv and eventual result
+                       struct motion_est_context_s *c)         //<! motion estimation context
+{
+       // Keep track of best candidate
+       int i,j,score;
+
+       // Go loopy
+       for( i = -c->mb_w; i <= c->mb_w; i++ ){
+               for( j = -c->mb_h; j <=  c->mb_h; j++ ){
+
+                       score = block_compare( ref, candidate_base,
+                                               x,
+                                               y,
+                                               x + i,
+                                               y + j,
+                                               c);
+
+                       if ( score < result->msad ) {
+                               result->dx = i;
+                               result->dy = j;
+                               result->msad = score;
+                       }
+               }
+       }
+}
+
+// Macros for pointer calculations
+#define CURRENT(i,j)   ( c->current_vectors + (j)*c->mv_buffer_width + (i) )
+#define FORMER(i,j)    ( c->former_vectors + (j)*c->mv_buffer_width + (i) )
+#define DENOISE(i,j)   ( c->denoise_vectors + (j)*c->mv_buffer_width + (i) )
+
+int ncompare (const void * a, const void * b)
+{
+       return ( *(const int*)a - *(const int*)b );
+}
+
+// motion vector denoising
+// for x and y components seperately,
+// change the vector to be the median value of the 9 adjacent vectors
+static void median_denoise( motion_vector *v, struct motion_est_context_s *c )
+{
+       int xvalues[9], yvalues[9];
+
+       int i,j,n;
+       for( j = c->top_mb; j <= c->bottom_mb; j++ )
+               for( i = c->left_mb; i <= c->right_mb; i++ ){
+               {
+                       n = 0;
+
+                       xvalues[n  ] = CURRENT(i,j)->dx; // Center
+                       yvalues[n++] = CURRENT(i,j)->dy;
+
+                       if( i > c->left_mb ) // Not in First Column
+                       {
+                               xvalues[n  ] = CURRENT(i-1,j)->dx; // Left
+                               yvalues[n++] = CURRENT(i-1,j)->dy;
+
+                               if( j > c->top_mb ) {
+                                       xvalues[n  ] = CURRENT(i-1,j-1)->dx; // Upper Left
+                                       yvalues[n++] = CURRENT(i-1,j-1)->dy;
+                               }
+
+                               if( j < c->bottom_mb ) {
+                                       xvalues[n  ] = CURRENT(i-1,j+1)->dx; // Bottom Left
+                                       yvalues[n++] = CURRENT(i-1,j+1)->dy;
+                               }
+                       }
+                       if( i < c->right_mb ) // Not in Last Column
+                       {
+                               xvalues[n  ] = CURRENT(i+1,j)->dx; // Right
+                               yvalues[n++] = CURRENT(i+1,j)->dy;
+
+
+                               if( j > c->top_mb ) {
+                                       xvalues[n  ] = CURRENT(i+1,j-1)->dx; // Upper Right
+                                       yvalues[n++] = CURRENT(i+1,j-1)->dy;
+                               }
+
+                               if( j < c->bottom_mb ) {
+                                       xvalues[n  ] = CURRENT(i+1,j+1)->dx; // Bottom Right
+                                       yvalues[n++] = CURRENT(i+1,j+1)->dy;
+                               }
+                       }
+                       if( j > c->top_mb ) // Not in First Row
+                       {
+                               xvalues[n  ] = CURRENT(i,j-1)->dx; // Top
+                               yvalues[n++] = CURRENT(i,j-1)->dy;
+                       }
+
+                       if( j < c->bottom_mb ) // Not in Last Row
+                       {
+                               xvalues[n  ] = CURRENT(i,j+1)->dx; // Bottom
+                               yvalues[n++] = CURRENT(i,j+1)->dy;
+                       }
+
+                       qsort (xvalues, n, sizeof(int), ncompare);
+                       qsort (yvalues, n, sizeof(int), ncompare);
+
+                       if( n % 2 == 1 ) {
+                               DENOISE(i,j)->dx = xvalues[n/2];
+                               DENOISE(i,j)->dy = yvalues[n/2];
+                       }
+                       else {
+                               DENOISE(i,j)->dx = (xvalues[n/2] + xvalues[n/2+1])/2;
+                               DENOISE(i,j)->dy = (yvalues[n/2] + yvalues[n/2+1])/2;
+                       }
+               }
+       }
+
+       motion_vector *t = c->current_vectors;
+       c->current_vectors = c->denoise_vectors;
+       c->denoise_vectors = t;
+
+}
+
+// Credits: ffmpeg
+// return the median
+static inline int median_predictor(int a, int b, int c) {
+       if ( a > b ){
+               if ( c > b ){
+                       if ( c > a  ) b = a;
+                       else      b = c;
+               }
+       } else {
+               if ( b > c ){
+                       if ( c > a ) b = c;
+                       else     b = a;
+               }
+       }
+       return b;
+}
+
+
+/** /brief Motion search
+*
+* For each macroblock in the current frame, estimate the block from the last frame that
+* matches best.
+*
+* Vocab: Colocated - the pixel in the previous frame at the current position
+*
+* Based on enhanced predictive zonal search. [Tourapis 2002]
+*/
+static void motion_search( uint8_t *from,                      //<! Image data.
+                          uint8_t *to,                         //<! Image data. Rigid grid.
+                          struct motion_est_context_s *c)      //<! The context
+{
+
+#ifdef COUNT_COMPARES
+       compares = 0;
+#endif
+
+       motion_vector candidates[10];
+       motion_vector *here;            // This one gets used alot (about 30 times per macroblock)
+       int n = 0;
+
+       int i, j, count=0;
+
+       // For every macroblock, perform motion vector estimation
+       for( i = c->left_mb; i <= c->right_mb; i++ ){
+        for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+               here = CURRENT(i,j);
+               here->valid = 1;
+               here->color = 100;
+               here->msad = MAX_MSAD;
+               count++;
+               n = 0;
+
+
+               /* Stack the predictors [i.e. checked in reverse order] */
+
+               /* Adjacent to collocated */
+               if( c->former_vectors_valid )
+               {
+                       // Top of colocated
+                       if( j > c->prev_top_mb ){// && COL_TOP->valid ){
+                               candidates[n  ].dx = FORMER(i,j-1)->dx;
+                               candidates[n++].dy = FORMER(i,j-1)->dy;
+                       }
+
+                       // Left of colocated
+                       if( i > c->prev_left_mb ){// && COL_LEFT->valid ){
+                               candidates[n  ].dx = FORMER(i-1,j)->dx;
+                               candidates[n++].dy = FORMER(i-1,j)->dy;
+                       }
+
+                       // Right of colocated
+                       if( i < c->prev_right_mb ){// && COL_RIGHT->valid ){
+                               candidates[n  ].dx = FORMER(i+1,j)->dx;
+                               candidates[n++].dy = FORMER(i+1,j)->dy;
+                       }
+
+                       // Bottom of colocated
+                       if( j < c->prev_bottom_mb ){// && COL_BOTTOM->valid ){
+                               candidates[n  ].dx = FORMER(i,j+1)->dx;
+                               candidates[n++].dy = FORMER(i,j+1)->dy;
+                       }
+
+                       // And finally, colocated
+                       candidates[n  ].dx = FORMER(i,j)->dx;
+                       candidates[n++].dy = FORMER(i,j)->dy;
+               }
+
+               // For macroblocks not in the top row
+               if ( j > c->top_mb) {
+
+                       // Top if ( TOP->valid ) {
+                               candidates[n  ].dx = CURRENT(i,j-1)->dx;
+                               candidates[n++].dy = CURRENT(i,j-1)->dy;
+                       //}
+
+                       // Top-Right, macroblocks not in the right row
+                       if ( i < c->right_mb ){// && TOP_RIGHT->valid ) {
+                               candidates[n  ].dx = CURRENT(i+1,j-1)->dx;
+                               candidates[n++].dy = CURRENT(i+1,j-1)->dy;
+                       }
+               }
+
+               // Left, Macroblocks not in the left column
+               if ( i > c->left_mb ){// && LEFT->valid ) {
+                       candidates[n  ].dx = CURRENT(i-1,j)->dx;
+                       candidates[n++].dy = CURRENT(i-1,j)->dy;
+               }
+
+               /* Median predictor vector (median of left, top, and top right adjacent vectors) */
+               if ( i > c->left_mb && j > c->top_mb && i < c->right_mb
+                        )//&& LEFT->valid && TOP->valid && TOP_RIGHT->valid )
+               {
+                       candidates[n  ].dx = median_predictor( CURRENT(i-1,j)->dx, CURRENT(i,j-1)->dx, CURRENT(i+1,j-1)->dx);
+                       candidates[n++].dy = median_predictor( CURRENT(i-1,j)->dy, CURRENT(i,j-1)->dy, CURRENT(i+1,j-1)->dy);
+               }
+
+               // Zero vector
+               candidates[n  ].dx = 0;
+               candidates[n++].dy = 0;
+
+               int x = i * c->mb_w;
+               int y = j * c->mb_h;
+               check_candidates ( to, from, x, y, candidates, n, 0, here, c );
+
+
+#ifndef FULLSEARCH
+               diamond_search( to, from, x, y, here, c);
+#else
+               full_search( to, from, x, y, here, c);
+#endif
+
+               assert( x + c->mb_w + here->dx > 0 );   // All macroblocks must have area > 0
+               assert( y + c->mb_h + here->dy > 0 );
+               assert( x + here->dx < c->width );
+               assert( y + here->dy < c->height );
+
+        } /* End column loop */
+       } /* End row loop */
+
+#ifdef USE_SSE
+       asm volatile ( "emms" );
+#endif
+
+#ifdef COUNT_COMPARES
+       fprintf(stderr, "%d comparisons per block were made", compares/count);
+#endif
+       return;
+}
+
+void collect_post_statistics( struct motion_est_context_s *c ) {
+
+       c->comparison_average = 0;
+       c->average_length = 0;
+       c->average_x = 0;
+       c->average_y = 0;
+
+       int i, j, count = 0;
+
+       for ( i = c->left_mb; i <= c->right_mb; i++ ){
+        for ( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+               count++;
+               c->comparison_average += CURRENT(i,j)->msad;
+               c->average_x += CURRENT(i,j)->dx;
+               c->average_y += CURRENT(i,j)->dy;
+
+
+        }
+       }
+
+       if ( count > 0 )
+       {
+               c->comparison_average /= count;
+               c->average_x /= count;
+               c->average_y /= count;
+               c->average_length = sqrt( c->average_x * c->average_x + c->average_y * c->average_y );
+       }
+
+}
+
+static void init_optimizations( struct motion_est_context_s *c )
+{
+       switch(c->mb_w){
+#ifdef USE_SSE
+               case 4:  if(c->mb_h == 4)       c->compare_optimized = sad_sse_422_luma_4x4;
+                        else                           c->compare_optimized = sad_sse_422_luma_4w;
+                        break;
+               case 8:  if(c->mb_h == 8)  c->compare_optimized = sad_sse_422_luma_8x8;
+                        else                           c->compare_optimized = sad_sse_422_luma_8w;
+                        break;
+               case 16: if(c->mb_h == 16) c->compare_optimized = sad_sse_422_luma_16x16;
+                        else                           c->compare_optimized = sad_sse_422_luma_16w;
+                        break;
+               case 32: if(c->mb_h == 32) c->compare_optimized = sad_sse_422_luma_32x32;
+                        else                           c->compare_optimized = sad_sse_422_luma_32w;
+                        break;
+               case 64: c->compare_optimized = sad_sse_422_luma_64w;
+                        break;
+#endif
+               default: c->compare_optimized = sad_reference;
+                        break;
+       }
+}
+
+inline static void set_red(uint8_t *image, struct motion_est_context_s *c)
+{
+       int n;
+       for( n = 0; n < c->width * c->height * 2; n+=4 )
+       {
+               image[n]   = 79;
+               image[n+1] = 91;
+               image[n+2] = 79;
+               image[n+3] = 237;
+       }
+
+}
+
+static void show_residual( uint8_t *result,  struct motion_est_context_s *c )
+{
+       int i, j;
+       int x,y,w,h;
+       int dx, dy;
+       int tx,ty;
+       uint8_t *b, *r;
+
+//     set_red(result,c);
+
+       for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+        for( i = c->left_mb; i <= c->right_mb; i++ ){
+
+               dx = CURRENT(i,j)->dx;
+               dy = CURRENT(i,j)->dy;
+               w = c->mb_w;
+               h = c->mb_h;
+               x = i * w;
+               y = j * h;
+
+               // Denoise function caused some blocks to be completely clipped, ignore them
+               if (constrain( &x, &y, &w, &h, dx, dy, 0, c->width, 0, c->height) == 0 )
+                       continue;
+
+               for( ty = y; ty < y + h ; ty++ ){
+                for( tx = x; tx < x + w ; tx++ ){
+
+                       b = c->former_image +  (tx+dx)*c->xstride + (ty+dy)*c->ystride;
+                       r = result +            tx*c->xstride + ty*c->ystride;
+
+                       r[0] = 16 + ABS( r[0] - b[0] );
+
+                       if( dx % 2 == 0 )
+                               r[1] = 128 + ABS( r[1] - b[1] );
+                       else
+                               // FIXME: may exceed boundies
+                               r[1] = 128 + ABS( r[1] - ( *(b-1) + b[3] ) /2 );
+                }
+               }
+        }
+       }
+}
+
+static void show_reconstruction( uint8_t *result, struct motion_est_context_s *c )
+{
+       int i, j;
+       int x,y,w,h;
+       int dx,dy;
+       uint8_t *r, *s;
+       int tx,ty;
+
+       for( i = c->left_mb; i <= c->right_mb; i++ ){
+        for( j = c->top_mb; j <= c->bottom_mb; j++ ){
+
+               dx = CURRENT(i,j)->dx;
+               dy = CURRENT(i,j)->dy;
+               w = c->mb_w;
+               h = c->mb_h;
+               x = i * w;
+               y = j * h;
+
+               // Denoise function caused some blocks to be completely clipped, ignore them
+               if (constrain( &x, &y, &w, &h, dx, dy, 0, c->width, 0, c->height) == 0 )
+                       continue;
+
+               for( ty = y; ty < y + h ; ty++ ){
+                for( tx = x; tx < x + w ; tx++ ){
+
+                       r = result +          tx*c->xstride + ty*c->ystride;
+                       s = c->former_image + (tx+dx)*c->xstride + (ty+dy)*c->ystride;
+
+                       r[0] = s[0];
+
+                       if( dx % 2 == 0 )
+                               r[1] = s[1];
+                       else
+                               // FIXME: may exceed boundies
+                               r[1] = ( *(s-1) + s[3] ) /2;
+                }
+               }
+        }
+       }
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter
+       mlt_filter filter = mlt_frame_pop_service( frame );
+
+       // Get the motion_est context object
+       struct motion_est_context_s *c = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "context", NULL);
+
+
+       // Get the new image and frame number
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       #ifdef BENCHMARK
+       struct timeval start; gettimeofday(&start, NULL );
+       #endif
+
+
+       if( error != 0 )
+               mlt_properties_debug( MLT_FRAME_PROPERTIES(frame), "error after mlt_frame_get_image() in motion_est", stderr );
+
+       c->current_frame_position = mlt_frame_get_position( frame );
+
+       /* Context Initialization */
+       if ( c->initialized == 0 ) {
+
+               // Get the filter properties object
+               mlt_properties properties = mlt_filter_properties( filter );
+
+               c->width = *width;
+               c->height = *height;
+
+               /* Get parameters that may have been overridden */
+               if( mlt_properties_get( properties, "macroblock_width") != NULL )
+                       c->mb_w = mlt_properties_get_int( properties, "macroblock_width");
+
+               if( mlt_properties_get( properties, "macroblock_height") != NULL )
+                       c->mb_h = mlt_properties_get_int( properties, "macroblock_height");
+
+               if( mlt_properties_get( properties, "prediction_thresh") != NULL )
+                       c->initial_thresh = mlt_properties_get_int( properties, "prediction_thresh" );
+               else
+                       c->initial_thresh = c->mb_w * c->mb_h;
+
+               if( mlt_properties_get( properties, "search_method") != NULL )
+                       c->search_method = mlt_properties_get_int( properties, "search_method");
+
+               if( mlt_properties_get( properties, "skip_prediction") != NULL )
+                       c->skip_prediction = mlt_properties_get_int( properties, "skip_prediction");
+
+               if( mlt_properties_get( properties, "limit_x") != NULL )
+                       c->limit_x = mlt_properties_get_int( properties, "limit_x");
+
+               if( mlt_properties_get( properties, "limit_y") != NULL )
+                       c->limit_y = mlt_properties_get_int( properties, "limit_y");
+
+               if( mlt_properties_get( properties, "check_chroma" ) != NULL )
+                       c->check_chroma = mlt_properties_get_int( properties, "check_chroma" );
+
+               if( mlt_properties_get( properties, "denoise" ) != NULL )
+                       c->denoise = mlt_properties_get_int( properties, "denoise" );
+
+               if( mlt_properties_get( properties, "show_reconstruction" ) != NULL )
+                       c->show_reconstruction = mlt_properties_get_int( properties, "show_reconstruction" );
+
+               if( mlt_properties_get( properties, "show_residual" ) != NULL )
+                       c->show_residual = mlt_properties_get_int( properties, "show_residual" );
+
+               if( mlt_properties_get( properties, "toggle_when_paused" ) != NULL )
+                       c->toggle_when_paused = mlt_properties_get_int( properties, "toggle_when_paused" );
+
+               init_optimizations( c );
+
+               // Calculate the dimensions in macroblock units
+               c->mv_buffer_width = (*width / c->mb_w);
+               c->mv_buffer_height = (*height / c->mb_h);
+
+               // Size of the motion vector buffer
+               c->mv_size =  c->mv_buffer_width * c->mv_buffer_height * sizeof(struct motion_vector_s);
+
+               // Allocate the motion vector buffers
+               c->former_vectors = mlt_pool_alloc( c->mv_size );
+               c->current_vectors = mlt_pool_alloc( c->mv_size );
+               c->denoise_vectors = mlt_pool_alloc( c->mv_size );
+
+               // Register motion buffers for destruction
+               mlt_properties_set_data( properties, "current_motion_vectors", (void *)c->current_vectors, 0, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "former_motion_vectors", (void *)c->former_vectors, 0, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "denoise_motion_vectors", (void *)c->denoise_vectors, 0, mlt_pool_release, NULL );
+
+               c->former_vectors_valid = 0;
+               memset( c->former_vectors, 0, c->mv_size );
+
+               // Calculate the size of our steps (the number of bytes that seperate adjacent pixels in X and Y direction)
+               switch( *format ) {
+                       case mlt_image_yuv422:
+                               c->xstride = 2;
+                               c->ystride = c->xstride * *width;
+                               break;
+                       default:
+                               // I don't know
+                               fprintf(stderr, "\"I am unfamiliar with your new fangled pixel format!\" -filter_motion_est\n");
+                               return -1;
+               }
+
+               // Allocate a cache for the previous frame's image
+               c->former_image = mlt_pool_alloc( *width * *height * 2 );
+               c->cache_image = mlt_pool_alloc( *width * *height * 2 );
+
+               // Register for destruction
+               mlt_properties_set_data( properties, "cache_image", (void *)c->cache_image, 0, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "former_image", (void *)c->former_image, 0, mlt_pool_release, NULL );
+
+               c->former_frame_position = c->current_frame_position;
+               c->previous_msad = 0;
+
+               c->initialized = 1;
+       }
+
+       /* Check to see if somebody else has given us bounds */
+       struct mlt_geometry_item_s *bounds = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "bounds", NULL );
+
+       if( bounds != NULL ) {
+               // translate pixel units (from bounds) to macroblock units
+               // make sure whole macroblock stays within bounds
+               c->left_mb = ( bounds->x + c->mb_w - 1 ) / c->mb_w;
+               c->top_mb = ( bounds->y + c->mb_h - 1 ) / c->mb_h;
+               c->right_mb = ( bounds->x + bounds->w ) / c->mb_w - 1;
+               c->bottom_mb = ( bounds->y + bounds->h ) / c->mb_h - 1;
+               c->bounds.x = bounds->x;
+               c->bounds.y = bounds->y;
+               c->bounds.w = bounds->w;
+               c->bounds.h = bounds->h;
+       } else {
+               c->left_mb = c->prev_left_mb = 0;
+               c->top_mb = c->prev_top_mb = 0;
+               c->right_mb = c->prev_right_mb = c->mv_buffer_width - 1;        // Zero indexed
+               c->bottom_mb = c->prev_bottom_mb = c->mv_buffer_height - 1;
+               c->bounds.x = 0;
+               c->bounds.y = 0;
+               c->bounds.w = *width;
+               c->bounds.h = *height;
+       }
+
+       // If video is advancing, run motion vector algorithm and etc...
+       if( c->former_frame_position + 1 == c->current_frame_position )
+       {
+
+               // Swap the motion vector buffers and reuse allocated memory
+               struct motion_vector_s *temp = c->current_vectors;
+               c->current_vectors = c->former_vectors;
+               c->former_vectors = temp;
+
+               // This is done because filter_vismv doesn't pay attention to frame boundry
+               memset( c->current_vectors, 0, c->mv_size );
+
+               // Perform the motion search
+               motion_search( c->cache_image, *image, c );
+
+               collect_post_statistics( c );
+
+
+               // Detect shot changes
+               if( c->comparison_average > 10 * c->mb_w * c->mb_h &&
+                   c->comparison_average > c->previous_msad * 2 )
+               {
+                       fprintf(stderr, " - SAD: %d   <<Shot change>>\n", c->comparison_average);
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "shot_change", 1);
+               //      c->former_vectors_valid = 0; // Invalidate the previous frame's predictors
+                       c->shot_change = 1;
+               }
+               else {
+                       c->former_vectors_valid = 1;
+                       c->shot_change = 0;
+                       //fprintf(stderr, " - SAD: %d\n", c->comparison_average);
+               }
+
+               c->previous_msad = c->comparison_average;
+
+               if( c->comparison_average != 0 ) { // If the frame is not a duplicate of the previous frame
+
+                       // denoise the vector buffer
+                       if( c->denoise )
+                               median_denoise( c->current_vectors, c );
+
+                       // Pass the new vector data into the frame
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+                                        (void*)c->current_vectors, c->mv_size, NULL, NULL );
+
+                       // Cache the frame's image. Save the old cache. Reuse memory.
+                       // After this block, exactly two unique frames will be cached
+                       uint8_t *timg = c->cache_image;
+                       c->cache_image = c->former_image;
+                       c->former_image = timg;
+                       memcpy( c->cache_image, *image, *width * *height * c->xstride );
+
+
+               }
+               else {
+                       // Undo the Swap, This fixes the ugliness caused by a duplicate frame
+                       temp = c->current_vectors;
+                       c->current_vectors = c->former_vectors;
+                       c->former_vectors = temp;
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+                                        (void*)c->former_vectors, c->mv_size, NULL, NULL );
+               }
+
+
+               if( c->shot_change == 1)
+                       ;
+               else if( c->show_reconstruction )
+                       show_reconstruction( *image, c );
+               else if( c->show_residual )
+                       show_residual( *image, c );
+
+       }
+       // paused
+       else if( c->former_frame_position == c->current_frame_position )
+       {
+               // Pass the old vector data into the frame if it's valid
+               if( c->former_vectors_valid == 1 ) {
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "motion_est.vectors",
+                                        (void*)c->current_vectors, c->mv_size, NULL, NULL );
+
+                       if( c->shot_change == 1)
+                               ;
+                       else if( c->toggle_when_paused == 1 ) {
+                               if( c->show_reconstruction )
+                                       show_reconstruction( *image, c );
+                               else if( c->show_residual )
+                                       show_residual( *image, c );
+                               c->toggle_when_paused = 2;
+                       }
+                       else if( c->toggle_when_paused == 2 )
+                               c->toggle_when_paused = 1;
+                       else {
+                               if( c->show_reconstruction )
+                                       show_reconstruction( *image, c );
+                               else if( c->show_residual )
+                                       show_residual( *image, c );
+                       }
+
+               }
+
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "shot_change", c->shot_change);
+       }
+       // there was jump in frame number
+       else {
+//             fprintf(stderr, "Warning: there was a frame number jumped from %d to %d.\n", c->former_frame_position, c->current_frame_position);
+               c->former_vectors_valid = 0;
+       }
+
+
+       // Cache our bounding geometry for the next frame's processing
+       c->prev_left_mb = c->left_mb;
+       c->prev_top_mb = c->top_mb;
+       c->prev_right_mb = c->right_mb;
+       c->prev_bottom_mb = c->bottom_mb;
+
+       // Remember which frame this is
+       c->former_frame_position = c->current_frame_position;
+
+
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.macroblock_width", c->mb_w );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.macroblock_height", c->mb_h );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.left_mb", c->left_mb );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.right_mb", c->right_mb );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.top_mb", c->top_mb );
+       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "motion_est.bottom_mb", c->bottom_mb );
+
+       #ifdef BENCHMARK
+       struct timeval finish; gettimeofday(&finish, NULL ); int difference = (finish.tv_sec - start.tv_sec) * 1000000 + (finish.tv_usec - start.tv_usec);
+       fprintf(stderr, " in frame %d:%d usec\n", c->current_frame_position, difference);
+       #endif
+
+
+       return error;
+}
+
+
+
+/** filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       // Keeps tabs on the filter object
+       mlt_frame_push_service( frame, this);
+
+       // Push the frame filter
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               // Get the properties object
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+
+               // Initialize the motion estimation context
+               struct motion_est_context_s *context;
+               context = mlt_pool_alloc( sizeof(struct motion_est_context_s) );
+               mlt_properties_set_data( properties, "context", (void *)context, sizeof( struct motion_est_context_s ),
+                                       mlt_pool_release, NULL );
+
+
+               // Register the filter
+               this->process = filter_process;
+
+               /* defaults that may be overridden */
+               context->mb_w = 16;
+               context->mb_h = 16;
+               context->skip_prediction = 0;
+               context->limit_x = 64;
+               context->limit_y = 64;
+               context->search_method = DIAMOND_SEARCH;        // FIXME: not used
+               context->check_chroma = 0;
+               context->denoise = 1;
+               context->show_reconstruction = 0;
+               context->show_residual = 0;
+               context->toggle_when_paused = 0;
+
+               /* reference functions that may have optimized versions */
+               context->compare_reference = sad_reference;
+
+               // The rest of the buffers will be initialized when the filter is first processed
+               context->initialized = 0;
+       }
+       return this;
+}
diff --git a/src/modules/motion_est/filter_motion_est.h b/src/modules/motion_est/filter_motion_est.h
new file mode 100644 (file)
index 0000000..296287e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Perform motion estimation
+ * Zachary K Drew, Copyright 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _FILTER_MOTION_EST_H_
+#define _FILTER_MOTION_EST_H_
+
+#include <framework/mlt_filter.h>
+
+extern mlt_filter filter_motion_est_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#define MAX_MSAD 0xffff
+
+struct motion_vector_s
+{
+       int msad;       //<! Sum of Absolute Differences for this vector
+       int dx;         //<! integer X displacement
+       int dy;         //<! integer Y displacement
+       int vert_dev;   //<! a measure of vertical color deviation
+       int horiz_dev;  //<! a measure of horizontal color deviation
+       int valid;
+       int quality;
+       uint8_t color;  //<! The color
+};
+       
+typedef struct motion_vector_s motion_vector;
+#endif
diff --git a/src/modules/motion_est/filter_vismv.c b/src/modules/motion_est/filter_vismv.c
new file mode 100644 (file)
index 0000000..eb0d987
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ *     /brief Draw motion vectors
+ *     /author Zachary Drew, Copyright 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "filter_motion_est.h"
+#include "arrow_code.h"
+
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+static void paint_arrows( uint8_t *image, struct motion_vector_s *vectors, int w, int h, int mb_w, int mb_h )
+{
+       int i, j, x, y;
+       struct motion_vector_s *p;
+       for( i = 0; i < w/mb_w; i++ ){
+               for( j = 0; j < h/mb_h; j++ ){
+                       x = i*mb_w;
+                       y = j*mb_h;
+                       p = vectors + (w/mb_w)*j + i;
+
+                       if ( p->valid == 1 ) {
+                               //draw_rectangle_outline(image, x-1, y-1, mb_w+1, mb_h+1,100);
+                               //x += mb_w/4;
+                               //y += mb_h/4;
+                               //draw_rectangle_outline(image, x + p->dx, y + p->dy, mb_w, mb_h,100);
+                               x += mb_w/2;
+                               y += mb_h/2;
+                               draw_arrow(image, x, y, x + p->dx, y + p->dy, 100);
+                               //draw_rectangle_fill(image, x + p->dx, y + p->dy, mb_w, mb_h, 100);
+                       }
+                       else if ( p->valid == 2 ) {
+                               draw_rectangle_outline(image, x+1, y+1, mb_w-2, mb_h-2,100);
+                       }
+                       else if ( p->valid == 3 ) {
+                               draw_rectangle_fill(image, x-p->dx, y-p->dy, mb_w, mb_h,0);
+                       }
+                       else if ( p->valid == 4 ) {
+                               draw_line(image, x, y, x + 4, y, 100);
+                               draw_line(image, x, y, x, y + 4, 100);
+                               draw_line(image, x + 4, y, x, y + 4, 100);
+
+                               draw_line(image, x+mb_w-1, y+mb_h-1, x+mb_w-5, y+mb_h-1, 100);
+                               draw_line(image, x+mb_w-1, y+mb_h-1, x+mb_w-1, y+mb_h-5, 100);
+                               draw_line(image, x+mb_w-5, y+mb_h-1, x+mb_w-1, y+mb_h-5, 100);
+                       }
+               }
+       }
+}
+
+// Image stack(able) method
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the frame properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES(frame);
+
+       // Get the new image
+       int error = mlt_frame_get_image( frame, image, format, width, height, 1 );
+
+       if( error != 0 )
+               mlt_properties_debug( MLT_FRAME_PROPERTIES(frame), "error after mlt_frame_get_image()", stderr );
+
+
+       // Get the size of macroblocks in pixel units
+       int macroblock_height = mlt_properties_get_int( properties, "motion_est.macroblock_height" );
+       int macroblock_width = mlt_properties_get_int( properties, "motion_est.macroblock_width" );
+
+       // Get the motion vectors
+       struct motion_vector_s *current_vectors = mlt_properties_get_data( properties, "motion_est.vectors", NULL );
+
+       init_arrows( format, *width, *height );
+
+       if ( mlt_properties_get_int( properties, "shot_change" ) == 1 )
+       {
+               draw_line(*image, 0, 0, *width, *height, 100);
+               draw_line(*image, 0, *height, *width, 0, 100);
+       }
+       if( current_vectors != NULL ) {
+               paint_arrows( *image, current_vectors, *width, *height, macroblock_width, macroblock_height);
+       }
+
+       return error;
+}
+
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the frame filter
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+
+mlt_filter filter_vismv_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+
+       }
+
+       return this;
+}
+
+/** This source code will self destruct in 5...4...3...
+*/
+
+
+
+
+
+
+
+
diff --git a/src/modules/motion_est/gpl b/src/modules/motion_est/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/motion_est/producer_slowmotion.c b/src/modules/motion_est/producer_slowmotion.c
new file mode 100644 (file)
index 0000000..e66db09
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * producer_slowmotion.c -- create subspeed frames
+ * Author: Zachary Drew
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "filter_motion_est.h"
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+#define SHIFT 8
+#define ABS(a) ((a) >= 0 ? (a) : (-(a)))
+
+// This is used to constrains pixel operations between two blocks to be within the image boundry
+inline static int constrain(   int *x, int *y, int *w, int *h,                 
+                               const int dx, const int dy,
+                               const int left, const int right,
+                               const int top, const int bottom)
+{
+       uint32_t penalty = 1 << SHIFT;                  // Retain a few extra bits of precision
+       int x2 = *x + dx;
+       int y2 = *y + dy;
+       int w_remains = *w;
+       int h_remains = *h;
+
+       // Origin of macroblock moves left of image boundy
+       if( *x < left || x2 < left ) {
+               w_remains = *w - left + ((*x < x2) ?  *x : x2);
+               *x += *w - w_remains;
+       }
+       // Portion of macroblock moves right of image boundry
+       else if( *x + *w > right || x2 + *w > right )
+               w_remains = right - ((*x > x2) ? *x : x2);
+
+       // Origin of macroblock moves above image boundy
+       if( *y < top || y2 < top ) {
+               h_remains = *h - top + ((*y < y2) ? *y : y2);
+               *y += *h - h_remains;
+       }
+       // Portion of macroblock moves bellow image boundry
+       else if( *y + *h > bottom || y2 + *h > bottom )
+               h_remains = bottom - ((*y > y2) ?  *y : y2);
+
+       if( w_remains == *w && h_remains == *h ) return penalty; 
+       if( w_remains <= 0 || h_remains <= 0) return 0; // Block is clipped out of existance
+       penalty = (*w * *h * penalty)
+               / ( w_remains * h_remains);             // Recipricol of the fraction of the block that remains
+
+       *w = w_remains;                                 // Update the width and height
+       *h = h_remains;
+
+       return penalty;
+}
+
+static void motion_interpolate( uint8_t *first_image, uint8_t *second_image, uint8_t *output,
+                               int top_mb, int bottom_mb, int left_mb, int right_mb,
+                               int mb_w, int mb_h,
+                               int width, int height,
+                               int xstride, int ystride,
+                               double scale,
+                               motion_vector *vectors )
+{
+       assert ( scale >= 0.0 && scale <= 1.0 ); 
+
+       int i, j;
+       int x,y,w,h;
+       int dx, dy;
+       int scaled_dx, scaled_dy;
+       int tx,ty;
+       uint8_t *f,*s,*r;
+       motion_vector *here;
+       int mv_width = width / mb_w;
+
+       for( j = top_mb; j <= bottom_mb; j++ ){  
+        for( i = left_mb; i <= right_mb; i++ ){
+
+               here = vectors + j*mv_width + i; 
+               scaled_dx = (1.0 - scale) * (double)here->dx;
+               scaled_dy = (1.0 - scale) * (double)here->dy;
+               dx = here->dx;
+               dy = here->dy;
+               w = mb_w; h = mb_h;
+               x = i * w; y = j * h;
+
+               // Denoise function caused some blocks to be completely clipped, ignore them    
+               if (constrain( &x, &y, &w, &h, dx, dy, 0, width, 0, height) == 0 )
+                       continue;
+
+               for( ty = y; ty < y + h ; ty++ ){
+                for( tx = x; tx < x + w ; tx++ ){
+
+                       f = first_image  + (tx + dx     )*xstride + (ty + dy     )*ystride;
+                       s = second_image + (tx          )*xstride + (ty    )*ystride;
+                       r = output + (tx+scaled_dx)*xstride + (ty+scaled_dy)*ystride;
+/*
+                       if( ABS(f[0] - s[0]) > 3 * here->msad / (mb_w * mb_h * 2) )
+                       {
+                               r[0] = f[0];
+                               r[1] = f[1];
+                       }
+
+                       else
+                       {
+
+*/
+                               r[0] = ( 1.0 - scale ) * (double)f[0] + scale * (double)s[0];
+
+                               if( dx % 2 == 0 )
+                               {
+                                       if( scaled_dx % 2 == 0 )
+                                               r[1] =  ( 1.0 - scale ) * (double)f[1] + scale * (double) s[1];
+                                       else
+                                               *(r-1) =  ( 1.0 - scale ) * (double)f[1] + scale * (double) s[1];
+                               }
+                               else
+                               {
+                                       if( scaled_dx %2 == 0 )
+                                               // FIXME: may exceed boundies
+                                               r[1] =  ( 1.0 - scale ) * ( (double)(*(f-1) + (double)f[3]) / 2.0 ) + scale * (double) s[1];
+                                       else
+                                               // FIXME: may exceed boundies
+                                               *(r-1) =  ( 1.0 - scale ) * ( (double)(*(f-1) + (double)f[3]) / 2.0 ) + scale * (double) s[1];
+                               }
+//                      }
+                }
+               }
+        }
+       }
+}
+
+// Image stack(able) method
+static int slowmotion_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       // Get the filter object and properties
+       mlt_producer producer = mlt_frame_pop_service( this );
+       mlt_frame second_frame = mlt_frame_pop_service( this );
+       mlt_frame first_frame = mlt_frame_pop_service( this );
+
+       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+       // Frame properties objects
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+       mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES( first_frame );
+       mlt_properties second_frame_properties = MLT_FRAME_PROPERTIES( second_frame );
+
+       // image stride
+       int size, xstride, ystride;
+       switch( *format ){
+               case mlt_image_yuv422:
+                       size = *width * *height * 2;
+                       xstride = 2;
+                       ystride = 2 * *width;
+                       break;
+               default:
+                       fprintf(stderr, "Unsupported image format\n");
+                       return -1;
+       }
+
+       uint8_t *output = mlt_properties_get_data( producer_properties, "output_buffer", 0 );
+       if( output == NULL )
+       {
+               output = mlt_pool_alloc( size );
+
+               // Let someone else clean up
+               mlt_properties_set_data( producer_properties, "output_buffer", output, size, mlt_pool_release, NULL ); 
+       }
+
+       uint8_t *first_image = mlt_properties_get_data( first_frame_properties, "image", NULL );
+       uint8_t *second_image = mlt_properties_get_data( second_frame_properties, "image", NULL );
+
+       // which frames are buffered?
+
+       int error = 0;
+
+       if( first_image == NULL )
+       {
+               error = mlt_frame_get_image( first_frame, &first_image, format, width, height, writable );
+
+               if( error != 0 ) {
+                       fprintf(stderr, "first_image == NULL get image died\n");
+                       return error;
+               }
+       }
+
+       if( second_image == NULL )
+       {
+               error = mlt_frame_get_image( second_frame, &second_image, format, width, height, writable );
+
+               if( error != 0 ) {
+                       fprintf(stderr, "second_image == NULL get image died\n");
+                       return error;
+               }
+       }
+
+       // These need to passed onto the frame for other
+       mlt_properties_pass_list( frame_properties, second_frame_properties,
+                       "motion_est.left_mb, motion_est.right_mb, \
+                       motion_est.top_mb, motion_est.bottom_mb, \
+                       motion_est.macroblock_width, motion_est.macroblock_height" );
+
+       // Pass the pointer to the vectors without serializing
+       mlt_properties_set_data( frame_properties, "motion_est.vectors", 
+                               mlt_properties_get_data( second_frame_properties, "motion_est.vectors", NULL ), 
+                               0, NULL, NULL );
+
+
+       // Start with a base image
+       memcpy( output, first_image, size );
+
+       if( mlt_properties_get_int( producer_properties, "method" ) == 1 ) {
+
+               mlt_position first_position = mlt_frame_get_position( first_frame );
+               double actual_position = mlt_producer_get_speed( producer ) * (double)mlt_frame_get_position( this );
+               double scale = actual_position - first_position;
+
+               motion_interpolate
+               (
+                       first_image, second_image, output,
+                       mlt_properties_get_int( second_frame_properties, "motion_est.top_mb" ),
+                       mlt_properties_get_int( second_frame_properties, "motion_est.bottom_mb" ),
+                       mlt_properties_get_int( second_frame_properties, "motion_est.left_mb" ),
+                       mlt_properties_get_int( second_frame_properties, "motion_est.right_mb" ),
+                       mlt_properties_get_int( second_frame_properties, "motion_est.macroblock_width" ),
+                       mlt_properties_get_int( second_frame_properties, "motion_est.macroblock_height" ),
+                       *width, *height,
+                       xstride, ystride,       
+                       scale,
+                       mlt_properties_get_data( second_frame_properties, "motion_est.vectors", NULL )
+               );
+
+               if( mlt_properties_get_int( producer_properties, "debug" ) == 1 ) {
+                       mlt_filter watermark = mlt_properties_get_data( producer_properties, "watermark", NULL );
+
+                       if( watermark == NULL ) {
+                               mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
+                               watermark = mlt_factory_filter( profile, "watermark", NULL );
+                               mlt_properties_set_data( producer_properties, "watermark", watermark, 0, (mlt_destructor)mlt_filter_close, NULL );
+                               mlt_producer_attach( producer, watermark );
+                       }
+
+                       mlt_properties wm_properties = MLT_FILTER_PROPERTIES( watermark );
+
+                       char disp[30];
+                       sprintf(disp, "+%10.2f.txt", actual_position);
+                       mlt_properties_set( wm_properties, "resource", disp );
+
+               }
+
+       }
+
+       *image = output;
+       mlt_properties_set_data( frame_properties, "image", output, size, NULL, NULL );
+
+       // Make sure that no further scaling is done
+       mlt_properties_set( frame_properties, "rescale.interps", "none" );
+       mlt_properties_set( frame_properties, "scale", "off" );
+
+       mlt_frame_close( first_frame );
+       mlt_frame_close( second_frame );
+
+       return 0;
+}
+
+static int slowmotion_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Construct a new frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES(this);
+
+
+       if( frame != NULL )
+       {
+
+               mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL );
+               mlt_frame second_frame = mlt_properties_get_data( properties, "second_frame", NULL );
+
+               mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position( first_frame ) : -1;
+               mlt_position second_position = (second_frame != NULL) ? mlt_frame_get_position( second_frame ) : -1;
+
+               // Get the real producer
+               mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL );
+
+               // Our "in" needs to be the same, keep it so
+               mlt_properties_pass_list( MLT_PRODUCER_PROPERTIES( real_producer ), properties, "in" );
+
+               // Calculate our positions
+               double actual_position = mlt_producer_get_speed( this ) * (double)mlt_producer_position( this );
+               mlt_position need_first = floor( actual_position );
+               mlt_position need_second = need_first + 1;
+
+               if( need_first != first_position )
+               {
+                       mlt_frame_close( first_frame );
+                       first_position = -1;
+                       first_frame = NULL;
+               }
+
+               if( need_second != second_position)
+               {
+                       mlt_frame_close( second_frame );
+                       second_position = -1;
+                       second_frame = NULL;
+               }
+
+               if( first_frame == NULL )
+               {
+                       // Seek the producer to the correct place
+                       mlt_producer_seek( real_producer, need_first );
+
+                       // Get the frame
+                       mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index );
+               }
+
+               if( second_frame == NULL )
+               {
+                       // Seek the producer to the correct place
+                       mlt_producer_seek( real_producer, need_second );
+
+                       // Get the frame
+                       mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &second_frame, index );
+               }
+
+               // Make sure things are in their place
+               mlt_properties_set_data( properties, "first_frame", first_frame, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "second_frame", second_frame, 0, NULL, NULL );
+
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 0 );
+
+               // Stack the producer and producer's get image
+               mlt_frame_push_service( *frame, first_frame );
+               mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( first_frame ) );
+
+               mlt_frame_push_service( *frame, second_frame );
+               mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( second_frame ) );
+
+               mlt_frame_push_service( *frame, this );
+               mlt_frame_push_service( *frame, slowmotion_get_image );
+
+               // Give the returned frame temporal identity
+               mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+       }
+
+       return 0;
+}
+
+mlt_producer producer_slowmotion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_producer this = mlt_producer_new( );
+
+       // Wrap fezzik
+       mlt_producer real_producer = mlt_factory_producer( profile, "fezzik", arg );
+
+       // We need to apply the motion estimation filter manually
+       mlt_filter filter = mlt_factory_filter( profile, "motion_est", NULL );
+
+       if ( this != NULL && real_producer != NULL && filter != NULL)
+       {
+               // attach the motion_est filter to the real producer
+               mlt_producer_attach( real_producer, filter );
+
+               // Get the properties of this producer
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+               // Fezzik normalised it for us already
+               mlt_properties_set_int( properties, "fezzik_normalised", 1);
+
+               // Store the producer and fitler
+               mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+               mlt_properties_set_data( properties, "motion_est", filter, 0, ( mlt_destructor )mlt_filter_close, NULL );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "macroblock_width", 16 );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "macroblock_height", 16 );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "denoise", 0 );
+
+               // Grap some stuff from the real_producer
+               mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ),
+                               "in, out, length, resource" );
+
+               // Since we control the seeking, prevent it from seeking on its own
+               mlt_producer_set_speed( real_producer, 0 );
+
+               //mlt_properties_set( properties, "method", "onefield" );
+
+               // Override the get_frame method
+               this->get_frame = slowmotion_get_frame;
+
+       }
+       else
+       {
+               if ( this )
+                       mlt_producer_close( this );
+               if ( real_producer )
+                       mlt_producer_close( real_producer );
+               if ( filter )
+                       mlt_filter_close( filter );
+
+               this = NULL;
+       }
+       return this;
+}
diff --git a/src/modules/motion_est/sad_sse.h b/src/modules/motion_est/sad_sse.h
new file mode 100644 (file)
index 0000000..b14a5f6
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+
+#define SAD_SSE_INIT \
+       asm volatile ( "pxor %%mm6,%%mm6\n\t" ::  );\
+
+// Sum two 8x1 pixel blocks
+#define SAD_SSE_SUM_8(OFFSET) \
+                       "movq " #OFFSET "(%0),%%mm0             \n\t"\
+                       "movq " #OFFSET "(%1),%%mm1             \n\t"\
+                       "psadbw %%mm1,%%mm0                     \n\t"\
+                       "paddw %%mm0,%%mm6                      \n\t"\
+
+#define SAD_SSE_FINISH(RESULT) \
+       asm volatile( "movd %%mm6,%0" : "=r" (RESULT) : );
+
+// Advance by ystride
+#define SAD_SSE_NEXTROW \
+                       "add %2,%0                              \n\t"\
+                       "add %2,%1                              \n\t"\
+
+// BROKEN!
+inline static int sad_sse_4x4( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_INIT
+       #define ROW     SAD_SSE_SUM_8(0) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+inline static int sad_sse_8x8( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_INIT
+       #define ROW     SAD_SSE_SUM_8(0) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+inline static int sad_sse_16x16( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_INIT
+       #define ROW     SAD_SSE_SUM_8(0) SAD_SSE_SUM_8(8) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+inline static int sad_sse_32x32( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_INIT
+       #define ROW     SAD_SSE_SUM_8(0) SAD_SSE_SUM_8(8) SAD_SSE_SUM_8(16) SAD_SSE_SUM_8(24)\
+                       SAD_SSE_NEXTROW
+
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+// BROKEN!
+inline static int sad_sse_4w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_SUM_8(0)
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+inline static int sad_sse_8w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_SUM_8(0)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+inline static int sad_sse_16w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_SUM_8(0)
+                       SAD_SSE_SUM_8(8)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+inline static int sad_sse_32w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_SUM_8(0)
+                       SAD_SSE_SUM_8(8)
+                       SAD_SSE_SUM_8(16)
+                       SAD_SSE_SUM_8(24)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+inline static int sad_sse_64w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_SUM_8(0)
+                       SAD_SSE_SUM_8(8)
+                       SAD_SSE_SUM_8(16)
+                       SAD_SSE_SUM_8(24)
+                       SAD_SSE_SUM_8(32)
+                       SAD_SSE_SUM_8(40)
+                       SAD_SSE_SUM_8(48)
+                       SAD_SSE_SUM_8(56)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+static __attribute__((used)) __attribute__((aligned(8))) uint64_t sad_sse_422_mask_chroma = 0x00ff00ff00ff00ffULL;
+
+#define SAD_SSE_422_LUMA_INIT \
+       asm volatile (  "movq %0,%%mm7\n\t"\
+                       "pxor %%mm6,%%mm6\n\t" :: "m" (sad_sse_422_mask_chroma) );\
+
+// Sum two 4x1 pixel blocks
+#define SAD_SSE_422_LUMA_SUM_4(OFFSET) \
+                       "movq " #OFFSET "(%0),%%mm0             \n\t"\
+                       "movq " #OFFSET "(%1),%%mm1             \n\t"\
+                       "pand %%mm7,%%mm0                       \n\t"\
+                       "pand %%mm7,%%mm1                       \n\t"\
+                       "psadbw %%mm1,%%mm0                     \n\t"\
+                       "paddw %%mm0,%%mm6                      \n\t"\
+
+static int sad_sse_422_luma_4x4( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_422_LUMA_INIT
+       #define ROW     SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+static int sad_sse_422_luma_8x8( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_422_LUMA_INIT
+       #define ROW     SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+static int sad_sse_422_luma_16x16( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_422_LUMA_INIT
+       #define ROW     SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_422_LUMA_SUM_4(16) SAD_SSE_422_LUMA_SUM_4(24) SAD_SSE_NEXTROW
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+static int sad_sse_422_luma_32x32( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+       SAD_SSE_422_LUMA_INIT
+       #define ROW     SAD_SSE_422_LUMA_SUM_4(0) SAD_SSE_422_LUMA_SUM_4(8) SAD_SSE_422_LUMA_SUM_4(16) SAD_SSE_422_LUMA_SUM_4(24)\
+                       SAD_SSE_422_LUMA_SUM_4(32) SAD_SSE_422_LUMA_SUM_4(40) SAD_SSE_422_LUMA_SUM_4(48) SAD_SSE_422_LUMA_SUM_4(56)\
+                       SAD_SSE_NEXTROW
+
+       asm volatile (  ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       ROW ROW ROW ROW ROW ROW ROW ROW
+                       :: "r" (block1), "r" (block2), "r" ((long int)(ystride)));
+       
+       SAD_SSE_FINISH(result)
+       return result;
+       #undef ROW
+
+}
+
+static int sad_sse_422_luma_4w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_422_LUMA_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_422_LUMA_SUM_4(0)
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+static int sad_sse_422_luma_8w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_422_LUMA_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_422_LUMA_SUM_4(0)
+                       SAD_SSE_422_LUMA_SUM_4(8)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+static int sad_sse_422_luma_16w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_422_LUMA_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_422_LUMA_SUM_4(0)
+                       SAD_SSE_422_LUMA_SUM_4(8)
+                       SAD_SSE_422_LUMA_SUM_4(16)
+                       SAD_SSE_422_LUMA_SUM_4(24)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+static int sad_sse_422_luma_32w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_422_LUMA_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_422_LUMA_SUM_4(0)
+                       SAD_SSE_422_LUMA_SUM_4(8)
+                       SAD_SSE_422_LUMA_SUM_4(16)
+                       SAD_SSE_422_LUMA_SUM_4(24)
+                       SAD_SSE_422_LUMA_SUM_4(32)
+                       SAD_SSE_422_LUMA_SUM_4(40)
+                       SAD_SSE_422_LUMA_SUM_4(48)
+                       SAD_SSE_422_LUMA_SUM_4(56)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+
+}
+
+static int sad_sse_422_luma_64w( uint8_t *block1, uint8_t *block2, int xstride, int ystride, int w, int h )
+{
+       int result; 
+
+       SAD_SSE_422_LUMA_INIT
+
+       while( h != 0 ) {
+               asm volatile (
+                       SAD_SSE_422_LUMA_SUM_4(0)
+                       SAD_SSE_422_LUMA_SUM_4(8)
+                       SAD_SSE_422_LUMA_SUM_4(16)
+                       SAD_SSE_422_LUMA_SUM_4(24)
+                       SAD_SSE_422_LUMA_SUM_4(32)
+                       SAD_SSE_422_LUMA_SUM_4(40)
+                       SAD_SSE_422_LUMA_SUM_4(48)
+                       SAD_SSE_422_LUMA_SUM_4(56)
+                       SAD_SSE_422_LUMA_SUM_4(64)
+                       SAD_SSE_422_LUMA_SUM_4(72)
+                       SAD_SSE_422_LUMA_SUM_4(80)
+                       SAD_SSE_422_LUMA_SUM_4(88)
+                       SAD_SSE_422_LUMA_SUM_4(96)
+                       SAD_SSE_422_LUMA_SUM_4(104)
+                       SAD_SSE_422_LUMA_SUM_4(112)
+                       SAD_SSE_422_LUMA_SUM_4(120)
+
+                       :: "r" (block1), "r" (block2)
+               );
+       
+               h--;
+               block1 += ystride;
+               block2 += ystride;
+       }
+       SAD_SSE_FINISH(result)
+       return result;
+}
diff --git a/src/modules/normalize/Makefile b/src/modules/normalize/Makefile
new file mode 100644 (file)
index 0000000..096e2e3
--- /dev/null
@@ -0,0 +1,33 @@
+include ../../../config.mak
+
+TARGET = ../libmltnormalize$(LIBSUF)
+
+OBJS = factory.o \
+          filter_volume.o 
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/normalize/factory.c b/src/modules/normalize/factory.c
new file mode 100644 (file)
index 0000000..3736d7d
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "volume", filter_volume_init );
+}
diff --git a/src/modules/normalize/filter_volume.c b/src/modules/normalize/filter_volume.c
new file mode 100644 (file)
index 0000000..3bc747c
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * filter_volume.c -- adjust audio volume
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+
+#define MAX_CHANNELS 6
+#define EPSILON 0.00001
+
+/* The following normalise functions come from the normalize utility:
+   Copyright (C) 1999--2002 Chris Vaill */
+
+#define samp_width 16
+
+#ifndef ROUND
+# define ROUND(x) floor((x) + 0.5)
+#endif
+
+#define DBFSTOAMP(x) pow(10,(x)/20.0)
+
+/** Return nonzero if the two strings are equal, ignoring case, up to
+    the first n characters.
+*/
+int strncaseeq(const char *s1, const char *s2, size_t n)
+{
+       for ( ; n > 0; n--)
+       {
+               if (tolower(*s1++) != tolower(*s2++))
+                       return 0;
+       }
+       return 1;
+}
+
+/** Limiter function.
+         / tanh((x + lev) / (1-lev)) * (1-lev) - lev        (for x < -lev)
+         |
+    x' = | x                                                (for |x| <= lev)
+         |
+         \ tanh((x - lev) / (1-lev)) * (1-lev) + lev        (for x > lev)
+  With limiter level = 0, this is equivalent to a tanh() function;
+  with limiter level = 1, this is equivalent to clipping.
+*/
+static inline double limiter( double x, double lmtr_lvl )
+{
+       double xp = x;
+
+       if (x < -lmtr_lvl)
+               xp = tanh((x + lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) - lmtr_lvl;
+       else if (x > lmtr_lvl)
+               xp = tanh((x - lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) + lmtr_lvl;
+
+//     if ( x != xp )
+//             fprintf( stderr, "filter_volume: sample %f limited %f\n", x, xp );
+
+       return xp;
+}
+
+
+/** Takes a full smoothing window, and returns the value of the center
+    element, smoothed.
+
+    Currently, just does a mean filter, but we could do a median or
+    gaussian filter here instead.
+*/
+static inline double get_smoothed_data( double *buf, int count )
+{
+       int i, j;
+       double smoothed = 0;
+
+       for ( i = 0, j = 0; i < count; i++ )
+       {
+               if ( buf[ i ] != -1.0 )
+               {
+                       smoothed += buf[ i ];
+                       j++;
+               }
+       }
+       smoothed /= j;
+//     fprintf( stderr, "smoothed over %d values, result %f\n", j, smoothed );
+
+       return smoothed;
+}
+
+/** Get the max power level (using RMS) and peak level of the audio segment.
+ */
+double signal_max_power( int16_t *buffer, int channels, int samples, int16_t *peak )
+{
+       // Determine numeric limits
+       int bytes_per_samp = (samp_width - 1) / 8 + 1;
+       int16_t max = (1 << (bytes_per_samp * 8 - 1)) - 1;
+       int16_t min = -max - 1;
+       
+       double *sums = (double *) calloc( channels, sizeof(double) );
+       int c, i;
+       int16_t sample;
+       double pow, maxpow = 0;
+
+       /* initialize peaks to effectively -inf and +inf */
+       int16_t max_sample = min;
+       int16_t min_sample = max;
+  
+       for ( i = 0; i < samples; i++ )
+       {
+               for ( c = 0; c < channels; c++ )
+               {
+                       sample = *buffer++;
+                       sums[ c ] += (double) sample * (double) sample;
+                       
+                       /* track peak */
+                       if ( sample > max_sample )
+                               max_sample = sample;
+                       else if ( sample < min_sample )
+                               min_sample = sample;
+               }
+       }
+       for ( c = 0; c < channels; c++ )
+       {
+               pow = sums[ c ] / (double) samples;
+               if ( pow > maxpow )
+                       maxpow = pow;
+       }
+                       
+       free( sums );
+       
+       /* scale the pow value to be in the range 0.0 -- 1.0 */
+       maxpow /= ( (double) min * (double) min);
+
+       if ( -min_sample > max_sample )
+               *peak = min_sample / (double) min;
+       else
+               *peak = max_sample / (double) max;
+
+       return sqrt( maxpow );
+}
+
+/* ------ End normalize functions --------------------------------------- */
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the a frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+       double gain = mlt_properties_get_double( properties, "volume.gain" );
+       double max_gain = mlt_properties_get_double( properties, "volume.max_gain" );
+       double limiter_level = 0.5; /* -6 dBFS */
+       int normalise =  mlt_properties_get_int( properties, "volume.normalise" );
+       double amplitude =  mlt_properties_get_double( properties, "volume.amplitude" );
+       int i, j;
+       double sample;
+       int16_t peak;
+
+       // Get the filter from the frame
+       mlt_filter this = mlt_properties_get_data( properties, "filter_volume", NULL );
+
+       // Get the properties from the filter
+       mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+
+       if ( mlt_properties_get( properties, "volume.limiter" ) != NULL )
+               limiter_level = mlt_properties_get_double( properties, "volume.limiter" );
+       
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+//     fprintf( stderr, "filter_volume: frequency %d\n", *frequency );
+
+       // Determine numeric limits
+       int bytes_per_samp = (samp_width - 1) / 8 + 1;
+       int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1;
+       int samplemin = -samplemax - 1;
+
+       if ( normalise )
+       {
+               int window = mlt_properties_get_int( filter_props, "window" );
+               double *smooth_buffer = mlt_properties_get_data( filter_props, "smooth_buffer", NULL );
+
+               if ( window > 0 && smooth_buffer != NULL )
+               {
+                       int smooth_index = mlt_properties_get_int( filter_props, "_smooth_index" );
+                       
+                       // Compute the signal power and put into smoothing buffer
+                       smooth_buffer[ smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak );
+//                     fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ smooth_index ] );
+                       if ( smooth_buffer[ smooth_index ] > EPSILON )
+                       {
+                               mlt_properties_set_int( filter_props, "_smooth_index", ( smooth_index + 1 ) % window );
+
+                               // Smooth the data and compute the gain
+//                             fprintf( stderr, "smoothed %f over %d frames\n", get_smoothed_data( smooth_buffer, window ), window );
+                               gain *= amplitude / get_smoothed_data( smooth_buffer, window );
+                       }
+               }
+               else
+               {
+                       gain *= amplitude / signal_max_power( *buffer, *channels, *samples, &peak );
+               }
+       }
+       
+//     if ( gain > 1.0 && normalise )
+//             fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain );
+
+       if ( max_gain > 0 && gain > max_gain )
+               gain = max_gain;
+
+       // Initialise filter's previous gain value to prevent an inadvertant jump from 0
+       mlt_position last_position = mlt_properties_get_position( filter_props, "_last_position" );
+       mlt_position current_position = mlt_frame_get_position( frame );
+       if ( mlt_properties_get( filter_props, "_previous_gain" ) == NULL
+            || current_position != last_position + 1 )
+               mlt_properties_set_double( filter_props, "_previous_gain", gain );
+
+       // Start the gain out at the previous
+       double previous_gain = mlt_properties_get_double( filter_props, "_previous_gain" );
+
+       // Determine ramp increment
+       double gain_step = ( gain - previous_gain ) / *samples;
+//     fprintf( stderr, "filter_volume: previous gain %f current gain %f step %f\n", previous_gain, gain, gain_step );
+
+       // Save the current gain for the next iteration
+       mlt_properties_set_double( filter_props, "_previous_gain", gain );
+       mlt_properties_set_position( filter_props, "_last_position", current_position );
+
+       // Ramp from the previous gain to the current
+       gain = previous_gain;
+
+       int16_t *p = *buffer;
+
+       // Apply the gain
+       for ( i = 0; i < *samples; i++ )
+       {
+               for ( j = 0; j < *channels; j++ )
+               {
+                       sample = *p * gain;
+                       *p = ROUND( sample );
+               
+                       if ( gain > 1.0 )
+                       {
+                               /* use limiter function instead of clipping */
+                               if ( normalise )
+                                       *p = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) );
+                               
+                               /* perform clipping */
+                               else if ( sample > samplemax )
+                                       *p = samplemax;
+                               else if ( sample < samplemin )
+                                       *p = samplemin;
+                       }
+                       p++;
+               }
+               gain += gain_step;
+       }
+       
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+       mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
+
+       // Parse the gain property
+       if ( mlt_properties_get( properties, "gain" ) == NULL )
+       {
+               double gain = 1.0; // no adjustment
+               
+               if ( mlt_properties_get( filter_props, "gain" ) != NULL )
+               {
+                       char *p = mlt_properties_get( filter_props, "gain" );
+                       
+                       if ( strncaseeq( p, "normalise", 9 ) )
+                               mlt_properties_set( filter_props, "normalise", "" );
+                       else
+                       {
+                               if ( strcmp( p, "" ) != 0 )
+                                       gain = fabs( strtod( p, &p) );
+
+                               while ( isspace( *p ) )
+                                       p++;
+
+                               /* check if "dB" is given after number */
+                               if ( strncaseeq( p, "db", 2 ) )
+                                       gain = DBFSTOAMP( gain );
+                                       
+                               // If there is an end adjust gain to the range
+                               if ( mlt_properties_get( filter_props, "end" ) != NULL )
+                               {
+                                       // Determine the time position of this frame in the transition duration
+                                       mlt_position in = mlt_filter_get_in( this );
+                                       mlt_position out = mlt_filter_get_out( this );
+                                       mlt_position time = mlt_frame_get_position( frame );
+                                       double position = ( double )( time - in ) / ( double )( out - in + 1 );
+
+                                       double end = -1;
+                                       char *p = mlt_properties_get( filter_props, "end" );
+                                       if ( strcmp( p, "" ) != 0 )
+                                               end = fabs( strtod( p, &p) );
+
+                                       while ( isspace( *p ) )
+                                               p++;
+
+                                       /* check if "dB" is given after number */
+                                       if ( strncaseeq( p, "db", 2 ) )
+                                               end = DBFSTOAMP( gain );
+
+                                       if ( end != -1 )
+                                               gain += ( end - gain ) * position;
+                               }
+                       }
+               }
+               mlt_properties_set_double( properties, "volume.gain", gain );
+       }
+       
+       // Parse the maximum gain property
+       if ( mlt_properties_get( filter_props, "max_gain" ) != NULL )
+       {
+               char *p = mlt_properties_get( filter_props, "max_gain" );
+               double gain = fabs( strtod( p, &p) ); // 0 = no max
+                       
+               while ( isspace( *p ) )
+                       p++;
+
+               /* check if "dB" is given after number */
+               if ( strncaseeq( p, "db", 2 ) )
+                       gain = DBFSTOAMP( gain );
+                       
+               mlt_properties_set_double( properties, "volume.max_gain", gain );
+       }
+
+       // Parse the limiter property
+       if ( mlt_properties_get( filter_props, "limiter" ) != NULL )
+       {
+               char *p = mlt_properties_get( filter_props, "limiter" );
+               double level = 0.5; /* -6dBFS */ 
+               if ( strcmp( p, "" ) != 0 )
+                       level = strtod( p, &p);
+               
+               while ( isspace( *p ) )
+                       p++;
+               
+               /* check if "dB" is given after number */
+               if ( strncaseeq( p, "db", 2 ) )
+               {
+                       if ( level > 0 )
+                               level = -level;
+                       level = DBFSTOAMP( level );
+               }
+               else
+               {
+                       if ( level < 0 )
+                               level = -level;
+               }
+               mlt_properties_set_double( properties, "volume.limiter", level );
+       }
+
+       // Parse the normalise property
+       if ( mlt_properties_get( filter_props, "normalise" ) != NULL )
+       {
+               char *p = mlt_properties_get( filter_props, "normalise" );
+               double amplitude = 0.2511886431509580; /* -12dBFS */
+               if ( strcmp( p, "" ) != 0 )
+                       amplitude = strtod( p, &p);
+
+               while ( isspace( *p ) )
+                       p++;
+
+               /* check if "dB" is given after number */
+               if ( strncaseeq( p, "db", 2 ) )
+               {
+                       if ( amplitude > 0 )
+                               amplitude = -amplitude;
+                       amplitude = DBFSTOAMP( amplitude );
+               }
+               else
+               {
+                       if ( amplitude < 0 )
+                               amplitude = -amplitude;
+                       if ( amplitude > 1.0 )
+                               amplitude = 1.0;
+               }
+               
+               // If there is an end adjust gain to the range
+               if ( mlt_properties_get( filter_props, "end" ) != NULL )
+               {
+                       // Determine the time position of this frame in the transition duration
+                       mlt_position in = mlt_filter_get_in( this );
+                       mlt_position out = mlt_filter_get_out( this );
+                       mlt_position time = mlt_frame_get_position( frame );
+                       double position = ( double )( time - in ) / ( double )( out - in + 1 );
+                       amplitude *= position;
+               }
+               mlt_properties_set_int( properties, "volume.normalise", 1 );
+               mlt_properties_set_double( properties, "volume.amplitude", amplitude );
+       }
+
+       // Parse the window property and allocate smoothing buffer if needed
+       int window = mlt_properties_get_int( filter_props, "window" );
+       if ( mlt_properties_get( filter_props, "smooth_buffer" ) == NULL && window > 1 )
+       {
+               // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
+               double *smooth_buffer = (double*) calloc( window, sizeof( double ) );
+               int i;
+               for ( i = 0; i < window; i++ )
+                       smooth_buffer[ i ] = -1.0;
+               mlt_properties_set_data( filter_props, "smooth_buffer", smooth_buffer, 0, free, NULL );
+       }
+       
+       // Put a filter reference onto the frame
+       mlt_properties_set_data( properties, "filter_volume", this, 0, NULL, NULL );
+
+       // Override the get_audio method
+       mlt_frame_push_audio( frame, filter_get_audio );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
+       if ( this != NULL && mlt_filter_init( this, NULL ) == 0 )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               this->process = filter_process;
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "gain", arg );
+
+               mlt_properties_set_int( properties, "window", 75 );
+               mlt_properties_set( properties, "max_gain", "20dB" );
+       }
+       return this;
+}
diff --git a/src/modules/normalize/gpl b/src/modules/normalize/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/oldfilm/Makefile b/src/modules/oldfilm/Makefile
new file mode 100644 (file)
index 0000000..90c13c3
--- /dev/null
@@ -0,0 +1,43 @@
+include ../../../config.mak
+
+TARGET = ../libmltoldfilm$(LIBSUF)
+
+OBJS = factory.o \
+          filter_oldfilm.o \
+          filter_dust.o \
+          filter_lines.o \
+          filter_grain.o \
+          filter_tcolor.o \
+          filter_vignette.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+       install -d $(DESTDIR)$(prefix)/share/mlt/oldfilm
+       install -m 644 *.svg "$(DESTDIR)$(prefix)/share/mlt/oldfilm"
+       install -m 644 *.yml "$(DESTDIR)$(prefix)/share/mlt/oldfilm"
+
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/oldfilm/dust1.svg b/src/modules/oldfilm/dust1.svg
new file mode 100644 (file)
index 0000000..db59d4d
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="240"
+   height="217.14285"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dust1.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="291.42857"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="436"
+     inkscape:window-y="136" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop3136" />
+      <stop
+         offset="1"
+         style="stop-color:#001b00;stop-opacity:0;"
+         id="stop3138" />
+    </linearGradient>
+    <radialGradient
+       r="120"
+       id="radialGradient3140"
+       fx="314.28571"
+       fy="352.36218"
+       gradientUnits="userSpaceOnUse"
+       inkscape:collect="always"
+       cy="352.36218"
+       cx="314.28571"
+       xlink:href="#linearGradient3134"
+       gradientTransform="matrix(1,0,0,3.824003,0,-899.71827)" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="translate(-194.28571,-243.79076)"
+     inkscape:label="Ebene 1"
+     id="layer1"
+     inkscape:groupmode="layer">
+    <path
+       id="path2171"
+       d="M 434.28571,447.71572 L 430.39845,446.98174 L 426.51105,447.70281 L 422.63086,444.95151 L 418.7369,447.33 L 415.17888,439.03832 L 412.09038,427.88049 L 408.04927,427.373 L 404.01654,426.16139 L 399.94798,424.73438 L 396.42874,433.48045 L 392.63006,438.51018 L 388.67834,436.48311 L 385.04569,429.98727 L 381.14982,427.38011 L 377.22038,430.04453 L 373.32102,426.68233 L 369.57046,421.84943 L 366.19199,413.43864 L 362.28568,414.01743 L 358.41557,411.70186 L 354.37205,414.23864 L 350.36416,417.63832 L 346.46571,411.53673 L 343.35113,399.8993 L 339.53706,393.48698 L 335.97202,384.88788 L 331.83892,385.51799 L 327.91078,390.9871 L 323.71372,391.53811 L 319.89063,384.19765 L 316.99236,371.9895 L 314.28571,359.01937 L 310.03453,356.25903 L 306.24091,347.69311 L 302.06907,343.31261 L 297.78709,344.91141 L 293.67116,336.67107 L 291.59835,319.53116 L 287.10897,318.6664 L 283.4444,307.67153 L 280.04266,294.45327 L 276.07896,284.41415 L 271.53901,279.72473 L 266.90066,282.14486 L 262.2405,285.1173 L 257.57444,282.31501 L 253.08029,290.12947 L 250.29693,306.96613 L 245.94295,304.25049 L 241.56118,305.99325 L 236.95654,305.68247 L 233.22692,317.10051 L 228.78363,307.65672 L 224.00778,313.52323 L 220.67578,327.30269 L 216.67116,337.38838 L 216.28682,356.83475 L 214.77646,375.27519 L 210.68738,385.66713 L 208.55789,403.71361 L 204.2669,410.01158 L 200.22848,418.80047 L 195.90418,428.29105 L 194.28571,447.71572 L 198.62042,451.1118 L 202.83258,456.61128 L 207.52564,457.54492 L 211.21379,445.24351 L 215.01113,434.95466 L 219.46816,432.02441 L 222.58281,418.55997 L 224.71894,402.03506 L 228.28989,393.27594 L 232.3716,390.64936 L 235.43763,377.86937 L 239.72488,376.16203 L 243.34806,366.44008 L 247.61567,368.35802 L 251.57005,364.6111 L 255.55167,361.42094 L 259.82848,362.36269 L 263.77023,355.28606 L 267.94113,354.12226 L 272.11574,355.01674 L 274.69118,340.04655 L 278.35161,329.88559 L 282.69754,328.43278 L 286.73607,321.49359 L 290.72647,316.70065 L 294.42096,308.72595 L 298.44502,301.8398 L 300.7415,286.26813 L 303.33482,272.24893 L 307.18187,265.01712 L 311.16529,259.69604 L 314.28571,247.95603 L 317.70857,259.01241 L 321.90346,263.15776 L 324.16016,278.97807 L 328.02132,287.63463 L 330.16565,304.13419 L 334.23472,311.78945 L 337.48222,325.11445 L 342.00603,325.72722 L 343.08521,344.83013 L 346.64696,357.4439 L 351.04337,360.80678 L 354.97171,369.80216 L 358.55898,378.84144 L 362.09284,388.24908 L 364.20762,404.75745 L 368.33325,411.71191 L 372.37551,402.09016 L 376.70855,395.08488 L 381.38937,392.28894 L 385.94136,397.67996 L 390.80528,391.4668 L 395.50331,399.6484 L 399.72846,389.6019 L 402.99765,374.47207 L 407.33054,381.30715 L 410.66368,394.85799 L 415.33507,395.57009 L 419.35168,405.67599 L 421.4868,423.88654 L 425.40104,435.69199 L 430.12905,437.93294 L 434.28571,447.71572"
+       style="opacity:1;fill:url(#radialGradient3140);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.80000019;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/dust2.svg b/src/modules/oldfilm/dust2.svg
new file mode 100644 (file)
index 0000000..055a1d3
--- /dev/null
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="194.28572"
+   height="257.14285"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dust2.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ff4f64"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.43137255"
+     inkscape:pageshadow="2"
+     inkscape:zoom="5.248863"
+     inkscape:cx="100.71553"
+     inkscape:cy="161.55164"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1043"
+     inkscape:window-height="804"
+     inkscape:window-x="387"
+     inkscape:window-y="16" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3147">
+      <stop
+         offset="0"
+         style="stop-color:#fffdff;stop-opacity:1;"
+         id="stop3149" />
+      <stop
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0.30222222;"
+         id="stop3151" />
+    </linearGradient>
+    <radialGradient
+       r="97.14286"
+       id="radialGradient3155"
+       fx="274.28571"
+       fy="335.21933"
+       gradientUnits="userSpaceOnUse"
+       inkscape:collect="always"
+       cy="335.21933"
+       cx="274.28571"
+       xlink:href="#linearGradient3147"
+       gradientTransform="matrix(1,0,0,1.3235293,0,-108.45329)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3147"
+       id="radialGradient4250"
+       cx="277.85837"
+       cy="302.23911"
+       fx="277.85837"
+       fy="302.23911"
+       r="91.162598"
+       gradientTransform="matrix(1,0,0,0.4733379,0,159.17788)"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="reflect" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="translate(-177.14285,-206.6479)"
+     inkscape:label="Ebene 1"
+     id="layer1"
+     inkscape:groupmode="layer">
+    <path
+       style="opacity:1;stroke-opacity:1;fill-rule:nonzero;fill-opacity:1;stroke:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-width:9.80000019;fill:url(#radialGradient3155)"
+       id="path2261"
+       d="M-463.907729645 59.4947232865L-465.88997919 53.0618899264L-467.549378959 46.5383133996L-469.743557035 39.9685086158L-468.666248439 33.1262751708L-466.83275019 26.2872473927L-464.142489942 19.7377041588L-464.524207184 12.4160838201L-468.615066603 6.33194857812L-468.910847311 -0.683251608856L-470.528779939 -7.51573387404L-475.092658479 -12.8099924273L-479.739067212 -18.0319697201L-485.73817452 -21.945195162L-491.476673252 -26.2314819186L-495.419964008 -32.6422392782L-494.847794523 -40.1469027611L-492.480636045 -47.3546670613L-491.9760398 -54.9243888592L-487.59770564 -61.5755338577L-480.048594523 -64.1090020642L-480.681659877 -71.3075932316L-483.088938826 -78.121218889L-482.704413791 -85.3251250446L-480.732250787 -92.2644835516L-480.193664405 -99.1533349308L-480.563047873 -106.053328008L-481.025575984 -113.263921496L-485.519721132 -118.921593287L-487.829778625 -125.3369764L-488.982048988 -132.057525418L-490.622429463 -138.759025882L-494.477291489 -144.481000656L-490.017834216 -150.068529921L-486.181732441 -156.10107214L-488.547366928 -163.013327043L-487.825680898 -170.283448339L-483.680993685 -176.261759704L-477.054071133 -179.262182402L-473.931082668 -185.714303117L-473.702723282 -192.878852696L-475.469781914 -199.784629318L-477.829700823 -206.510923223L-477.23499722 -214.07026968L-471.904284019 -219.46293952L-465.900025338 -223.606434671L-462.736688564 -230.180092316L-460.270934889 -237.140901407L-462.186942229 -244.272641747L-466.091911117 -251.164436042L-473.420709869 -254.170150512L-476.639538032 -261.370949763L-474.91411937 -269.067396674L-476.902252287 -276.267891846L-473.768056392 -283.048497964L-471.927801573 -289.824732823L-469.462772581 -296.399497362L-473.63808065 -302.194623254L-473.385435919 -309.332749914L-470.992019665 -316.077927348L-473.315226176 -322.847607657L-469.159901772 -328.49003245L-468.226046876 -335.434929388L-466.477069188 -342.175668962L-467.83251643 -349.006427025L-468.619692204 -342.861624953L-469.232433338 -336.696984939L-469.270127678 -330.399469569L-471.539300702 -324.52486537L-469.175868919 -318.623388776L-469.451228748 -312.272214651L-469.494680958 -305.649307059L-465.230740834 -300.581407327L-466.848690529 -294.42110035L-465.396556407 -288.219612646L-464.671633164 -282.020530147L-462.277656517 -276.256589365L-457.32019267 -272.060157806L-455.620890942 -265.791277581L-452.61976616 -260.162976606L-452.506823585 -253.785534658L-448.590863545 -248.313985908L-442.40244285 -245.67276686L-436.775329639 -241.776492435L-434.694933516 -235.255962589L-430.649188281 -230.08736824L-426.360593181 -225.118427295L-426.522251677 -218.24464111L-422.736390033 -212.505100921L-421.184044907 -205.52226576L-416.037969794 -200.553596602L-414.115707783 -193.510946069L-417.221248935 -186.904159263L-417.863146941 -180.340520036L-419.806708769 -174.038460026L-423.287581644 -168.296267035L-424.069044195 -161.627041247L-428.887255151 -156.278326662L-434.045287782 -151.256513851L-433.326629484 -143.830959692L-436.493579921 -137.076276188L-437.682349818 -130.098447357L-437.141816316 -123.040749956L-435.698813185 -116.074162685L-432.15564799 -109.90475618L-436.107369158 -104.582431036L-441.225344756 -100.369407072L-445.873539454 -95.7110641291L-450.663205747 -91.1983077948L-456.689257495 -87.7139504068L-460.611723363 -81.9634454213L-460.944089225 -74.9120245456L-464.955139375 -69.103024621L-465.072247513 -62.0229138732L-462.619857147 -55.3800632798L-466.83973372 -49.3915250869L-466.686726973 -42.06714546L-471.932610662 -36.8701051203L-472.475682082 -29.5057587537L-475.351295643 -22.6593122799L-472.818255363 -15.6788617489L-468.420393858 -10.3894993088L-466.007500221 -3.94772275733L-462.565501903 2.02266925624L-457.257152241 6.41747950113L-455.108038321 13.1308446653L-449.705425696 17.6585084437L-446.615339789 23.6840617883L-443.691410505 29.7919715425L-443.834112479 27.6258854399L-444.622542121 25.6033445736L-445.953081595 23.7975458469L-446.0491224 21.5565593461L-446.983100742 19.5267545249L-446.689395405 17.3117691233L-446.889202368 15.1341090161L-447.892328034 13.190950437L-447.54431353 10.9365319366L-447.355692539 8.66322179673L-448.319103587 6.42725471482L-447.57495036 4.10907749707L-445.818960067 2.31007059794L-445.593591355 -0.193753811675L-443.169512041 -0.794859146968L-441.594740826 -2.7333079502L-441.751576788 -5.1941918928L-440.347046752 -7.22097325163L-440.726728996 -9.55444992462L-441.874587681 -11.6212572274L-441.279520581 -13.9047599671L-439.438387611 -15.380808409L-437.432020582 -16.5135110874L-436.161599633 -18.4356335139L-435.92862693 -20.6173392212L-435.42668271 -22.753262458L-435.289712269 -25.0248976546L-433.983265008 -26.8883039232L-433.401121503 -29.2199353346L-434.254295445 -31.466597375L-435.111107727 -33.726545982L-437.151631256 -35.0218292415L-436.066282761 -36.9725956002L-434.851799059 -38.8456946579L-434.261655739 -41.0107210837L-433.223767866 -43.0002932298L-431.126756235 -44.2246334879L-429.933945352 -46.3397384612L-427.520250123 -46.0469733464L-425.297751369 -47.0329425968L-424.522781181 -49.3719034696L-422.41135462 -50.6420194625L-420.152486655 -51.1432050003L-417.853768022 -51.4069550266L-416.343748636 -52.9997806889L-414.732249004 -54.4898548719L-413.142907066 -56.1717566343L-412.890969446 -58.4720454787L-411.787687693 -60.5370227362L-412.242940004 -62.8335651446L-410.712191286 -64.4296673016L-409.449633098 -66.2453436368L-408.595453403 -68.2653602801L-408.543146068 -70.4579282112L-407.465522226 -72.3239843987L-406.320615683 -74.1495330409L-407.826904864 -76.02385871L-407.945837444 -78.425495413L-408.36933663 -80.7099492081L-407.976077791 -82.9998025194L-407.655011284 -85.3917960569L-405.808786488 -86.9461997334L-404.873082686 -88.9962164888L-403.756626889 -90.9536731947L-397.015159322 -91.0875840666L-390.409053459 -92.4383921199L-383.620259345 -93.8219196157L-376.994491283 -91.7968093889L-370.108965629 -91.7478139483L-363.235893609 -92.1646413613L-356.987905447 -89.2793105236L-350.793396387 -86.2808863355L-343.842106007 -85.5492613123L-337.846546474 -81.9563545468L-332.833046291 -77.1444674512L-326.396322465 -74.5256837838L-320.822682615 -69.3981094471L-313.365931136 -68.0735955266L-311.047140319 -61.1767629572L-308.174302228 -54.4917130963L-306.055039952 -48.0460317659L-305.089013164 -41.3300156367L-299.578942435 -37.2714270775L-295.592428915 -31.7089878212L-289.895297312 -27.9775705903L-285.639189489 -22.6609617795L-281.415140784 -16.9781719087L-281.393337828 -9.89747894781L-279.283967767 -3.30117966192L-275.372610809 2.41387878645L-271.347798333 8.20347366812L-265.11734486 11.5049845176L-259.515641334 15.5179817881L-253.976959999 19.6175230504L-247.375812407 21.7977822101L-240.433782143 22.167775948L-239.544101582 29.4445099406L-242.97202649 35.924611786L-242.064651312 42.8609559472L-240.932582714 49.7641885758L-235.875328983 55.3471574642L-235.685237064 62.8777100318L-232.12551614 69.3802882778L-225.456960119 72.6184200711L-219.734331322 76.9985748797L-212.546927371 77.5234186006L-206.655383583 81.7414625545L-204.281614953 88.5874398762L-202.30547674 95.1905802403L-198.477159637 100.922122185L-193.660055705 105.827832694L-187.733919809 109.313718409L-182.636160048 115.376982806L-182.453614469 123.29638978L-180.99130228 130.963204121L-184.00757162 138.161851102L-187.837601453 144.43631717L-193.19247332 149.472526087L-194.774067819 157.084692164L-200.952125162 161.804662608L-194.878176039 165.975823665L-190.712234237 172.053353709L-183.838431296 175.255536902L-179.976744528 181.781678135L-179.70717508 189.033679487L-179.707524286 196.290689282L-175.832563251 203.061642595L-177.631031812 210.652864013L-180.358123591 208.53676233L-183.803320815 208.750144593L-186.87372201 209.809469837L-189.649358458 211.496294007L-192.583175366 210.729191221L-195.593005129 210.359524987L-198.618901455 210.227625364L-201.64437624 210.368864214L-204.734675973 210.300945345L-207.825490993 210.263157502L-210.749350006 209.229731918L-213.801342219 208.679946962L-216.729400603 210.041902322L-219.92615288 209.584493111L-222.350567264 211.767511497L-225.599554575 212.063207005L-228.739810938 212.892946167L-231.950331853 213.385117439L-235.155858612 213.991415061L-238.157642158 215.269027736L-241.503032849 215.34158236L-244.554121011 213.96762309L-247.864645937 212.856056485L-251.076496236 214.226885889L-253.645603234 212.177454149L-256.796092225 211.242093076L-258.410648036 208.246765103L-261.505692815 206.832736128L-264.712676556 205.973103819L-267.110552773 203.676606036L-270.223625316 202.321910284L-271.875847769 199.356007014L-274.938364664 198.530965573L-278.029093095 197.818855413L-281.213587679 197.411447687L-284.368701276 198.004950004L-287.543728528 199.639966139L-291.00723176 198.76920717L-293.203261068 196.025206168L-296.595064481 195.104469596L-299.708539427 196.698131903L-302.684890145 198.535200815L-306.239611818 198.985809476L-309.642139815 197.862467333L-313.050764021 197.342308962L-315.829040105 195.300137149L-318.383425209 193.087822822L-321.436829683 191.640086984L-322.993255201 194.838351242L-326.301468505 196.144903726L-329.129589518 197.919157743L-331.460373071 200.309491152L-334.552324992 201.935547844L-337.013053599 204.415273993L-337.070893795 208.087065083L-339.466049913 210.870699841L-343.190919309 211.240383306L-346.325092863 213.28691061L-349.411626896 215.352829212L-351.416666196 218.479253594L-355.059179679 219.163910493L-358.486906456 217.754176941L-361.971268653 217.024557135L-365.344666436 215.887318206L-367.497455987 215.118464771L-369.140452102 213.52906167L-370.956846922 212.219664462L-372.586680972 210.684264789L-373.582755094 208.537109875L-374.756630394 206.481762054L-376.667672899 204.952112289L-379.087039004 204.579849594L-380.412740154 202.586469081L-381.848765139 200.671036583L-382.44933235 198.225768735L-384.379529127 196.608871638L-386.85074277 197.161228453L-389.373247355 196.939946876L-392.023342351 196.513171599L-393.691505691 194.41022802L-395.536117279 192.697574772L-396.472760733 190.361237447L-398.214891073 188.592344191L-400.393978306 187.402562571L-402.555552842 186.204999056L-404.999459756 185.839100375L-406.987317202 184.395269713L-409.301670059 183.57065158L-409.658809292 181.097743833L-408.945213044 178.703249825L-408.438424655 176.151456706L-409.713383331 173.883646537L-411.613665903 172.100126219L-411.782373285 169.499442387L-412.68444869 167.244265553L-413.866678827 165.122498721L-412.558795255 162.88642467L-412.062380327 160.343953664L-411.959640475 157.724783003L-413.016169027 155.32595874L-413.672415771 152.80133923L-414.541212917 150.34175492L-416.771654718 148.987123198L-418.851714884 147.411290898L-418.330558988 145.106933813L-417.657132366 142.84238935L-416.234235109 140.95782181L-414.855702462 139.040563572L-414.478702599 136.694236778L-414.767053241 134.335374292L-413.836449574 132.1714512L-413.110224194 129.93065156L-413.149501247 127.467441358L-414.523918352 125.422954917L-415.746304722 123.390388409L-416.582215979 121.170747128L-416.894014401 118.684463613L-415.645874389 116.511684176L-414.388953725 114.482804246L-413.533864173 112.254570599L-413.478493878 109.789706516L-414.221650015 107.438889685L-416.437888322 106.253459483L-417.794681612 104.137787257L-418.015690606 101.805456524L-418.492221524 99.5116540235L-417.070120913 97.4638685624L-417.317748055 94.9830468759L-417.767223284 95.8396999193L-417.922143721 96.7946250872L-417.658938212 97.787485909L-418.147471428 98.6910261949L-417.301037261 99.2909032575L-416.814989134 100.207451406L-416.882609931 101.219396491L-416.751915844 102.225142226L-415.921762019 102.759076832L-414.960332592 102.982448702L-414.262196509 103.724666009L-413.265607358 103.937011446L-412.735704401 104.761955569L-412.292972883 105.636780553L-411.777471754 106.51271637L-411.788356472 107.529026605L-411.311241608 108.278620776L-410.924989835 109.078832987L-411.057030184 109.981927129L-410.761080066 110.845308394L-410.507457033 111.701840902L-410.675743374 112.579139191L-410.788036755 113.456303577L-410.562441643 114.311367323L-410.6028685 115.207449498L-410.36848858 116.073280688L-410.650092067 116.913685341L-410.949756917 117.747820511L-410.480326073 118.540437468L-410.299521252 119.443718473L-410.516663021 120.325616809L-410.759483342 121.200793086L-410.231165342 122.003722639L-409.472202857 122.593461328L-408.464177327 122.706677108L-407.785610554 123.460653512L-407.23275184 124.271892541L-406.882346412 125.188940825L-405.97604137 125.564903249L-405.175863361 126.132751581L-404.29777269 126.686382126L-403.815317332 127.605504987L-403.158914412 128.363608374L-402.539129541 129.151930805L-403.043065313 130.098344903L-403.34817618 131.126235104L-402.817832682 132.076293196L-402.694794327 133.157374214L-401.748601706 133.177068849L-400.893856682 133.583368366L-399.97744352 133.71210377L-399.06507283 133.86691208L-398.033020291 133.775508341L-397.116144292 134.258028278L-396.723815166 135.199554114L-395.958683661 135.8740664L-395.052053046 135.478083389L-394.075965566 135.63943656L-393.291650272 136.192559725L-392.377602856 136.485156783L-391.461505526 136.325103352L-390.53170496 136.343056797L-389.551444331 136.319763258L-388.769911502 136.911928713" />
+    <path
+       id="path3275"
+       d="M 193.52657,268.85494 L 196.21468,268.3195 L 198.85198,267.71783 L 201.69355,267.96748 L 204.57393,267.98941 L 207.40716,267.76168 L 210.17135,268.16093 L 213.07609,268.11678 L 215.62066,268.85718 L 217.60818,269.97429 L 220.26216,270.59636 L 223.17753,270.55063 L 225.8528,271.16357 L 228.65319,270.66657 L 231.57332,270.90391 L 234.25862,271.44468 L 236.83056,272.12166 L 239.29588,271.28646 L 241.82144,270.50296 L 243.90868,269.31191 L 244.30342,267.70325 L 247.09102,267.17023 L 248.7518,265.87439 L 251.43723,265.13153 L 252.43375,263.62071 L 255.22434,263.12308 L 257.77501,262.34573 L 260.70964,262.36233 L 263.62717,262.53003 L 266.0192,261.69988 L 268.25858,260.75864 L 271.10496,260.41658 L 273.46908,259.51306 L 276.04712,259.20928 L 278.67872,259.0884 L 281.15376,259.61513 L 283.81642,259.71086 L 286.3649,260.14722 L 288.98848,259.86028 L 291.57093,259.47108 L 294.168,259.83222 L 296.48345,260.61287 L 298.20355,261.74339 L 300.9083,261.90419 L 303.51903,262.31034 L 306.13221,262.4557 L 308.6521,262.84865 L 311.11867,263.46936 L 313.82479,263.26833 L 314.59115,264.80821 L 316.49863,266.04191 L 319.07256,266.86811 L 322.05839,267.08082 L 323.95246,268.18416 L 326.42091,268.90524 L 329.27457,269.02076 L 331.72002,269.80523 L 334.45898,269.16804 L 337.30899,269.65104 L 339.90969,269.10352 L 342.42858,268.45827 L 345.48472,268.41841 L 347.81558,269.46197 L 350.48053,268.81708 L 353.4116,268.85494 L 355.28034,269.82676 L 356.03733,271.15224 L 355.81457,272.53663 L 356.94306,273.79198 L 359.16015,274.45565 L 360.96755,275.404 L 361.74233,276.75822 L 363.96322,277.55081 L 363.12037,278.88976 L 363.66231,280.27134 L 365.86107,281.13189 L 366.55513,282.52905 L 367.78192,283.75554 L 367.19975,285.10788 L 367.41773,286.46173 L 369.02098,287.52509 L 366.49292,288.31114 L 364.91092,289.61502 L 364.15483,291.08773 L 364.32723,292.61079 L 361.61539,292.99142 L 359.73825,294.09185 L 357.81457,295.08739 L 355.67752,295.95338 L 355.93972,297.50097 L 354.48713,298.85259 L 352.63219,300.04231 L 352.2208,301.56742 L 352.41957,303.13644 L 354.49724,304.26399 L 355.1223,305.74634 L 356.96311,306.91381 L 355.32924,307.91299 L 354.17254,309.08302 L 354.72621,310.42905 L 353.68427,311.69196 L 351.76986,312.49474 L 350.36118,313.54935 L 348.30903,314.38214 L 347.82055,315.72349 L 347.64283,317.03404 L 348.37142,318.29044 L 349.41034,319.52701 L 348.93789,320.85644 L 349.09324,322.24229 L 347.42678,323.3167 L 346.185,324.44236 L 345.48444,325.69123 L 345.64185,327.02915 L 346.87301,328.20178 L 347.3392,329.48617 L 347.86922,330.76366 L 345.62155,331.63092 L 344.73022,333.02273 L 344.17793,334.4614 L 345.283,335.80854 L 346.76921,336.9064 L 347.46325,338.20485 L 349.18693,339.19105 L 351.35007,339.89618 L 351.83986,341.17231 L 352.62311,342.40704 L 353.576,343.64207 L 353.4116,344.97267 L 350.54841,345.19741 L 347.67676,345.38983 L 345.28496,344.43492 L 342.30604,344.24988 L 339.35177,343.8842 L 336.40961,344.27613 L 334.63769,342.92192 L 331.65947,342.43295 L 329.92211,341.10893 L 326.93443,340.77916 L 325.4112,339.51604 L 323.69005,338.32575 L 322.52656,336.94173 L 320.49844,335.87052 L 318.86475,334.54313 L 316.0004,334.07271 L 313.28916,333.96062 L 310.5994,334.17243 L 308.31306,335.09828 L 305.43534,335.01307 L 302.70104,335.2819 L 300.35267,336.06817 L 297.61014,336.4021 L 294.89152,336.01756 L 291.93647,335.6717 L 289.76752,334.55784 L 286.8656,334.36764 L 284.07988,333.89847 L 281.0364,333.90561 L 278.03684,334.17754 L 275.16577,334.87599 L 273.46908,336.28342 L 270.97426,336.85112 L 268.36226,337.24573 L 266.66031,338.37655 L 264.44253,339.2227 L 261.73548,339.2416 L 259.25886,339.81847 L 256.45382,339.70266 L 254.13443,340.54295 L 251.34418,340.77439 L 248.76305,341.37952 L 246.63158,340.29445 L 243.68547,340.13281 L 241.07739,339.88262 L 238.44785,340.05959 L 235.83806,339.92643 L 233.21635,339.90729 L 230.257,339.67357 L 227.26481,339.65804 L 224.99804,338.63323 L 222.76561,337.58766 L 219.94304,337.85152 L 217.39346,338.54277 L 215.01369,339.49662 L 212.02504,339.5037 L 209.41943,339.87094 L 206.98346,340.4816 L 205.30771,341.64393 L 202.7935,342.25485 L 200.38026,342.84533 L 197.9433,343.40808 L 195.53661,344.03456 L 193.52657,344.97267 L 191.81721,343.98059 L 191.24652,342.67414 L 190.65904,341.37939 L 189.05398,340.35205 L 188.1617,339.06122 L 188.78644,337.72736 L 188.02932,336.47196 L 186.95015,335.28392 L 187.80815,333.83816 L 186.69578,332.44149 L 187.83049,331.07889 L 190.21868,330.28668 L 190.51922,328.89958 L 189.19138,327.69192 L 188.9004,326.37296 L 188.91654,325.04513 L 191.67272,324.8437 L 194.39736,324.54592 L 195.74182,323.19526 L 198.23407,322.42156 L 198.2201,320.92384 L 198.21507,319.4261 L 200.64772,318.57431 L 202.08178,317.23258 L 202.4686,315.90217 L 203.45589,314.66107 L 204.81833,313.52188 L 205.58694,312.23738 L 204.473,310.97693 L 204.12808,309.59816 L 204.19798,308.19855 L 205.25284,306.91381 L 206.63363,305.65025 L 207.27664,304.23174 L 205.66553,303.00202 L 205.44391,301.51172 L 204.24735,300.15147 L 204.73194,298.67384 L 202.20698,297.99463 L 200.81764,296.69135 L 198.27875,296.25753 L 195.77947,295.76381 L 194.03485,294.68043 L 192.92783,293.38437 L 191.86587,292.12578 L 189.79667,291.28538 L 188.75047,290.06783 L 187.50549,288.90349 L 187.41767,287.57527 L 188.15487,286.30443 L 189.00265,285.03766 L 190.79573,284.08376 L 192.3734,283.01488 L 193.16448,281.72606 L 193.84812,280.3582 L 192.75538,279.06639 L 193.30719,277.76615 L 193.68267,276.44853 L 192.87475,275.18688 L 192.00468,273.93678 L 191.06278,272.6497 L 191.56876,271.2961 L 191.70866,269.88824 L 193.52657,268.85494"
+       style="fill:url(#radialGradient4250);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1.71851885;stroke-miterlimit:4;stroke-opacity:1" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/dust3.svg b/src/modules/oldfilm/dust3.svg
new file mode 100644 (file)
index 0000000..d9c4089
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="387.78088"
+   height="236.67728"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dust3.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="359"
+     inkscape:window-y="103" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-112.24286,-169.15633)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.80000019;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 117.14286,383.79075 C 132.89134,377.9933 141.35833,331.00385 162.85714,309.50504 C 179.04427,293.31791 204.56791,260.36922 214.28571,240.93361 C 221.87943,225.74618 269.62455,193.26419 288.57143,183.79075 C 322.78762,166.68266 384.04214,174.38325 414.28571,189.50504 C 443.33513,204.02975 470.5072,233.37658 482.85714,258.07647 C 494.64175,281.64568 500.51676,319.90009 488.57143,343.79075 C 472.23394,376.46573 461.06991,400.93361 420,400.93361 C 386.98682,400.93361 357.67527,390.56986 345.71429,366.6479 C 331.7481,338.71553 313.48288,313.61367 300,286.6479 C 271.13254,228.91297 304.25918,299.42543 294.28571,269.50504"
+       id="path2160" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/dust4.svg b/src/modules/oldfilm/dust4.svg
new file mode 100644 (file)
index 0000000..19b5a19
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="99.410004"
+   height="233.62619"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dust4.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="DiamondS"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="DiamondS"
+       style="overflow:visible">
+      <path
+         id="path3217"
+         d="M 0,-7.0710768 L -7.0710894,0 L 0,7.0710589 L 7.0710462,0 L 0,-7.0710768 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="scale(0.2,0.2)" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="DistanceIn"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="DistanceIn">
+      <g
+         id="g2300">
+        <path
+           style="fill:none;stroke:#ffffff;stroke-width:1.14999998;stroke-linecap:square"
+           d="M 0,0 L 2,0"
+           id="path2306" />
+        <path
+           style="fill:#000000;fill-rule:evenodd;stroke:none"
+           d="M 0,0 L 13,4 L 9,0 L 13,-4 L 0,0 z "
+           id="path2302" />
+        <path
+           style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square"
+           d="M 0,-4 L 0,40"
+           id="path2304" />
+      </g>
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Send"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow2Send"
+       style="overflow:visible">
+      <path
+         id="path3164"
+         style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4074468"
+     inkscape:cx="71.428571"
+     inkscape:cy="105.71429"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="875"
+     inkscape:window-y="0"
+     width="99.41px"
+     height="233.62619px"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-243.11429,-209.90743)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Send);marker-mid:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 291.42857,218.07647 C 272.34606,222.088 251.42857,231.57861 251.42857,269.50504 C 251.42857,297.73622 256.73845,331.55337 268.57143,355.21933 C 278.43679,374.95004 253.6971,424.96798 245.71429,440.93361"
+       id="path2160" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/dust5.svg b/src/modules/oldfilm/dust5.svg
new file mode 100644 (file)
index 0000000..ca9b24b
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="172.63466"
+   height="265.65714"
+   id="svg2186"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dust5.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs2188" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata2191">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-167.17143,-216.67647)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 185.71429,480.93361 C 188.96777,468.49843 168.57143,429.55044 168.57143,400.93361 C 168.57143,374.88777 199.28869,339.49909 208.57143,320.93361 C 222.78379,292.50889 246.00003,285.60027 277.14286,275.21933 C 307.18704,265.2046 334.28571,323.13529 334.28571,343.79075 C 334.28571,376.81996 350.98623,401.15478 317.14286,418.07647 C 290.79544,431.25018 254.79122,418.32922 231.42857,406.6479 C 210.48639,396.17681 220,341.45229 220,320.93361 C 220,279.3284 208.87913,269.19734 242.85714,235.21933 C 249.33287,228.7436 269.08286,222.10647 277.14286,218.07647"
+       id="path2194" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/factory.c b/src/modules/oldfilm/factory.c
new file mode 100644 (file)
index 0000000..bb7c074
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+#include <limits.h>
+
+extern mlt_filter filter_dust_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_grain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_lines_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_oldfilm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_tcolor_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+static mlt_properties oldfilm_metadata( mlt_service_type type, const char *id, void *data )
+{
+       char file[ PATH_MAX ];
+       snprintf( file, PATH_MAX, "%s/oldfilm/filter_%s.yml", mlt_environment( "MLT_DATA" ), id );
+       return mlt_properties_parse_yaml( file );
+}
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "oldfilm", filter_oldfilm_init );
+       MLT_REGISTER( filter_type, "dust", filter_dust_init );
+       MLT_REGISTER( filter_type, "lines", filter_lines_init );
+       MLT_REGISTER( filter_type, "grain", filter_grain_init );
+       MLT_REGISTER( filter_type, "tcolor", filter_tcolor_init );
+       MLT_REGISTER( filter_type, "vignette", filter_vignette_init );
+       
+       MLT_REGISTER_METADATA( filter_type, "vignette", oldfilm_metadata, NULL );
+       MLT_REGISTER_METADATA( filter_type, "tcolor", oldfilm_metadata, NULL );
+       MLT_REGISTER_METADATA( filter_type, "grain", oldfilm_metadata, NULL );
+       MLT_REGISTER_METADATA( filter_type, "lines", oldfilm_metadata, NULL );
+       MLT_REGISTER_METADATA( filter_type, "dust", oldfilm_metadata, NULL );
+       MLT_REGISTER_METADATA( filter_type, "oldfilm", oldfilm_metadata, NULL );
+       
+}
+
+
+
diff --git a/src/modules/oldfilm/fdust.svg b/src/modules/oldfilm/fdust.svg
new file mode 100644 (file)
index 0000000..693aa39
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="119.44395"
+   height="103.53123"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="dus.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1672"
+     inkscape:window-height="969"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-63.761736,-89.023088)">
+    <path
+       sodipodi:type="star"
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="path2160"
+       sodipodi:sides="4"
+       sodipodi:cx="88.571426"
+       sodipodi:cy="120.93361"
+       sodipodi:r1="28.571428"
+       sodipodi:r2="11.714285"
+       sodipodi:arg1="0.64350111"
+       sodipodi:arg2="1.4288993"
+       inkscape:flatsided="false"
+       inkscape:rounded="0.6"
+       inkscape:randomized="-0.046"
+       d="M 112.40071,138.48615 C 104.63523,149.2232 103.39252,131.84024 90.301427,133.56163 C 77.210335,135.28301 81.871947,152.01419 71.336774,143.68907 C 60.801601,135.36395 78.494219,134.93387 76.174183,122.07035 C 73.854148,109.20683 57.818168,114.22716 66.13398,103.91357 C 74.449793,93.59998 72.430606,110.63192 85.604689,108.83809 C 98.778772,107.04427 94.053041,90.946435 104.56934,98.710649 C 115.08564,106.47486 97.158906,106.41587 99.590228,119.11612 C 102.02155,131.81637 120.16619,127.74909 112.40071,138.48615 z " />
+    <path
+       sodipodi:type="star"
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="path2162"
+       sodipodi:sides="4"
+       sodipodi:cx="111.42857"
+       sodipodi:cy="178.07648"
+       sodipodi:r1="12.777531"
+       sodipodi:r2="5.2387872"
+       sodipodi:arg1="-0.46364761"
+       sodipodi:arg2="0.32175055"
+       inkscape:flatsided="true"
+       inkscape:rounded="0.6"
+       inkscape:randomized="-0.046"
+       d="M 122.44277,172.69754 C 127.84769,182.25699 126.41544,184.43858 116.88365,189.64343 C 107.35186,194.84828 104.6647,193.25522 99.636235,183.39008 C 94.607766,173.52495 95.553578,171.33947 105.19655,166.41559 C 114.83951,161.4917 117.03785,163.1381 122.44277,172.69754 z " />
+    <path
+       sodipodi:type="star"
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="path2164"
+       sodipodi:sides="4"
+       sodipodi:cx="168.57143"
+       sodipodi:cy="103.79075"
+       sodipodi:r1="12.777531"
+       sodipodi:r2="9.035079"
+       sodipodi:arg1="0.46364761"
+       sodipodi:arg2="1.2490458"
+       inkscape:flatsided="true"
+       inkscape:rounded="0.6"
+       inkscape:randomized="-0.046"
+       d="M 180.40305,108.92085 C 175.41043,118.48299 172.98728,120.00139 163.16132,115.65738 C 153.33537,111.31338 152.8374,107.92884 157.36569,97.901837 C 161.89398,87.874831 165.34367,86.870642 174.60621,92.239286 C 183.86875,97.60793 185.39567,99.358711 180.40305,108.92085 z " />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/filter_dust.c b/src/modules/oldfilm/filter_dust.c
new file mode 100644 (file)
index 0000000..fc62024
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * filter_dust.c -- dust filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+//#include <framework/mlt_frame.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <time.h>
+
+static void overlay_image(uint8_t *src, int src_width, int src_height , uint8_t * overlay, int overlay_width, int overlay_height, uint8_t * alpha , int xpos, int ypos, int upsidedown , int mirror ){
+       int x,y;
+
+       for (y=ypos;y<src_height ; y++){
+               if ( y>=0 && (y-ypos)<overlay_height ){
+                       uint8_t *scanline_image=src+src_width*y*2;
+                       int overlay_y = upsidedown ?  ( overlay_height - ( y - ypos ) - 1 ) : ( y - ypos  );
+                       uint8_t *scanline_overlay=overlay + overlay_width * 2 * overlay_y;
+                       
+                       for ( x = xpos  ;  x < src_width  && x-xpos < overlay_width ;x++){
+                               if ( x>0  ){
+                                       int overlay_x = mirror ? overlay_width - ( x - xpos ) -1 : ( x - xpos );
+                                       double alp=(double)*(alpha+ overlay_width * overlay_y + overlay_x )/255.0;
+                                       uint8_t* image_pixel = scanline_image + x * 2;
+                                       uint8_t* overlay_pixel = scanline_overlay + overlay_x * 2;
+                                       
+                                       *image_pixel=(double)(*overlay_pixel)*alp+ (double)*image_pixel*(1.0-alp) ;
+                                       if (xpos%2==0)
+                                               image_pixel++;
+                                       else
+                                               image_pixel+=3;
+
+                                       mirror? overlay_pixel-- : overlay_pixel++;
+
+                                       *image_pixel=(double)(*(overlay_pixel))*alp + (double)(*image_pixel )*(1.0-alp) ;
+                               }
+               
+                       }
+               }
+       }
+}
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_filter filter = mlt_frame_pop_service( this );
+       
+       int maxdia = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxdiameter" );
+       int maxcount = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "maxcount" );
+
+       mlt_position in = mlt_filter_get_in( filter );
+       mlt_position out = mlt_filter_get_out( filter );
+       mlt_position time = mlt_frame_get_position( this );
+       double position = ( double )( time - in ) / ( double )( out - in + 1 );
+
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );       
+       // load svg
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       char *factory = mlt_properties_get( properties, "factory" );
+       char temp[1204]="";
+       sprintf( temp, "%s/oldfilm/", mlt_environment( "MLT_DATA" ) );
+       
+       mlt_properties direntries=mlt_properties_new();
+       mlt_properties_dir_list(direntries,temp,"dust*.svg",1);
+       
+       if (!maxcount)
+               return 0;
+       srand(position*10000);
+
+       int im=rand()%maxcount;
+       int piccount=mlt_properties_count(direntries);
+       while (im-- && piccount){
+       
+               int picnum=rand()%piccount;
+               
+               int y1=rand()%*height;
+               int x1=rand()%*width;
+               char resource[1024]="";
+               char savename[1024]="",savename1[1024]="", cachedy[100];
+               int dx=(*width*maxdia/100);
+               int luma_width,luma_height;
+               uint8_t *luma_image = NULL;
+               uint8_t *alpha =NULL;
+               int updown= rand()%2;
+               int mirror=rand()%2;
+               
+               sprintf(resource,"%s",mlt_properties_get_value(direntries,picnum));
+               sprintf(savename,"cache-%d-%d",picnum,dx);
+               sprintf(savename1,"cache-alpha-%d-%d",picnum,dx);
+               sprintf(cachedy,"cache-dy-%d-%d",picnum,dx);
+               
+               luma_image= mlt_properties_get_data( properties , savename , NULL );
+               alpha= mlt_properties_get_data( properties , savename1 , NULL );
+               
+               if (luma_image == NULL || alpha == NULL ){
+                       mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+                       mlt_producer producer = mlt_factory_producer( profile, factory, resource );
+               
+                       if ( producer != NULL )
+                       {
+                               mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+                               
+                               mlt_properties_set( producer_properties, "eof", "loop" );
+                               mlt_frame luma_frame = NULL;
+                               
+                               if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &luma_frame, 0 ) == 0 ){
+               
+                                       mlt_properties_set_double ( MLT_FRAME_PROPERTIES ( luma_frame ) , "consumer_aspect_ratio" , 1.0 );
+                                       mlt_image_format luma_format = mlt_image_yuv422;
+                                       luma_width = dx;
+                                       luma_height = luma_width * mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "height" ) / mlt_properties_get_int( MLT_FRAME_PROPERTIES ( luma_frame ) , "width" );
+
+                                       mlt_properties_set( MLT_FRAME_PROPERTIES( luma_frame ), "rescale.interp", "best" );// none/nearest/tiles/hyper
+                                       
+                                       mlt_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 );
+                                       alpha =mlt_frame_get_alpha_mask(luma_frame);
+                                       
+                                       uint8_t* savealpha=mlt_pool_alloc ( luma_width * luma_height );
+                                       uint8_t* savepic=mlt_pool_alloc ( luma_width * luma_height * 2);
+                                       
+                                       if (savealpha && savepic ){
+                                               memcpy (savealpha, alpha , luma_width * luma_height );
+                                               memcpy (savepic, luma_image , luma_width * luma_height * 2);
+                                               
+                                               mlt_properties_set_data ( properties , savename , savepic , sizeof(uint8_t*) , mlt_pool_release, NULL );
+                                               mlt_properties_set_data ( properties , savename1 , savealpha , sizeof(uint8_t*)  ,  mlt_pool_release, NULL );
+                                               mlt_properties_set_int ( properties , cachedy , luma_height );
+                                               
+                                               overlay_image(*image,*width,*height,luma_image,luma_width,luma_height, alpha, x1, y1 , updown , mirror );
+                                       }
+                                       mlt_frame_close( luma_frame );  
+                               }
+                               mlt_producer_close( producer ); 
+                       }
+               }else {
+                       overlay_image ( *image , *width, *height , luma_image , dx , mlt_properties_get_int ( properties , cachedy ) , alpha , x1 , y1 , updown , mirror );
+               }
+       }
+       if (piccount>0 )
+               return 0;
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+
+               int h = *height;
+               int w = *width;
+               if (maxcount==0)
+                       return 0;
+
+               int im=rand()%maxcount;
+               
+               while (im-- ){
+                       int type=im%2;
+                       int y1=rand()%h;
+                       int x1=rand()%w;
+                       int dx=rand()%maxdia;
+                       int dy=rand()%maxdia;
+                       int x=0,y=0;//,v=0;
+                       double v=0.0;
+                       for ( x = -dx ; x < dx ; x++ )
+                               for ( y = -dy ; y < dy ; y++ ) {
+                                       if ( x1+x < w && x1+x > 0 && y1+y < h && y1+y > 0 ){
+                                               uint8_t *pix=*image+(y+y1)*w*2+(x+x1)*2;
+                                               //v=(1.0-fabs(x)/dx)*(1.0-fabs(y)/dy);
+                                               v=pow((double)x/(double)dx*5.0,2.0)+pow((double)y/(double)dy*5.0,2.0);
+                                               if (v>10)
+                                                       v=10;
+                                               v=1.0-(v/10.0);
+
+                                               switch(type){
+                                                       case 0:
+                                                               *pix-=(*pix)*v;
+                                                               break;
+                                                       case 1:
+                                                               *pix+=(255-*pix)*v;
+                                                               break;
+                                               }
+                                       }
+                               }
+               }
+       }
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+
+mlt_filter filter_dust_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxdiameter", "2" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "maxcount", "10" );
+       }
+       return this;
+}
+
+
diff --git a/src/modules/oldfilm/filter_dust.yml b/src/modules/oldfilm/filter_dust.yml
new file mode 100644 (file)
index 0000000..7460b60
--- /dev/null
@@ -0,0 +1,47 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: dust
+title: Dust
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: Add dust and specks to the Video, as in old movies
+icon:
+  filename: oldfilm/fdust.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: maxdiameter
+    title: Maximal Diameter
+    type: integer
+    description: Maximal diameter of a dust piece
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 2
+    mutable: no
+    widget: spinner
+    unit: %
+
+  - identifier: maxcount
+    title: Maximal number of dust 
+    type: integer
+    description: How many dust pieces are on the image
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 400
+    default: 10
+    mutable: no
+    widget: spinner
+
diff --git a/src/modules/oldfilm/filter_grain.c b/src/modules/oldfilm/filter_grain.c
new file mode 100644 (file)
index 0000000..296da5a
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * filter_grain.c -- grain filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a>b?a:b)
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               int h = *height;
+               int w = *width;
+               
+               mlt_position in = mlt_filter_get_in( filter );
+               mlt_position out = mlt_filter_get_out( filter );
+               mlt_position time = mlt_frame_get_position( this );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               srand(position*10000);
+               
+               int noise = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "noise" );
+               double contrast = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "contrast" )/100.0;
+               double brightness = 127.0 * (mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "brightness" )-100.0)/100.0;
+               
+               int x=0,y=0,pix=0;
+               for (x=0;x<w;x++)
+                       for(y=0;y<h;y++){
+                               uint8_t* pixel=(*image+(y)*w*2+(x)*2);
+                               if (*pixel>20){
+                                       pix= MIN ( MAX ( ( (double)*pixel -127.0  ) * contrast + 127.0 + brightness , 0 ) , 255 ) ;
+                                       if (noise>0)
+                                        pix-=(rand()%noise-noise);
+                                       
+                                       *pixel= MIN ( MAX ( pix , 0 ) , 255 );
+                               }
+                               //*(pixel+1)= MIN ( MAX ( ( (double)*(pixel+1) -127.0  ) * .5 + 127.0 , 0 ) , 255 ) ;
+                       }
+       }
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+mlt_filter filter_grain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "noise", "40" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "contrast", "160" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightness", "70" );
+       }
+       return this;
+}
+
diff --git a/src/modules/oldfilm/filter_grain.yml b/src/modules/oldfilm/filter_grain.yml
new file mode 100644 (file)
index 0000000..b5e6153
--- /dev/null
@@ -0,0 +1,58 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: grain
+title: Grain
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: Grain over the Image 
+icon:
+  filename: oldfilm/grain.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: noise
+    title: Noise
+    type: integer
+    description: Maximal value of noise
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 200
+    default: 40
+    mutable: no
+    widget: spinner
+    unit: %
+
+  - identifier: contrast
+    title: Contrast
+    type: integer
+    description: Adjust contrast for the image
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 400
+    default: 160
+    mutable: no
+    widget: spinner
+
+  - identifier: brightness
+    title: Brightness
+    type: integer
+    description: Adjust brightness for the image
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 400
+    default: 70
+    mutable: no
+    widget: spinner
diff --git a/src/modules/oldfilm/filter_lines.c b/src/modules/oldfilm/filter_lines.c
new file mode 100644 (file)
index 0000000..7d3e415
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * filter_lines.c -- lines filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               int h = *height;
+               int w = *width;
+
+               int width_line = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "width" );
+               int num = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "num" );
+               double maxdarker= (double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "darker" ) ;
+               double maxlighter=(double)mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "lighter" ) ;
+               //int frame = mlt_properties_get_int( this, "_position" );
+               char buf[256];
+               char typebuf[256];
+               
+               mlt_position in = mlt_filter_get_in( filter );
+               mlt_position out = mlt_filter_get_out( filter );
+               mlt_position time = mlt_frame_get_position( this );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               srand(position*10000);
+               if (!width_line)
+                       return 0;
+               
+               while (num--){
+                       int type=(rand()%3)+1;
+                       int x1=(double)w*rand()/RAND_MAX;
+                       int dx=rand()%width_line;
+                       int x=0,y=0;
+                       int ystart=rand()%h;
+                       int yend=rand()%h;
+                       
+                       sprintf(buf,"line%d",num);
+                       sprintf(typebuf,"typeline%d",num);
+                       maxlighter+=rand()%30-15;
+                       maxdarker+=rand()%30-15;
+                       
+                       if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf)==0){
+                               mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1);
+                       }
+                       
+                       if (mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf)==0 ){
+                               mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),typebuf,type);
+                       }
+
+                       
+                       x1=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf);
+                       type=mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf);
+                       if (position!=mlt_properties_get_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos")){
+                               x1+=(rand()%11-5);
+                       }
+               
+                       if (yend<ystart){
+                               yend=h;
+                       }
+                       
+                       for (x = -dx ; x < dx && dx != 0 ; x++ )
+                               for(y=ystart;y<yend;y++)
+                                       if (x+x1<w && x+x1>0){
+                                               uint8_t* pixel=(*image+(y)*w*2+(x+x1)*2);
+                                               double diff=1.0-fabs(x)/dx;
+                                               switch(type){
+                                                       case 1: //blackline
+                                                               *pixel-=((double)*pixel*diff*maxdarker/100.0);
+                                                               break;
+                                                       case 2: //whiteline
+                                                               *pixel+=((255.0-(double)*pixel)*diff*maxlighter/100.0);
+                                                               break;
+                                                       case 3: //greenline
+                                                               *(pixel+1)-=((*(pixel+1))*diff*maxlighter/100.0);
+                                                       break;
+                                               }
+                                                       
+                               }
+                       mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),buf,x1);
+               }
+               mlt_properties_set_double(MLT_FILTER_PROPERTIES( filter ),"last_oldfilm_line_pos",position);
+       }
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+mlt_filter filter_lines_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "width", "2" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "num", "5" );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "darker" , 40 ) ;
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "lighter" , 40 ) ;
+       }
+       return this;
+}
+
+
diff --git a/src/modules/oldfilm/filter_lines.yml b/src/modules/oldfilm/filter_lines.yml
new file mode 100644 (file)
index 0000000..5f13c51
--- /dev/null
@@ -0,0 +1,73 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: lines
+title: Scratchlines
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: Scratchlines over the Picture
+icon:
+  filename: oldfilm/lines.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: width
+    title: Width of line
+    type: integer
+    description: Linewidth in picture
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 2
+    mutable: no
+    widget: spinner
+    unit: pixel
+
+  - identifier: num
+    title: Max number of lines
+    type: integer
+    description: Maximal number of lines in picture
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 5
+    mutable: no
+    widget: spinner
+    unit: lines
+
+  - identifier: darker
+    title: Max darker
+    type: integer
+    description: Make image up to n values darker behind line
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 40
+    mutable: no
+    widget: spinner
+
+  - identifier: lighter
+    title: Max lighter
+    type: integer
+    description: Make image up to n values lighter behind line
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 40
+    mutable: no
+    widget: spinner
+
+
diff --git a/src/modules/oldfilm/filter_oldfilm.c b/src/modules/oldfilm/filter_oldfilm.c
new file mode 100644 (file)
index 0000000..11a6c3e
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * filter_oldfilm.c -- oldfilm filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+       
+       if (  error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               int h = *height;
+               int w = *width;
+
+               int x=0;
+               int y=0;
+               
+               mlt_position in = mlt_filter_get_in( filter );
+               mlt_position out = mlt_filter_get_out( filter );
+               mlt_position time = mlt_frame_get_position( this );
+               double position = ( double )( time - in ) / ( double )( out - in + 1 );
+               srand(position*10000);
+               
+               int delta = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "delta" );
+               int every = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "every" );
+               
+               int bdu = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_up" );
+               int bdd = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_down" );
+               int bevery = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_every" );
+                       
+               int diffpic=0;
+               if (delta)
+                       diffpic=rand()%delta*2-delta;
+               
+               int brightdelta=0;
+               if ((bdu+bdd)!=0)
+                        brightdelta=rand()%(bdu+bdd)-bdd;
+               if (rand()%100>every)
+                       diffpic=0;
+               if (rand()%100>bevery)
+                       brightdelta=0;
+               int yend,ydiff;
+               if (diffpic<=0){
+                       y=h;
+                       yend=0;
+                       ydiff=-1;
+               }else{
+                       y=0;
+                       yend=h;
+                       ydiff=1;
+               }
+
+               while(y!=yend){
+                       //int newy=y+diffpic;
+                       for (x=0;x<w;x++){
+                                       uint8_t* pic=(*image+y*w*2+x*2);
+                                       int newy=y+diffpic;
+                                       if (newy>0 && newy<h ){
+                                               uint8_t oldval=*(pic+diffpic*w*2);
+                                               /* frame around
+                                               int randx=(x<=frameborder)?x:(x+frameborder>w)?w-x:-1;
+                                               int randy=((newy)<=frameborder)?(newy):((newy)+frameborder>h)?h-(y+diffpic):-1;
+                                               if (randx>=0 ){
+                                                       oldval=oldval*pow(((double)randx/(double)frameborder),1.5);
+                                               }
+                                               if (randy>=0 ){
+                                                       oldval=oldval*pow(((double)randy/(double)frameborder),1.5);
+                                               }
+                                               if (randx>=0 && randy>=0){
+                                                       //oldval=oldval*(randx*randy)/500.0;
+                                               }
+                                               */
+                                               if ( ((int) oldval + brightdelta ) >255)
+                                                       *pic=255;
+                                               else if ( ( (int) oldval+brightdelta )  <0){
+                                                       *pic=0;
+                                               }else
+                                                       *pic=oldval+brightdelta;
+                                               *(pic+1)=*(pic+diffpic*w*2+1);
+
+                                       }else{
+                                               *pic=0;
+                                               //*(pic-1)=127; 
+                                       }
+                                       
+                       }
+                       y+=ydiff;
+               }
+       }
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+mlt_filter filter_oldfilm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "delta", "14" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "every", "20" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_up" , "20" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_down" , "30" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "brightnessdelta_every" , "70" );
+       }
+       return this;
+}
+
diff --git a/src/modules/oldfilm/filter_oldfilm.yml b/src/modules/oldfilm/filter_oldfilm.yml
new file mode 100644 (file)
index 0000000..e0bdcaa
--- /dev/null
@@ -0,0 +1,84 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: oldfilm
+title: Oldfilm
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: Moves the Picture up and down and random brightness change
+icon:
+  filename: oldfilm/oldfilm.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: delta
+    title: Y-Delta
+    type: integer
+    description: Maximum delta value of Up/Down move
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 400
+    default: 14
+    mutable: no
+    widget: spinner
+    unit: pixel
+
+  - identifier: every
+    title: % of picture have a delta
+    type: integer
+    description: n'th % have a Y-Delta in picture
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 20
+    mutable: no
+    widget: spinner
+    unit: %
+
+  - identifier: brightnessdelta_up
+    title: Brightness up
+    type: integer
+    description: Makes image n values lighter
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 20
+    mutable: no
+    widget: spinner
+
+  - identifier: brightnessdelta_down
+    title: Brightness down
+    type: integer
+    description: Makes image n values darker
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 20
+    mutable: no
+    widget: spinner
+
+  - identifier: brightnessdelta_every
+    title: Brightness every
+    type: integer
+    description: Change value only for n/100
+    readonly: no
+    required: yes
+    minimum: 0
+    maximum: 100
+    default: 70
+    mutable: no
+    widget: spinner
+    unit: %
diff --git a/src/modules/oldfilm/filter_tcolor.c b/src/modules/oldfilm/filter_tcolor.c
new file mode 100644 (file)
index 0000000..824f107
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * filter_tcolor.c -- tcolor filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a<b?b:a)
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+
+               double over_cr = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cr" )/100.0;
+               double over_cb = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cb" )/100.0;
+                       
+               int video_width = *width;
+               int video_height = *height;
+
+               int x,y;
+               
+               for (y=0;y<video_height;y++){
+                       for (x=0;x<video_width;x+=2){
+                               uint8_t *pix=(*image+y*video_width*2+x*2+1);
+                               uint8_t *pix1=(*image+y*video_width*2+x*2+3);
+                               *pix=MIN(MAX( ((double)*pix-127.0)*over_cb+127.0,0),255);
+                               *pix1=MIN(MAX( ((double)*pix1-127.0)*over_cr+127.0,0),255);
+                       }
+               }
+               // short a, short b, short c, short d
+               // a= gray val pix 1
+               // b: +=blue, -=yellow
+               // c: =gray pix 2
+               // d: +=red,-=green
+       }
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+
+mlt_filter filter_tcolor_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cr", "190" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "oversaturate_cb", "190" );
+       }
+       return this;
+}
+
+
diff --git a/src/modules/oldfilm/filter_tcolor.yml b/src/modules/oldfilm/filter_tcolor.yml
new file mode 100644 (file)
index 0000000..fb8327e
--- /dev/null
@@ -0,0 +1,45 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: tcolor
+title: Technicolor
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: Oversaturate the Color in Video, like in old Technicolor movies
+icon:
+filename: oldfilm/tcolor.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: oversaturate_cr # 'argument' is a reserved name for a value supplied to the factory
+    title: Blue/Yellow- axis
+    type: integer
+    description: Adjust factor for Blue/Yellow axis
+    readonly: no
+    required: yes
+    minimum: -400
+    maximum: 400
+    default: 190
+    mutable: no
+    widget: spinner
+
+  - identifier: oversaturate_cb
+    title: Red/Green-axis
+    type: integer
+    description: Adjust factor for Red/Green axis
+    readonly: no
+    required: yes
+    minimum: -400
+    maximum: 400
+    default: 190
+    mutable: no
+    widget: spinner
diff --git a/src/modules/oldfilm/filter_vignette.c b/src/modules/oldfilm/filter_vignette.c
new file mode 100644 (file)
index 0000000..e9e3f5d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * filter_vignette.c -- vignette filter
+ * Copyright (c) 2007 Marco Gittler <g.marco@freenet.de>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_geometry.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define MIN(a,b) (a<b?a:b)
+#define MAX(a,b) (a<b?b:a)
+
+#define SIGMOD_STEPS 1000
+#define POSITION_VALUE(p,s,e) (s+((double)(e-s)*p ))
+//static double pow2[SIGMOD_STEPS];
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               mlt_position in = mlt_filter_get_in( filter );
+               //mlt_position out = mlt_filter_get_out( filter );
+               mlt_position time = mlt_frame_get_position( this );
+               
+               mlt_geometry geom=mlt_geometry_init();
+               struct mlt_geometry_item_s item;
+               float smooth, radius, cx, cy, opac;
+               char *val=mlt_properties_get(MLT_FILTER_PROPERTIES( filter ), "geometry" );
+               mlt_geometry_parse(geom,val,-1,-1,-1);
+               mlt_geometry_fetch(geom,&item,time-in);
+               smooth=item.x;
+               radius=item.y;
+               cx=item.w;
+               cy=item.h;
+               opac=item.mix;
+               mlt_geometry_close(geom);
+               
+               int video_width = *width;
+               int video_height = *height;
+               
+               int x,y;
+               int w2=cx,h2=cy;
+               double delta=1.0;
+               double max_opac=opac/100.0;
+
+               for (y=0;y<video_height;y++){
+                       int h2_pow2=pow(y-h2,2.0);
+                       for (x=0;x<video_width;x++){
+                               uint8_t *pix=(*image+y*video_width*2+x*2);
+                               int dx=sqrt(h2_pow2+pow(x-w2,2.0));
+                               
+                               if (radius-smooth>dx){  //center, make not darker
+                                       continue;
+                               }
+                               else if (radius+smooth<=dx){//max dark after smooth area
+                                       delta=0.0;
+                               }else{
+                                       //double sigx=5.0-10.0*(double)(dx-radius+smooth)/(2.0*smooth);//smooth >10 inner area, <-10 in dark area
+                                       //delta=pow2[((int)((sigx+10.0)*SIGMOD_STEPS/20.0))];//sigmoidal
+                                       delta = ((double)(radius+smooth-dx)/(2.0*smooth));//linear
+                               }
+                               delta=MAX(max_opac,delta);
+                               *pix=(double)(*pix)*delta;
+                               *(pix+1)=((double)(*(pix+1)-127.0)*delta)+127.0;
+                       }
+               }
+               // short a, short b, short c, short d
+               // a= gray val pix 1
+               // b: +=blue, -=yellow
+               // c: =gray pix 2
+               // d: +=red,-=green
+       }
+       
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+
+mlt_filter filter_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       //int i=0;
+       if ( this != NULL )
+       {
+               /*
+               for (i=-SIGMOD_STEPS/2;i<SIGMOD_STEPS/2;i++){
+                       pow2[i+SIGMOD_STEPS/2]=1.0/(1.0+pow(2.0,-((double)i)/((double)SIGMOD_STEPS/20.0)));
+               }
+               */
+               
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "geometry", "80:50%:50%:50%:0" );
+               //mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "end", "" );
+
+       }
+       return this;
+}
+
+
diff --git a/src/modules/oldfilm/filter_vignette.yml b/src/modules/oldfilm/filter_vignette.yml
new file mode 100644 (file)
index 0000000..a63bd47
--- /dev/null
@@ -0,0 +1,35 @@
+schema_version: 0.1
+type: filter # consumer, filter, producer, or transition
+identifier: vignette
+title: Vignette Effect
+version: 0.2.5
+copyright: Copyright (C) 2008 Marco Gittler
+license: GPL
+language: en
+url: none
+creator: Marco Gittler
+tags:
+  - Video # this may produce video
+description: |
+  Vigentte around a point with adjustable smooth, radius, position
+  and transparency
+icon:
+filename: oldfilm/vignette.svg # relative to $MLT_DATA/modules/
+  content-type: image/svg
+
+notes: Implementation or additional usage notes go here.
+bugs: # this can be just for documentation, or the tool may disclose it to help user avoid pitfalls
+  - need to do some speed improvement.
+
+parameters:
+  - identifier: geometry # 'argument' is a reserved name for a value supplied to the factory
+    title: Geometry Value  # the title can be used as a label for the widget
+    type: geometry
+    description: Start position, "smooth":"radius","X"x"Y","maxopac"
+    readonly: no
+    required: yes
+    mutable: no
+    format: %d:%d:%dx%d:%d
+    minimum: 0:0:0:0:0
+    maximum: 100:100:100:100:100
+    default: 80:50%:50%:50%:0
diff --git a/src/modules/oldfilm/grain.svg b/src/modules/oldfilm/grain.svg
new file mode 100644 (file)
index 0000000..bbf11b2
--- /dev/null
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="68.586548"
+   height="57.142868"
+   id="svg2267"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="grai.svg"
+   version="1.0"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.82687501"
+     inkscape:cx="547.34922"
+     inkscape:cy="-51.149976"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1195"
+     inkscape:window-height="742"
+     inkscape:window-x="315"
+     inkscape:window-y="144" />
+  <defs
+     id="defs2269" />
+  <metadata
+     id="metadata2272">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     id="layer1"
+     inkscape:groupmode="layer"
+     transform="translate(-49.372635,-144.21402)">
+    <rect
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3308"
+       width="22.857143"
+       height="28.571428"
+       x="49.372635"
+       y="144.21402" />
+    <rect
+       y="144.21404"
+       x="72.199547"
+       height="28.571428"
+       width="22.857143"
+       id="rect3310"
+       style="opacity:0.68085106;fill:#000000;fill-opacity:0.63380283;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+    <rect
+       style="opacity:0.68085106;fill:#000000;fill-opacity:0.10563378;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3312"
+       width="22.857143"
+       height="28.571428"
+       x="95.026451"
+       y="144.21404" />
+    <rect
+       y="172.78546"
+       x="95.102043"
+       height="28.571428"
+       width="22.857143"
+       id="rect3314"
+       style="opacity:0.94680852;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+    <rect
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3316"
+       width="22.857143"
+       height="28.571428"
+       x="72.275139"
+       y="172.78546" />
+    <rect
+       y="172.78546"
+       x="49.448231"
+       height="28.571428"
+       width="22.857143"
+       id="rect3318"
+       style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1" />
+  </g>
+  <g
+     id="g2293"
+     inkscape:groupmode="layer"
+     inkscape:label="fill text"
+     transform="translate(-49.372635,-144.21402)" />
+</svg>
diff --git a/src/modules/oldfilm/lines.svg b/src/modules/oldfilm/lines.svg
new file mode 100644 (file)
index 0000000..e517574
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="138.14285"
+   height="275.28571"
+   id="svg2251"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="lin.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2253" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="62.857143"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata2256">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-128.07143,-217.57647)">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 180,223.79075 L 180,492.36218"
+       id="path2261" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 265.71429,218.07647 L 265.71429,389.50504"
+       id="path2263" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 128.57143,309.50504 L 128.57143,440.93361"
+       id="path2265" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/oldfilm.svg b/src/modules/oldfilm/oldfilm.svg
new file mode 100644 (file)
index 0000000..b3a521f
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="320"
+   height="200"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="old.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1181"
+     inkscape:window-height="822"
+     inkscape:window-x="402"
+     inkscape:window-y="124" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-45.714287,-120.93361)">
+    <rect
+       style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2160"
+       width="320"
+       height="200"
+       x="45.714287"
+       y="120.93361" />
+    <rect
+       style="opacity:0.41489366;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2162"
+       width="268.57144"
+       height="171.42857"
+       x="74.285713"
+       y="149.50504" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/tcolor.svg b/src/modules/oldfilm/tcolor.svg
new file mode 100644 (file)
index 0000000..1e5c452
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="112.63159"
+   height="102.85714"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="tcol.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Lend"
+       style="overflow:visible">
+      <path
+         id="path4111"
+         d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.5843946"
+     inkscape:cx="88.57143"
+     inkscape:cy="77.236043"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="1121"
+     inkscape:window-height="586"
+     inkscape:window-x="551"
+     inkscape:window-y="239" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-42.857143,-175.21933)">
+    <rect
+       style="opacity:1;fill:#910000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2160"
+       width="52.370373"
+       height="102.85714"
+       x="42.857143"
+       y="175.21933" />
+    <rect
+       y="175.21933"
+       x="94.469757"
+       height="102.85714"
+       width="61.018974"
+       id="rect2162"
+       style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5999999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    <path
+       style="opacity:0.68085106;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;marker-start:none;marker-end:url(#Arrow1Lend);stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       d="M 66.39894,224.60733 C 123.87029,224.60733 123.87029,224.60733 123.87029,224.60733"
+       id="path3134" />
+  </g>
+</svg>
diff --git a/src/modules/oldfilm/vignette.svg b/src/modules/oldfilm/vignette.svg
new file mode 100644 (file)
index 0000000..4758eb0
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="251.42857"
+   height="188.57143"
+   id="svg2182"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/home/marco/mlt-svn/mlt/src/modules/oldfilm"
+   sodipodi:docname="vig.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2184">
+    <linearGradient
+       id="linearGradient3165">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3167" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0.98666668;"
+         offset="1"
+         id="stop3169" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3165"
+       id="radialGradient3171"
+       cx="228.57143"
+       cy="300.93362"
+       fx="228.57143"
+       fy="300.93362"
+       r="77.14286"
+       gradientTransform="matrix(1,0,0,0.9259259,0,22.291383)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="-176.04888"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     inkscape:window-width="797"
+     inkscape:window-height="586"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata2187">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-105.71429,-195.21933)">
+    <rect
+       style="opacity:0.95035466;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="rect2190"
+       width="251.42857"
+       height="188.57143"
+       x="105.71429"
+       y="195.21933" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:url(#radialGradient3171);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2.4000001;stroke-dasharray:none;stroke-opacity:1"
+       id="path2192"
+       sodipodi:cx="228.57143"
+       sodipodi:cy="300.93362"
+       sodipodi:rx="77.14286"
+       sodipodi:ry="71.428574"
+       d="M 305.71429 300.93362 A 77.14286 71.428574 0 1 1  151.42857,300.93362 A 77.14286 71.428574 0 1 1  305.71429 300.93362 z"
+       transform="translate(0,-11.428571)" />
+  </g>
+</svg>
diff --git a/src/modules/plus/Makefile b/src/modules/plus/Makefile
new file mode 100644 (file)
index 0000000..4003b42
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltplus$(LIBSUF)
+
+OBJS = factory.o \
+          filter_affine.o \
+          filter_charcoal.o \
+          filter_invert.o \
+          filter_sepia.o \
+          transition_affine.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/plus/factory.c b/src/modules/plus/factory.c
new file mode 100644 (file)
index 0000000..2666f7f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "affine", filter_affine_init );
+       MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init );
+       MLT_REGISTER( filter_type, "invert", filter_invert_init );
+       MLT_REGISTER( filter_type, "sepia", filter_sepia_init );
+       MLT_REGISTER( transition_type, "affine", transition_affine_init );
+}
diff --git a/src/modules/plus/filter_affine.c b/src/modules/plus/filter_affine.c
new file mode 100644 (file)
index 0000000..cbf6caa
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * filter_affine.c -- affine filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Get the properties
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the image
+       int error = 0; //mlt_frame_get_image( this, image, format, width, height, 0 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
+               mlt_transition transition = mlt_properties_get_data( properties, "transition", NULL );
+               mlt_frame a_frame = NULL;
+
+               if ( producer == NULL )
+               {
+                       char *background = mlt_properties_get( properties, "background" );
+                       mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+                       producer = mlt_factory_producer( profile, "fezzik", background );
+                       mlt_properties_set_data( properties, "producer", producer, 0, (mlt_destructor)mlt_producer_close, NULL );
+               }
+
+               if ( transition == NULL )
+               {
+                       mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) );
+                       transition = mlt_factory_transition( profile, "affine", NULL );
+                       mlt_properties_set_data( properties, "transition", transition, 0, (mlt_destructor)mlt_transition_close, NULL );
+               }
+
+               if ( producer != NULL && transition != NULL )
+               {
+                       char *name = mlt_properties_get( properties, "_unique_id" );
+                       mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( this ), name );
+                       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( this );
+                       double consumer_ar = mlt_properties_get_double( frame_properties, "consumer_aspect_ratio" );
+                       mlt_properties_set_position( MLT_TRANSITION_PROPERTIES( transition ), "in", mlt_filter_get_in( filter ) );
+                       mlt_properties_set_position( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_filter_get_out( filter ) );
+                       mlt_producer_seek( producer, position );
+                       mlt_frame_set_position( this, position );
+                       mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." );
+                       mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "transition." );
+                       mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &a_frame, 0 );
+                       mlt_properties_set( MLT_FRAME_PROPERTIES( a_frame ), "rescale.interp", "nearest" );
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame ), "distort", 1 );
+
+                       // Special case - aspect_ratio = 0
+                       if ( mlt_properties_get_double( frame_properties, "aspect_ratio" ) == 0 )
+                               mlt_properties_set_double( frame_properties, "aspect_ratio", consumer_ar );
+                       if ( mlt_properties_get_double( MLT_FRAME_PROPERTIES( a_frame ), "aspect_ratio" ) == 0 )
+                               mlt_properties_set_double( MLT_FRAME_PROPERTIES( a_frame ), "aspect_ratio", consumer_ar );
+                       mlt_properties_set_double( MLT_FRAME_PROPERTIES( a_frame ), "consumer_aspect_ratio", consumer_ar );
+
+                       mlt_transition_process( transition, a_frame, this );
+                       mlt_frame_get_image( a_frame, image, format, width, height, writable );
+                       mlt_properties_set_data( frame_properties, "affine_frame", a_frame, 0, (mlt_destructor)mlt_frame_close, NULL );
+                       mlt_properties_set_data( frame_properties, "image", *image, *width * *height * 2, NULL, NULL );
+                       mlt_properties_set_data( frame_properties, "alpha", mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL, NULL );
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get a unique name to store the frame position
+       char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+
+       // Assign the current position to the name
+       mlt_properties_set_position( properties, name, mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) );
+
+       // Push the frame filter
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "background", "colour:black" );
+       }
+       return this;
+}
+
+
diff --git a/src/modules/plus/filter_charcoal.c b/src/modules/plus/filter_charcoal.c
new file mode 100644 (file)
index 0000000..e99981a
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * filter_charcoal.c -- charcoal filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static inline int get_Y( uint8_t *pixels, int width, int height, int x, int y )
+{
+       if ( x < 0 || x >= width || y < 0 || y >= height )
+       {
+               return 235;
+       }
+       else
+       {
+               uint8_t *pixel = pixels + y * ( width << 1 ) + ( x << 1 );
+               return *pixel;
+       }
+}
+
+static inline int sqrti( int n )
+{
+       int p = 0;
+       int q = 1;
+       int r = n;
+       int h = 0;
+
+       while( q <= n )
+               q = q << 2;
+
+       while( q != 1 )
+       {
+               q = q >> 2;
+               h = p + q;
+               p = p >> 1;
+               if ( r >= h )
+               {
+                       p = p + q;
+                       r = r - h;
+               }
+       }
+
+       return p;
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               // Get the charcoal scatter value
+               int x_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "x_scatter" );
+               int y_scatter = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "y_scatter" );
+               float scale = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "scale" );
+               float mix = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "mix" );
+               int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" );
+
+               // We'll process pixel by pixel
+               int x = 0;
+               int y = 0;
+
+               // We need to create a new frame as this effect modifies the input
+               uint8_t *temp = mlt_pool_alloc( *width * *height * 2 );
+               uint8_t *p = temp;
+               uint8_t *q = *image;
+
+               // Calculations are carried out on a 3x3 matrix
+               int matrix[ 3 ][ 3 ];
+
+               // Used to carry out the matrix calculations
+               int sum1;
+               int sum2;
+               float sum;
+               int val;
+
+               // Loop for each row
+               for ( y = 0; y < *height; y ++ )
+               {
+                       // Loop for each pixel
+                       for ( x = 0; x < *width; x ++ )
+                       {
+                               // Populate the matrix
+                               matrix[ 0 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y - y_scatter );
+                               matrix[ 0 ][ 1 ] = get_Y( *image, *width, *height, x            , y - y_scatter );
+                               matrix[ 0 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y - y_scatter );
+                               matrix[ 1 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y             );
+                               matrix[ 1 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y             );
+                               matrix[ 2 ][ 0 ] = get_Y( *image, *width, *height, x - x_scatter, y + y_scatter );
+                               matrix[ 2 ][ 1 ] = get_Y( *image, *width, *height, x            , y + y_scatter );
+                               matrix[ 2 ][ 2 ] = get_Y( *image, *width, *height, x + x_scatter, y + y_scatter );
+
+                               // Do calculations
+                               sum1 = (matrix[2][0] - matrix[0][0]) + ( (matrix[2][1] - matrix[0][1]) << 1 ) + (matrix[2][2] - matrix[2][0]);
+                               sum2 = (matrix[0][2] - matrix[0][0]) + ( (matrix[1][2] - matrix[1][0]) << 1 ) + (matrix[2][2] - matrix[2][0]);
+                               sum = scale * sqrti( sum1 * sum1 + sum2 * sum2 );
+
+                               // Assign value
+                               *p ++ = !invert ? ( sum >= 16 && sum <= 235 ? 251 - sum : sum < 16 ? 235 : 16 ) :
+                                                                 ( sum >= 16 && sum <= 235 ? sum : sum < 16 ? 16 : 235 );
+                               q ++;
+                               val = 128 + mix * ( *q ++ - 128 );
+                               val = val < 16 ? 16 : val > 240 ? 240 : val;
+                               *p ++ = val;
+                       }
+               }
+
+               // Return the created image
+               *image = temp;
+
+               // Store new and destroy old
+               mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", *image, *width * *height * 2, mlt_pool_release, NULL );
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the frame filter
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "x_scatter", "1" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "y_scatter", "1" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "scale", "1.5" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "mix", "0" );
+       }
+       return this;
+}
+
diff --git a/src/modules/plus/filter_invert.c b/src/modules/plus/filter_invert.c
new file mode 100644 (file)
index 0000000..4385fc8
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * filter_invert.c -- invert filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+static inline int clamp( int v, int l, int u )
+{
+       return v < l ? l : ( v > u ? u : v );
+}
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the image
+       mlt_filter filter = mlt_frame_pop_service( this );
+       int mask = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "alpha" );
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *format == mlt_image_yuv422 )
+       {
+               uint8_t *p = *image;
+               uint8_t *q = *image + *width * *height * 2;
+               uint8_t *r = *image;
+
+               while ( p != q )
+               {
+                       *p ++ = clamp( 251 - *r ++, 16, 235 );
+                       *p ++ = clamp( 256 - *r ++, 16, 240 );
+               }
+
+               if ( mask )
+               {
+                       uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+                       int size = *width * *height;
+                       memset( alpha, mask, size );
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the frame filter
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+               this->process = filter_process;
+       return this;
+}
+
diff --git a/src/modules/plus/filter_sepia.c b/src/modules/plus/filter_sepia.c
new file mode 100644 (file)
index 0000000..aa20b23
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * filter_sepia.c -- sepia filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the filter
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Get the image
+       int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+       // Only process if we have no error and a valid colour space
+       if ( error == 0 && *image && *format == mlt_image_yuv422 )
+       {
+               // We modify the whole image
+               uint8_t *p = *image;
+               int h = *height;
+               int uneven = *width % 2;
+               int w = ( *width - uneven ) / 2;
+               int t;
+
+               // Get u and v values
+               int u = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "u" );
+               int v = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "v" );
+
+               // Loop through image
+               while( h -- )
+               {
+                       t = w;
+                       while( t -- )
+                       {
+                               p ++;
+                               *p ++ = u;
+                               p ++;
+                               *p ++ = v;
+                       }
+                       if ( uneven )
+                       {
+                               p ++;
+                               *p ++ = u;
+                       }
+               }
+       }
+
+       return error;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Push the frame filter
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = filter_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "u", "75" );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "v", "150" );
+       }
+       return this;
+}
+
diff --git a/src/modules/plus/transition_affine.c b/src/modules/plus/transition_affine.c
new file mode 100644 (file)
index 0000000..7197056
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * transition_affine.c -- affine transformations
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <framework/mlt_transition.h>
+#include <framework/mlt.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+
+/** Calculate real geometry.
+*/
+
+static void geometry_calculate( mlt_transition this, const char *store, struct mlt_geometry_item_s *output, float position )
+{
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
+       int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
+       int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
+       int length = mlt_geometry_get_length( geometry );
+
+       // Allow wrapping
+       if ( !repeat_off && position >= length && length != 0 )
+       {
+               int section = position / length;
+               position -= section * length;
+               if ( !mirror_off && section % 2 == 1 )
+                       position = length - position;
+       }
+
+       // Fetch the key for the position
+       mlt_geometry_fetch( geometry, output, position );
+}
+
+
+static mlt_geometry transition_parse_keys( mlt_transition this, const char *name, const char *store, int normalised_width, int normalised_height )
+{
+       // Get the properties of the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // Try to fetch it first
+       mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
+
+       // Get the in and out position
+       mlt_position in = mlt_transition_get_in( this );
+       mlt_position out = mlt_transition_get_out( this );
+
+       // Determine length and obtain cycle
+       int length = out - in + 1;
+       double cycle = mlt_properties_get_double( properties, "cycle" );
+
+       // Allow a geometry repeat cycle
+       if ( cycle >= 1 )
+               length = cycle;
+       else if ( cycle > 0 )
+               length *= cycle;
+
+       if ( geometry == NULL )
+       {
+               // Get the new style geometry string
+               char *property = mlt_properties_get( properties, name );
+
+               // Create an empty geometries object
+               geometry = mlt_geometry_init( );
+
+               // Parse the geometry if we have one
+               mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
+
+               // Store it
+               mlt_properties_set_data( properties, store, geometry, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+       }
+       else
+       {
+               // Check for updates and refresh if necessary
+               mlt_geometry_refresh( geometry, mlt_properties_get( properties, name ), length, normalised_width, normalised_height );
+       }
+
+       return geometry;
+}
+
+static mlt_geometry composite_calculate( mlt_transition this, struct mlt_geometry_item_s *result, int nw, int nh, float position )
+{
+       // Structures for geometry
+       mlt_geometry start = transition_parse_keys( this, "geometry", "geometries", nw, nh );
+
+       // Do the calculation
+       geometry_calculate( this, "geometries", result, position );
+
+       return start;
+}
+
+static inline float composite_calculate_key( mlt_transition this, const char *name, const char *store, int norm, float position )
+{
+       // Struct for the result
+       struct mlt_geometry_item_s result;
+
+       // Structures for geometry
+       transition_parse_keys( this, name, store, norm, 0 );
+
+       // Do the calculation
+       geometry_calculate( this, store, &result, position );
+
+       return result.x;
+}
+
+typedef struct
+{
+       float matrix[3][3];
+}
+affine_t;
+
+static void affine_init( float this[3][3] )
+{
+       this[0][0] = 1;
+       this[0][1] = 0;
+       this[0][2] = 0;
+       this[1][0] = 0;
+       this[1][1] = 1;
+       this[1][2] = 0;
+       this[2][0] = 0;
+       this[2][1] = 0;
+       this[2][2] = 1;
+}
+
+// Multiply two this affine transform with that
+static void affine_multiply( float this[3][3], float that[3][3] )
+{
+       float output[3][3];
+       int i;
+       int j;
+
+       for ( i = 0; i < 3; i ++ )
+               for ( j = 0; j < 3; j ++ )
+                       output[i][j] = this[i][0] * that[j][0] + this[i][1] * that[j][1] + this[i][2] * that[j][2];
+
+       this[0][0] = output[0][0];
+       this[0][1] = output[0][1];
+       this[0][2] = output[0][2];
+       this[1][0] = output[1][0];
+       this[1][1] = output[1][1];
+       this[1][2] = output[1][2];
+       this[2][0] = output[2][0];
+       this[2][1] = output[2][1];
+       this[2][2] = output[2][2];
+}
+
+// Rotate by a given angle
+static void affine_rotate_x( float this[3][3], float angle )
+{
+       float affine[3][3];
+       affine[0][0] = cos( angle * M_PI / 180 );
+       affine[0][1] = 0 - sin( angle * M_PI / 180 );
+       affine[0][2] = 0;
+       affine[1][0] = sin( angle * M_PI / 180 );
+       affine[1][1] = cos( angle * M_PI / 180 );
+       affine[1][2] = 0;
+       affine[2][0] = 0;
+       affine[2][1] = 0;
+       affine[2][2] = 1;
+       affine_multiply( this, affine );
+}
+
+static void affine_rotate_y( float this[3][3], float angle )
+{
+       float affine[3][3];
+       affine[0][0] = cos( angle * M_PI / 180 );
+       affine[0][1] = 0;
+       affine[0][2] = 0 - sin( angle * M_PI / 180 );
+       affine[1][0] = 0;
+       affine[1][1] = 1;
+       affine[1][2] = 0;
+       affine[2][0] = sin( angle * M_PI / 180 );
+       affine[2][1] = 0;
+       affine[2][2] = cos( angle * M_PI / 180 );
+       affine_multiply( this, affine );
+}
+
+static void affine_rotate_z( float this[3][3], float angle )
+{
+       float affine[3][3];
+       affine[0][0] = 1;
+       affine[0][1] = 0;
+       affine[0][2] = 0;
+       affine[1][0] = 0;
+       affine[1][1] = cos( angle * M_PI / 180 );
+       affine[1][2] = sin( angle * M_PI / 180 );
+       affine[2][0] = 0;
+       affine[2][1] = - sin( angle * M_PI / 180 );
+       affine[2][2] = cos( angle * M_PI / 180 );
+       affine_multiply( this, affine );
+}
+
+static void affine_scale( float this[3][3], float sx, float sy )
+{
+       float affine[3][3];
+       affine[0][0] = sx;
+       affine[0][1] = 0;
+       affine[0][2] = 0;
+       affine[1][0] = 0;
+       affine[1][1] = sy;
+       affine[1][2] = 0;
+       affine[2][0] = 0;
+       affine[2][1] = 0;
+       affine[2][2] = 1;
+       affine_multiply( this, affine );
+}
+
+// Shear by a given value
+static void affine_shear( float this[3][3], float shear_x, float shear_y, float shear_z )
+{
+       float affine[3][3];
+       affine[0][0] = 1;
+       affine[0][1] = tan( shear_x * M_PI / 180 );
+       affine[0][2] = 0;
+       affine[1][0] = tan( shear_y * M_PI / 180 );
+       affine[1][1] = 1;
+       affine[1][2] = tan( shear_z * M_PI / 180 );
+       affine[2][0] = 0;
+       affine[2][1] = 0;
+       affine[2][2] = 1;
+       affine_multiply( this, affine );
+}
+
+static void affine_offset( float this[3][3], int x, int y )
+{
+       this[0][2] += x;
+       this[1][2] += y;
+}
+
+// Obtain the mapped x coordinate of the input
+static inline double MapX( float this[3][3], int x, int y )
+{
+       return this[0][0] * x + this[0][1] * y + this[0][2];
+}
+
+// Obtain the mapped y coordinate of the input
+static inline double MapY( float this[3][3], int x, int y )
+{
+       return this[1][0] * x + this[1][1] * y + this[1][2];
+}
+
+static inline double MapZ( float this[3][3], int x, int y )
+{
+       return this[2][0] * x + this[2][1] * y + this[2][2];
+}
+
+#define MAX( x, y ) x > y ? x : y
+#define MIN( x, y ) x < y ? x : y
+
+static void affine_max_output( float this[3][3], float *w, float *h, float dz )
+{
+       int tlx = MapX( this, -720, 576 ) / dz;
+       int tly = MapY( this, -720, 576 ) / dz;
+       int trx = MapX( this, 720, 576 ) / dz;
+       int try = MapY( this, 720, 576 ) / dz;
+       int blx = MapX( this, -720, -576 ) / dz;
+       int bly = MapY( this, -720, -576 ) / dz;
+       int brx = MapX( this, 720, -576 ) / dz;
+       int bry = MapY( this, 720, -576 ) / dz;
+
+       int max_x;
+       int max_y;
+       int min_x;
+       int min_y;
+
+       max_x = MAX( tlx, trx );
+       max_x = MAX( max_x, blx );
+       max_x = MAX( max_x, brx );
+
+       min_x = MIN( tlx, trx );
+       min_x = MIN( min_x, blx );
+       min_x = MIN( min_x, brx );
+
+       max_y = MAX( tly, try );
+       max_y = MAX( max_y, bly );
+       max_y = MAX( max_y, bry );
+
+       min_y = MIN( tly, try );
+       min_y = MIN( min_y, bly );
+       min_y = MIN( min_y, bry );
+
+       *w = ( float )( max_x - min_x + 1 ) / 1440.0;
+       *h = ( float )( max_y - min_y + 1 ) / 1152.0;
+}
+
+#define IN_RANGE( v, r )       ( v >= - r / 2 && v < r / 2 )
+
+static inline void get_affine( affine_t *affine, mlt_transition this, float position )
+{
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+       int keyed = mlt_properties_get_int( properties, "keyed" );
+       affine_init( affine->matrix );
+
+       if ( keyed == 0 )
+       {
+               float fix_rotate_x = mlt_properties_get_double( properties, "fix_rotate_x" );
+               float fix_rotate_y = mlt_properties_get_double( properties, "fix_rotate_y" );
+               float fix_rotate_z = mlt_properties_get_double( properties, "fix_rotate_z" );
+               float rotate_x = mlt_properties_get_double( properties, "rotate_x" );
+               float rotate_y = mlt_properties_get_double( properties, "rotate_y" );
+               float rotate_z = mlt_properties_get_double( properties, "rotate_z" );
+               float fix_shear_x = mlt_properties_get_double( properties, "fix_shear_x" );
+               float fix_shear_y = mlt_properties_get_double( properties, "fix_shear_y" );
+               float fix_shear_z = mlt_properties_get_double( properties, "fix_shear_z" );
+               float shear_x = mlt_properties_get_double( properties, "shear_x" );
+               float shear_y = mlt_properties_get_double( properties, "shear_y" );
+               float shear_z = mlt_properties_get_double( properties, "shear_z" );
+               float ox = mlt_properties_get_double( properties, "ox" );
+               float oy = mlt_properties_get_double( properties, "oy" );
+
+               affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
+               affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
+               affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
+               affine_shear( affine->matrix,
+                                         fix_shear_x + shear_x * position,
+                                         fix_shear_y + shear_y * position,
+                                         fix_shear_z + shear_z * position );
+               affine_offset( affine->matrix, ox, oy );
+       }
+       else
+       {
+               float rotate_x = composite_calculate_key( this, "rotate_x", "rotate_x_info", 360, position );
+               float rotate_y = composite_calculate_key( this, "rotate_y", "rotate_y_info", 360, position );
+               float rotate_z = composite_calculate_key( this, "rotate_z", "rotate_z_info", 360, position );
+               float shear_x = composite_calculate_key( this, "shear_x", "shear_x_info", 360, position );
+               float shear_y = composite_calculate_key( this, "shear_y", "shear_y_info", 360, position );
+               float shear_z = composite_calculate_key( this, "shear_z", "shear_z_info", 360, position );
+
+               affine_rotate_x( affine->matrix, rotate_x );
+               affine_rotate_y( affine->matrix, rotate_y );
+               affine_rotate_z( affine->matrix, rotate_z );
+               affine_shear( affine->matrix, shear_x, shear_y, shear_z );
+       }
+}
+
+/** Get the image.
+*/
+
+static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Get the b frame from the stack
+       mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
+
+       // Get the transition object
+       mlt_transition this = mlt_frame_pop_service( a_frame );
+
+       // Get the properties of the transition
+       mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+
+       // Get the properties of the a frame
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+
+       // Get the properties of the b frame
+       mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
+
+       // Image, format, width, height and image for the b frame
+       uint8_t *b_image = NULL;
+       mlt_image_format b_format = mlt_image_yuv422;
+       int b_width;
+       int b_height;
+
+       // Get the unique name to retrieve the frame position
+       char *name = mlt_properties_get( properties, "_unique_id" );
+
+       // Assign the current position to the name
+       mlt_position position =  mlt_properties_get_position( a_props, name );
+       mlt_position in = mlt_properties_get_position( properties, "in" );
+       mlt_position out = mlt_properties_get_position( properties, "out" );
+       int mirror = mlt_properties_get_position( properties, "mirror" );
+       int length = out - in + 1;
+
+       // Obtain the normalised width and height from the a_frame
+       int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
+       int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
+
+       double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) ;
+
+       // Structures for geometry
+       struct mlt_geometry_item_s result;
+
+       if ( mirror && position > length / 2 )
+               position = abs( position - length );
+
+       // Fetch the a frame image
+       mlt_frame_get_image( a_frame, image, format, width, height, 1 );
+
+       // Calculate the region now
+       composite_calculate( this, &result, normalised_width, normalised_height, ( float )position );
+
+       // Fetch the b frame image
+       result.w = ( int )( result.w * *width / normalised_width );
+       result.h = ( int )( result.h * *height / normalised_height );
+       result.x = ( int )( result.x * *width / normalised_width );
+       result.y = ( int )( result.y * *height / normalised_height );
+       //result.w -= ( int )abs( result.w ) % 2;
+       //result.x -= ( int )abs( result.x ) % 2;
+       b_width = result.w;
+       b_height = result.h;
+
+       if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( b_props, "aspect_ratio", consumer_ar );
+
+       if ( !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+       {
+               mlt_properties_set( b_props, "rescale.interp", "nearest" );
+               mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
+       }
+       else
+       {
+               mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
+               mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
+       }
+
+       mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
+       mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
+       result.w = b_width;
+       result.h = b_height;
+
+       // Check that both images are of the correct format and process
+       if ( *format == mlt_image_yuv422 && b_format == mlt_image_yuv422 )
+       {
+               register int x, y;
+               register int dx, dy;
+               double dz;
+               float sw, sh;
+
+               // Get values from the transition
+               float scale_x = mlt_properties_get_double( properties, "scale_x" );
+               float scale_y = mlt_properties_get_double( properties, "scale_y" );
+               int scale = mlt_properties_get_int( properties, "scale" );
+
+               uint8_t *p = *image;
+               uint8_t *q = *image;
+
+               int cx = result.x + ( b_width >> 1 );
+               int cy = result.y + ( b_height >> 1 );
+               cx -= cx % 2;
+
+               int lower_x = 0 - cx;
+               int upper_x = *width - cx;
+               int lower_y = 0 - cy;
+               int upper_y = *height - cy;
+
+               int b_stride = b_width << 1;
+               int a_stride = *width << 1;
+               int x_offset = ( int )result.w >> 1;
+               int y_offset = ( int )result.h >> 1;
+
+               uint8_t *alpha = mlt_frame_get_alpha_mask( b_frame );
+               uint8_t *mask = mlt_frame_get_alpha_mask( a_frame );
+               uint8_t *pmask = mask;
+               float mix;
+
+               affine_t affine;
+
+               get_affine( &affine, this, ( float )position );
+
+               q = *image;
+
+               dz = MapZ( affine.matrix, 0, 0 );
+
+               if ( mask == NULL )
+               {
+                       mask = mlt_pool_alloc( *width * *height );
+                       pmask = mask;
+                       memset( mask, 255, *width * *height );
+               }
+
+               if ( ( int )abs( dz * 1000 ) < 25 )
+                       goto getout;
+
+               if ( scale )
+               {
+                       affine_max_output( affine.matrix, &sw, &sh, dz );
+                       affine_scale( affine.matrix, sw, sh );
+               }
+               else if ( scale_x != 0 && scale_y != 0 )
+               {
+                       affine_scale( affine.matrix, scale_x, scale_y );
+               }
+
+               if ( alpha == NULL )
+               {
+                       for ( y = lower_y; y < upper_y; y ++ )
+                       {
+                               p = q;
+
+                               for ( x = lower_x; x < upper_x; x ++ )
+                               {
+                                       dx = MapX( affine.matrix, x, y ) / dz + x_offset;
+                                       dy = MapY( affine.matrix, x, y ) / dz + y_offset;
+
+                                       if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
+                                       {
+                                               pmask ++;
+                                               dx -= dx & 1;
+                                               *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) );
+                                               *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
+                                       }
+                                       else
+                                       {
+                                               p += 2;
+                                               pmask ++;
+                                       }
+                               }
+
+                               q += a_stride;
+                       }
+               }
+               else
+               {
+                       for ( y = lower_y; y < upper_y; y ++ )
+                       {
+                               p = q;
+
+                               for ( x = lower_x; x < upper_x; x ++ )
+                               {
+                                       dx = MapX( affine.matrix, x, y ) / dz + x_offset;
+                                       dy = MapY( affine.matrix, x, y ) / dz + y_offset;
+
+                                       if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
+                                       {
+                                               *pmask ++ = *( alpha + dy * b_width + dx );
+                                               mix = ( float )*( alpha + dy * b_width + dx ) / 255.0;
+                                               dx -= dx & 1;
+                                               *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) );
+                                               p ++;
+                                               *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
+                                               p ++;
+                                       }
+                                       else
+                                       {
+                                               p += 2;
+                                               pmask ++;
+                                       }
+                               }
+
+                               q += a_stride;
+                       }
+               }
+
+getout:
+               a_frame->get_alpha_mask = NULL;
+               mlt_properties_set_data( a_props, "alpha", mask, 0, mlt_pool_release, NULL );
+       }
+
+       return 0;
+}
+
+/** Affine transition processing.
+*/
+
+static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
+{
+       // Get a unique name to store the frame position
+       char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
+
+       // Assign the current position to the name
+       mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
+       mlt_properties_set_position( a_props, name, mlt_frame_get_position( a_frame ) );
+
+       // Push the transition on to the frame
+       mlt_frame_push_service( a_frame, transition );
+
+       // Push the b_frame on to the stack
+       mlt_frame_push_frame( a_frame, b_frame );
+
+       // Push the transition method
+       mlt_frame_push_get_image( a_frame, transition_get_image );
+
+       return a_frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_transition transition = mlt_transition_new( );
+       if ( transition != NULL )
+       {
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sx", 1 );
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sy", 1 );
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
+               mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "geometry", "0,0:100%x100%" );
+               // Inform apps and framework that this is a video only transition
+               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
+               transition->process = transition_process;
+       }
+       return transition;
+}
diff --git a/src/modules/qimage/Makefile b/src/modules/qimage/Makefile
new file mode 100644 (file)
index 0000000..42bdc81
--- /dev/null
@@ -0,0 +1,41 @@
+include ../../../config.mak
+include config.mak
+
+TARGET = ../libmltqimage$(LIBSUF)
+
+OBJS = factory.o producer_qimage.o
+CPPOBJS = qimage_wrapper.o
+
+CFLAGS += -I../..
+CXXFLAGS += $(CFLAGS) $(QTCXXFLAGS) -Wno-deprecated
+
+LDFLAGS = -L../../framework -lmlt
+LDFLAGS += $(QTLIBS)
+LDFLAGS += -lstdc++
+
+ifdef USE_KDE
+LDFLAGS += -lkio
+endif
+
+SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS) $(CPPOBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $(QTCXXFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend config.h config.mak
+
+clean: 
+               rm -f $(OBJS) $(TARGET) $(CPPOBJS)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/qimage/configure b/src/modules/qimage/configure
new file mode 100755 (executable)
index 0000000..7111947
--- /dev/null
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+if [ "$help" = "1" ]
+then
+       cat << EOF
+QImage options:
+
+  --force-qt3            - Force compile against Qt3 if Qt4 is present on the system
+  --qimage-libdir         - Location of QT lib directory [/usr/lib/qt4 or /usr/lib/qt3]
+  --qimage-includedir     - Location of QT include directory [/usr/include/qt4 or /usr/include/qt3]
+  --kde-libdir            - Location of KDE lib directory [/usr/lib]
+  --kde-includedir        - Location of KDE include directory [/usr/include/kde]
+
+EOF
+
+else
+       targetos=$(uname -s)
+       case $targetos in
+       MINGW32*)
+               export LIBSUF=.dll
+               ;;
+       Darwin)
+               export LIBSUF=.dylib
+               ;;
+       Linux|FreeBSD)
+               export LIBSUF=.so
+               ;;
+       *)
+               ;;
+       esac
+
+       qimage_includedir=/usr/include/qt4
+       qimage_libdir=/usr/lib/qt4
+
+       if [ ! -d "$qimage_libdir" -o ! -d "$qimage_includedir" ]
+       then
+               qimage_includedir=/usr/include/qt3
+               qimage_libdir=/usr/lib/qt3
+               kde_includedir=/usr/include/kde
+               kde_libdir=/usr/lib
+               if [ "$KDEDIR" != "" ]
+               then
+                       kde_includedir="$KDEDIR/include"
+                       kde_libdir="$KDEDIR"
+               fi
+       fi
+
+       if [ "$QTDIR" != "" ]
+       then
+               qimage_includedir="$QTDIR/include"
+               qimage_libdir="$QTDIR/lib"
+       fi
+
+       export force_qt3=
+       export qt4_found=
+
+       for i in "$@"
+       do
+               case $i in
+                       --qimage-libdir=* )     qimage_libdir="${i#--qimage-libdir=}" ;;
+                       --qimage-includedir=* ) qimage_includedir="${i#--qimage-includedir=}" ;;
+                       --kde-libdir=* )        kde_libdir="${i#--kde-libdir=}" ;;
+                       --kde-includedir=* )    kde_includedir="${i#--kde-includedir=}" ;;
+                       --force-qt3 )           force_qt3="true" ;;
+               esac
+       done
+
+       pkg-config --exists 'QtGui >= 4'
+       if [ $? -eq 0 ] && [ "$force_qt3" = "" ]
+       then
+               echo "Qt version 4.x detected, will compile Qt4 qimage producer"
+               qt4_found=true
+               echo "#define USE_QT4" > config.h
+               echo "USE_QT4=1" > config.mak
+               echo QTCXXFLAGS=$(pkg-config --cflags QtGui) >> config.mak
+               echo QTLIBS=$(pkg-config --libs QtGui) >> config.mak
+               
+       elif [ -d "$qimage_libdir" -a -d "$qimage_includedir" ]
+       then
+
+               # test if we have a Qt3 or Qt4
+               if [ -f "$qimage_libdir/libQtCore.so" ] || [ -d "$qimage_libdir/QtGui.framework" ] && [ "$force_qt3" = "" ]
+               then
+                       echo "Qt version 4.x detected, will compile Qt4 qimage producer"
+                       qt4_found=true
+               else
+                       echo "Qt version 3.x detected, will compile Qt3 qimage producer"
+               fi
+
+               echo "Include directory: " $qimage_includedir
+
+               echo > config.h
+               echo > config.mak
+               if [ "$qt4_found" != "" ] && [ "$force_qt3" = "" ]
+               then
+                       echo "#define USE_QT4" >> config.h
+                       echo "USE_QT4=1" >> config.mak
+                       if [ -d "$qimage_libdir/QtGui.framework" ]
+                       then
+                               echo QTCXXFLAGS=$(pkg-config --cflags QtGui) >> config.mak
+                               echo QTLIBS=$(pkg-config --libs QtGui) >> config.mak
+                       else
+                               echo QTCXXFLAGS=-I$qimage_includedir >> config.mak
+                               echo QTLIBS=-L$qimage_libdir -lQtGui >> config.mak
+                       fi
+               else 
+                   if [ -d "$kde_includedir" ]
+                   then 
+                           echo "#define USE_KDE" >> config.h
+                           echo "USE_KDE=1" >> config.mak
+                           echo "#define USE_QT3" >> config.h
+                           echo "USE_QT3=1" >> config.mak
+                           echo QTCXXFLAGS=-I$qimage_includedir -I$kde_includedir -DQT_THREAD_SUPPORT >> config.mak
+                           echo QTLIBS=-L$qimage_libdir -L$kde_libdir/lib -lqt-mt >> config.mak
+                   else 
+                           echo "qimage: KDE environment not found - disabling extra image formats"
+                           echo "#define USE_QT3" >> config.h
+                           echo "USE_QT3=1" >> config.mak
+                           echo QTCXXFLAGS=-I$qimage_includedir -DQT_THREAD_SUPPORT>> config.mak
+                           echo QTLIBS=-L$qimage_libdir -lqt-mt >> config.mak
+                   fi
+               fi
+       else
+               echo "qimage: QT environment not found - disabling"
+               touch ../disable-qimage
+       fi
+
+fi
diff --git a/src/modules/qimage/factory.c b/src/modules/qimage/factory.c
new file mode 100644 (file)
index 0000000..61cb0ec
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( producer_type, "qimage", producer_qimage_init );
+}
diff --git a/src/modules/qimage/gpl b/src/modules/qimage/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/qimage/producer_qimage.c b/src/modules/qimage/producer_qimage.c
new file mode 100644 (file)
index 0000000..841b456
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * producer_image.c -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the 
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_cache.h>
+#include "qimage_wrapper.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void load_filenames( producer_qimage this, mlt_properties producer_properties );
+static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
+{
+       producer_qimage this = calloc( sizeof( struct producer_qimage_s ), 1 );
+       if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
+       {
+               mlt_producer producer = &this->parent;
+
+               // Get the properties interface
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+       
+               // Callback registration
+#ifdef USE_KDE
+               init_qimage();
+#endif
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Set the default properties
+               mlt_properties_set( properties, "resource", filename );
+               mlt_properties_set_int( properties, "ttl", 25 );
+               mlt_properties_set_int( properties, "aspect_ratio", 1 );
+               mlt_properties_set_int( properties, "progressive", 1 );
+               
+               // Validate the resource
+               if ( filename )
+                       load_filenames( this, properties );
+               if ( this->count )
+               {
+                       mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+                       if ( frame )
+                       {
+                               mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+                               pthread_mutex_init( &this->mutex, NULL );
+                               mlt_properties_set_data( frame_properties, "producer_qimage", this, 0, NULL, NULL );
+                               mlt_frame_set_position( frame, mlt_producer_position( producer ) );
+                               mlt_properties_set_position( frame_properties, "qimage_position", mlt_producer_position( producer ) );
+                               refresh_qimage( this, frame, 0, 0 );
+                               mlt_frame_close( frame );
+                       }
+               }
+               if ( this->current_width == 0 )
+               {
+                       producer_close( producer );
+                       producer = NULL;
+               }
+               return producer;
+       }
+       free( this );
+       return NULL;
+}
+
+static void load_filenames( producer_qimage this, mlt_properties producer_properties )
+{
+       char *filename = mlt_properties_get( producer_properties, "resource" );
+       this->filenames = mlt_properties_new( );
+
+       // Read xml string
+       if ( strstr( filename, "<svg" ) )
+       {
+               // Generate a temporary file for the svg
+               char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
+               int fd = mkstemp( fullname );
+
+               if ( fd > -1 )
+               {
+                       // Write the svg into the temp file
+                       ssize_t remaining_bytes;
+                       char *xml = filename;
+                       
+                       // Strip leading crap
+                       while ( xml[0] != '<' )
+                               xml++;
+                       
+                       remaining_bytes = strlen( xml );
+                       while ( remaining_bytes > 0 )
+                               remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
+                       close( fd );
+
+                       mlt_properties_set( this->filenames, "0", fullname );
+
+                       // Teehe - when the producer closes, delete the temp file and the space allo
+                       mlt_properties_set_data( producer_properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
+               }
+       }
+       // Obtain filenames
+       else if ( strchr( filename, '%' ) != NULL )
+       {
+               // handle picture sequences
+               int i = mlt_properties_get_int( producer_properties, "begin" );
+               int gap = 0;
+               char full[1024];
+               int keyvalue = 0;
+               char key[ 50 ];
+
+               while ( gap < 100 )
+               {
+                       struct stat buf;
+                       snprintf( full, 1023, filename, i ++ );
+                       if ( stat( full, &buf ) == 0 )
+                       {
+                               sprintf( key, "%d", keyvalue ++ );
+                               mlt_properties_set( this->filenames, key, full );
+                               gap = 0;
+                       }
+                       else
+                       {
+                               gap ++;
+                       }
+               }
+       }
+       else if ( strstr( filename, "/.all." ) != NULL )
+       {
+               char wildcard[ 1024 ];
+               char *dir_name = strdup( filename );
+               char *extension = strrchr( dir_name, '.' );
+
+               *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+               sprintf( wildcard, "*%s", extension );
+
+               mlt_properties_dir_list( this->filenames, dir_name, wildcard, 1 );
+
+               free( dir_name );
+       }
+       else
+       {
+               mlt_properties_set( this->filenames, "0", filename );
+       }
+
+       this->count = mlt_properties_count( this->filenames );
+}
+
+static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer for this frame
+       producer_qimage this = mlt_properties_get_data( properties, "producer_qimage", NULL );
+
+       *width = mlt_properties_get_int( properties, "rescale_width" );
+       *height = mlt_properties_get_int( properties, "rescale_height" );
+
+       // Refresh the image
+       refresh_qimage( this, frame, *width, *height );
+
+       // We need to know the size of the image to clone it
+       int image_size = this->current_width * ( this->current_height + 1 ) * 2;
+       int alpha_size = this->current_width * this->current_height;
+
+       // Get width and height (may have changed during the refresh)
+       *width = mlt_properties_get_int( properties, "width" );
+       *height = mlt_properties_get_int( properties, "height" );
+
+       // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
+       // The fault is not in the design of mlt, but in the implementation of the qimage producer...
+       if ( this->current_image != NULL )
+       {
+               if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
+               {
+                       // Clone the image and the alpha
+                       uint8_t *image_copy = mlt_pool_alloc( image_size );
+                       uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+                       memcpy( image_copy, this->current_image, image_size );
+
+                       // Copy or default the alpha
+                       if ( this->current_alpha )
+                               memcpy( alpha_copy, this->current_alpha, alpha_size );
+                       else
+                               memset( alpha_copy, 255, alpha_size );
+
+                       // Now update properties so we free the copy after
+                       mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+                       mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+                       // We're going to pass the copy on
+                       *buffer = image_copy;
+               }
+               else if ( *format == mlt_image_rgb24a )
+               {
+                       // Clone the image and the alpha
+                       image_size = *width * ( *height + 1 ) * 4;
+                       alpha_size = *width * ( *height + 1 );
+                       uint8_t *image_copy = mlt_pool_alloc( image_size );
+                       uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
+
+                       mlt_convert_yuv422_to_rgb24a(this->current_image, image_copy, (*width)*(*height));
+
+                       // Now update properties so we free the copy after
+                       mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
+                       mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
+
+                       // We're going to pass the copy on
+                       *buffer = image_copy;
+               }
+       }
+       else
+       {
+               // TODO: Review all cases of invalid images
+               *buffer = mlt_pool_alloc( 50 * 50 * 2 );
+               mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
+               *width = 50;
+               *height = 50;
+       }
+
+       // Release references and locks
+       pthread_mutex_unlock( &this->mutex );
+       mlt_cache_item_close( this->image_cache );
+       mlt_cache_item_close( this->alpha_cache );
+
+       return 0;
+}
+
+static uint8_t *producer_get_alpha_mask( mlt_frame this )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( this );
+
+       // Return the alpha mask
+       return mlt_properties_get_data( properties, "alpha", NULL );
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Get the real structure for this producer
+       producer_qimage this = producer->child;
+
+       // Fetch the producers properties
+       mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
+
+       if ( this->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
+               load_filenames( this, producer_properties );
+
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       if ( *frame != NULL && this->count > 0 )
+       {
+               // Obtain properties of frame and producer
+               mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+               // Set the producer on the frame properties
+               mlt_properties_set_data( properties, "producer_qimage", this, 0, NULL, NULL );
+
+               // Update timecode on the frame we're creating
+               mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+               // Ensure that we have a way to obtain the position in the get_image
+               mlt_properties_set_position( properties, "qimage_position", mlt_producer_position( producer ) );
+
+               // Refresh the image
+               refresh_qimage( this, *frame, 0, 0 );
+
+               // Set producer-specific frame properties
+               mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
+               mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
+
+               // Set alpha call back
+               ( *frame )->get_alpha_mask = producer_get_alpha_mask;
+
+               // Push the get_image method
+               mlt_frame_push_get_image( *frame, producer_get_image );
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       producer_qimage this = parent->child;
+       pthread_mutex_destroy( &this->mutex );
+       parent->close = NULL;
+       mlt_producer_close( parent );
+       mlt_properties_close( this->filenames );
+       free( this );
+}
diff --git a/src/modules/qimage/qimage_wrapper.cpp b/src/modules/qimage/qimage_wrapper.cpp
new file mode 100644 (file)
index 0000000..d8722e0
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * qimage_wrapper.cpp -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the 
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "qimage_wrapper.h"
+
+#ifdef USE_QT3
+#include <qimage.h>
+#include <qmutex.h>
+
+#ifdef USE_KDE
+#include <kinstance.h>
+#include <kimageio.h>
+#endif
+
+#endif
+
+
+#ifdef USE_QT4
+#include <QtGui/QImage>
+#include <QtCore/QSysInfo>
+#include <QtCore/QMutex>
+#endif
+
+
+#include <cmath>
+
+extern "C" {
+
+#include <framework/mlt_pool.h>
+#include <framework/mlt_cache.h>
+
+#ifdef USE_KDE
+static KInstance *instance = 0L;
+#endif
+
+static void qimage_delete( void *data )
+{
+       QImage *image = ( QImage * )data;
+       delete image;
+       image = NULL;
+#ifdef USE_KDE
+       if (instance) delete instance;
+       instance = 0L;
+#endif
+}
+
+static QMutex g_mutex;
+
+#ifdef USE_KDE
+void init_qimage()
+{
+       if (!instance) {
+           instance = new KInstance("qimage_prod");
+           KImageIO::registerFormats();
+       }
+}
+#endif
+
+void refresh_qimage( producer_qimage self, mlt_frame frame, int width, int height )
+{
+       // Obtain properties of frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the producer 
+       mlt_producer producer = &self->parent;
+
+       // Obtain properties of producer
+       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+       // restore QImage
+       pthread_mutex_lock( &self->mutex );
+       mlt_cache_item qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
+       QImage *qimage = static_cast<QImage*>( mlt_cache_item_data( qimage_cache, NULL ) );
+
+       // restore scaled image
+       self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" );
+       self->current_image = static_cast<uint8_t*>( mlt_cache_item_data( self->image_cache, NULL ) );
+
+       // restore alpha channel
+       self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" );
+       self->current_alpha = static_cast<uint8_t*>( mlt_cache_item_data( self->alpha_cache, NULL ) );
+
+       // Check if user wants us to reload the image
+       if ( mlt_properties_get_int( producer_props, "force_reload" ) ) 
+       {
+               qimage = NULL;
+               self->current_image = NULL;
+               mlt_properties_set_int( producer_props, "force_reload", 0 );
+       }
+
+       // Obtain the cache flag and structure
+       int use_cache = mlt_properties_get_int( producer_props, "cache" );
+       mlt_properties cache = ( mlt_properties )mlt_properties_get_data( producer_props, "_cache", NULL );
+       int update_cache = 0;
+
+       // Get the time to live for each frame
+       double ttl = mlt_properties_get_int( producer_props, "ttl" );
+
+       // Get the original position of this frame
+       mlt_position position = mlt_properties_get_position( properties, "qimage_position" );
+       position += mlt_producer_get_in( producer );
+
+       // Image index
+       int image_idx = ( int )floor( ( double )position / ttl ) % self->count;
+
+       // Key for the cache
+       char image_key[ 10 ];
+       sprintf( image_key, "%d", image_idx );
+
+       g_mutex.lock();
+
+       // Check if the frame is already loaded
+       if ( use_cache )
+       {
+               if ( cache == NULL )
+               {
+                       cache = mlt_properties_new( );
+                       mlt_properties_set_data( producer_props, "_cache", cache, 0, ( mlt_destructor )mlt_properties_close, NULL );
+               }
+
+               mlt_frame cached = ( mlt_frame )mlt_properties_get_data( cache, image_key, NULL );
+
+               if ( cached )
+               {
+                       self->image_idx = image_idx;
+                       mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+                       self->current_width = mlt_properties_get_int( cached_props, "width" );
+                       self->current_height = mlt_properties_get_int( cached_props, "height" );
+                       mlt_properties_set_int( producer_props, "_real_width", mlt_properties_get_int( cached_props, "real_width" ) );
+                       mlt_properties_set_int( producer_props, "_real_height", mlt_properties_get_int( cached_props, "real_height" ) );
+                       self->current_image = ( uint8_t * )mlt_properties_get_data( cached_props, "image", NULL );
+                       self->current_alpha = ( uint8_t * )mlt_properties_get_data( cached_props, "alpha", NULL );
+
+                       if ( width != 0 && ( width != self->current_width || height != self->current_height ) )
+                               self->current_image = NULL;
+               }
+       }
+
+    // optimization for subsequent iterations on single picture
+       if ( width != 0 && ( image_idx != self->image_idx || width != self->current_width || height != self->current_height ) )
+               self->current_image = NULL;
+       if ( image_idx != self->image_idx )
+               qimage = NULL;
+       if ( qimage == NULL && ( width == 0 || self->current_image == NULL ) )
+       {
+               self->current_image = NULL;
+               self->image_idx = image_idx;
+               qimage = new QImage( mlt_properties_get_value( self->filenames, image_idx ) );
+
+               if ( !qimage->isNull( ) )
+               {
+                       // Store the width/height of the qimage
+                       self->current_width = qimage->width( );
+                       self->current_height = qimage->height( );
+
+                       // Register qimage for destruction and reuse
+                       mlt_cache_item_close( qimage_cache );
+                       mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete );
+                       qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
+
+                       mlt_events_block( producer_props, NULL );
+                       mlt_properties_set_int( producer_props, "_real_width", self->current_width );
+                       mlt_properties_set_int( producer_props, "_real_height", self->current_height );
+                       mlt_events_unblock( producer_props, NULL );
+               }
+               else
+               {
+                       delete qimage;
+                       qimage = NULL;
+               }
+       }
+
+       // If we have a pixbuf and this request specifies a valid dimension and we haven't already got a cached version...
+       if ( qimage && width > 0 && self->current_image == NULL )
+       {
+               char *interps = mlt_properties_get( properties, "rescale.interp" );
+               int interp = 0;
+
+               // QImage has two scaling modes - we'll toggle between them here
+               if ( strcmp( interps, "tiles" ) == 0 )
+                       interp = 1;
+               else if ( strcmp( interps, "hyper" ) == 0 )
+                       interp = 1;
+
+#ifdef USE_QT4
+               // Note - the original qimage is already safe and ready for destruction
+               QImage scaled = interp == 0 ? qimage->scaled( QSize( width, height)) : qimage->scaled( QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+               QImage temp;
+               bool hasAlpha = scaled.hasAlphaChannel();
+               if (hasAlpha)
+                   temp = scaled.convertToFormat(QImage::Format_ARGB32);
+               else 
+                   temp = scaled.convertToFormat(QImage::Format_RGB888);
+#endif
+
+#ifdef USE_QT3
+               // Note - the original qimage is already safe and ready for destruction
+               QImage scaled = interp == 0 ? qimage->scale( width, height, QImage::ScaleFree ) : qimage->smoothScale( width, height, QImage::ScaleFree );
+               QImage temp = scaled.convertDepth( 32 );
+               bool hasAlpha = true;
+#endif
+
+               // Store width and height
+               self->current_width = width;
+               self->current_height = height;
+               
+               // Allocate/define image
+               self->current_image = ( uint8_t * )mlt_pool_alloc( width * ( height + 1 ) * 2 );
+               if ( !use_cache )
+                       mlt_cache_item_close( self->image_cache );
+               mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.image", self->current_image, width * ( height + 1 ) * 2, mlt_pool_release );
+               self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" );
+
+               if (!hasAlpha) {
+                       mlt_convert_rgb24_to_yuv422( temp.bits(), self->current_width, self->current_height, temp.bytesPerLine(), self->current_image ); 
+               }
+               else {
+                       // Allocate the alpha mask
+                       self->current_alpha = ( uint8_t * )mlt_pool_alloc( width * height );
+                       if ( !use_cache )
+                               mlt_cache_item_close( self->alpha_cache );
+                       mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha", self->current_alpha, width * height, mlt_pool_release );
+                       self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" );
+
+#ifdef USE_QT4
+                       if ( QSysInfo::ByteOrder == QSysInfo::BigEndian )
+                               mlt_convert_argb_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine(), self->current_image, self->current_alpha );
+                       else
+                               mlt_convert_bgr24a_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+#endif
+
+#ifdef USE_QT3
+                       // Convert the image
+                       if ( QImage::systemByteOrder( ) == QImage::BigEndian )
+                               mlt_convert_argb_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+                       else
+                               mlt_convert_bgr24a_to_yuv422( temp.bits( ), self->current_width, self->current_height, temp.bytesPerLine( ), self->current_image, self->current_alpha );
+#endif
+               }
+
+               // Ensure we update the cache when we need to
+               update_cache = use_cache;
+       }
+
+       // release references no longer needed
+       mlt_cache_item_close( qimage_cache );
+       if ( width == 0 )
+       {
+               pthread_mutex_unlock( &self->mutex );
+               mlt_cache_item_close( self->image_cache );
+               mlt_cache_item_close( self->alpha_cache );
+       }
+
+       // Set width/height of frame
+       mlt_properties_set_int( properties, "width", self->current_width );
+       mlt_properties_set_int( properties, "height", self->current_height );
+       mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+       mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+
+       if ( update_cache )
+       {
+               mlt_frame cached = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+               mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
+               mlt_properties_set_int( cached_props, "width", self->current_width );
+               mlt_properties_set_int( cached_props, "height", self->current_height );
+               mlt_properties_set_int( cached_props, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
+               mlt_properties_set_int( cached_props, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
+               mlt_properties_set_data( cached_props, "image", self->current_image, self->current_width * ( self->current_height + 1 ) * 2, mlt_pool_release, NULL );
+               mlt_properties_set_data( cached_props, "alpha", self->current_alpha, self->current_width * self->current_height, mlt_pool_release, NULL );
+               mlt_properties_set_data( cache, image_key, cached, 0, ( mlt_destructor )mlt_frame_close, NULL );
+       }
+       g_mutex.unlock();
+}
+
+} // extern "C"
diff --git a/src/modules/qimage/qimage_wrapper.h b/src/modules/qimage/qimage_wrapper.h
new file mode 100644 (file)
index 0000000..9c9243e
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * qimage_wrapper.h -- a QT/QImage based producer for MLT
+ * Copyright (C) 2006 Visual Media
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * NB: This module is designed to be functionally equivalent to the 
+ * gtk2 image loading module so it can be used as replacement.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MLT_QIMAGE_WRAPPER
+#define MLT_QIMAGE_WRAPPER
+
+#include <framework/mlt.h>
+
+#include "config.h"
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct producer_qimage_s
+{
+       struct mlt_producer_s parent;
+       mlt_properties filenames;
+       int count;
+       int image_idx;
+       uint8_t *current_image;
+       uint8_t *current_alpha;
+       int current_width;
+       int current_height;
+       mlt_cache_item image_cache;
+       mlt_cache_item alpha_cache;
+       pthread_mutex_t mutex;
+};
+
+typedef struct producer_qimage_s *producer_qimage;
+
+extern void refresh_qimage( producer_qimage, mlt_frame, int width, int height );
+#ifdef USE_KDE
+extern void init_qimage();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/modules/resample/Makefile b/src/modules/resample/Makefile
new file mode 100644 (file)
index 0000000..faf00a8
--- /dev/null
@@ -0,0 +1,35 @@
+include ../../../config.mak
+
+TARGET = ../libmltresample$(LIBSUF)
+
+OBJS = factory.o \
+          filter_resample.o 
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags samplerate`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs samplerate`
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/resample/configure b/src/modules/resample/configure
new file mode 100755 (executable)
index 0000000..954cb04
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       pkg-config samplerate 2> /dev/null
+       disable_samplerate=$?
+
+       if [ "$disable_samplerate" != "0" ]
+       then
+               echo "- libsamplerate not found: disabling"
+               touch ../disable-resample
+       fi
+       exit 0
+fi
+
diff --git a/src/modules/resample/factory.c b/src/modules/resample/factory.c
new file mode 100644 (file)
index 0000000..c61ec51
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "resample", filter_resample_init );
+}
diff --git a/src/modules/resample/filter_resample.c b/src/modules/resample/filter_resample.c
new file mode 100644 (file)
index 0000000..6838465
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * filter_resample.c -- adjust audio sample frequency
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <samplerate.h>
+#define __USE_ISOC99 1
+#include <math.h>
+
+#define BUFFER_LEN 20480
+#define RESAMPLE_TYPE SRC_SINC_FASTEST
+
+/** Get the audio.
+*/
+
+static int resample_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_audio( frame );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the resample information
+       int output_rate = mlt_properties_get_int( filter_properties, "frequency" );
+       SRC_STATE *state = mlt_properties_get_data( filter_properties, "state", NULL );
+       float *input_buffer = mlt_properties_get_data( filter_properties, "input_buffer", NULL );
+       float *output_buffer = mlt_properties_get_data( filter_properties, "output_buffer", NULL );
+       int channels_avail = *channels;
+       SRC_DATA data;
+       int i;
+
+       // If no resample frequency is specified, default to requested value
+       if ( output_rate == 0 )
+               output_rate = *frequency;
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+       // Duplicate channels as necessary
+       if ( channels_avail < *channels )
+       {
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               int j, k = 0;
+               
+               // Duplicate the existing channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+                               k = ( k + 1 ) % channels_avail;
+                       }
+               }
+               
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+       else if ( channels_avail == 6 && *channels == 2 )
+       {
+               // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               
+               // Drop all but the first *channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+                       new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+               }
+
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+
+       // Return now if no work to do
+       if ( output_rate != *frequency )
+       {
+               float *p = input_buffer;
+               float *end = p + *samples * *channels;
+               int16_t *q = *buffer;
+
+               // Convert to floating point
+               while( p != end )
+                       *p ++ = ( float )( *q ++ ) / 32768.0;
+
+               // Resample
+               data.data_in = input_buffer;
+               data.data_out = output_buffer;
+               data.src_ratio = ( float ) output_rate / ( float ) *frequency;
+               data.input_frames = *samples;
+               data.output_frames = BUFFER_LEN / *channels;
+               data.end_of_input = 0;
+               i = src_process( state, &data );
+               if ( i == 0 )
+               {
+                       if ( data.output_frames_gen > *samples )
+                       {
+                               *buffer = mlt_pool_realloc( *buffer, data.output_frames_gen * *channels * sizeof( int16_t ) );
+                               mlt_properties_set_data( properties, "audio", *buffer, *channels * data.output_frames_gen * 2, mlt_pool_release, NULL );
+                       }
+
+                       *samples = data.output_frames_gen;
+                       *frequency = output_rate;
+
+                       p = output_buffer;
+                       q = *buffer;
+                       end = p + *samples * *channels;
+                       
+                       // Convert from floating back to signed 16bit
+                       while( p != end )
+                       {
+                               if ( *p > 1.0 )
+                                       *p = 1.0;
+                               if ( *p < -1.0 )
+                                       *p = -1.0;
+                               if ( *p > 0 )
+                                       *q ++ = 32767 * *p ++;
+                               else
+                                       *q ++ = 32768 * *p ++;
+                       }
+               }
+               else
+                       fprintf( stderr, "resample_get_audio: %s %d,%d,%d\n", src_strerror( i ), *frequency, *samples, output_rate );
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       if ( mlt_frame_is_test_audio( frame ) == 0 )
+       {
+               mlt_frame_push_audio( frame, this );
+               mlt_frame_push_audio( frame, resample_get_audio );
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               int error;
+               SRC_STATE *state = src_new( RESAMPLE_TYPE, 2 /* channels */, &error );
+               if ( error == 0 )
+               {
+                       void *input_buffer = mlt_pool_alloc( BUFFER_LEN );
+                       void *output_buffer = mlt_pool_alloc( BUFFER_LEN );
+                       this->process = filter_process;
+                       if ( arg != NULL )
+                               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "frequency", atoi( arg ) );
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "channels", 2 );
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "state", state, 0, (mlt_destructor)src_delete, NULL );
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "input_buffer", input_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+                       mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+               }
+               else
+               {
+                       fprintf( stderr, "filter_resample_init: %s\n", src_strerror( error ) );
+               }
+       }
+       return this;
+}
diff --git a/src/modules/resample/gpl b/src/modules/resample/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/sdl/Makefile b/src/modules/sdl/Makefile
new file mode 100644 (file)
index 0000000..e2b5b36
--- /dev/null
@@ -0,0 +1,52 @@
+include ../../../config.mak
+
+include config.mak
+
+TARGET = ../libmltsdl$(LIBSUF)
+
+OBJS = factory.o \
+          consumer_sdl.o \
+          consumer_sdl_preview.o \
+          consumer_sdl_still.o 
+
+ifeq ($(targetos),Darwin)
+CFLAGS += -ObjC
+LDFLAGS += -lobjc -framework Foundation
+else
+LDFLAGS += -lX11
+endif
+
+CFLAGS += -I../..
+CFLAGS += `sdl-config --cflags`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `sdl-config --libs`
+
+ifeq ($(WITH_SDL_IMAGE),1)
+OBJS += producer_sdl_image.o
+CFLAGS += -DWITH_SDL_IMAGE
+LDFLAGS += -lSDL_image
+endif
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/sdl/configure b/src/modules/sdl/configure
new file mode 100755 (executable)
index 0000000..43b90d9
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       sdl-config --version > /dev/null 2>&1
+       disable_sdl=$?
+
+       if [ "$disable_sdl" = "0" ]
+       then
+               echo > config.mak
+               image=`sdl-config --prefix`/include/SDL/SDL_image.h
+               if [ -f "$image" ]
+               then
+                       echo "WITH_SDL_IMAGE=1" >> config.mak
+               fi
+       else
+               echo "- sdl development libs not found: disabling"
+               touch ../disable-sdl
+       fi
+       exit 0
+fi
+
diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c
new file mode 100644 (file)
index 0000000..9870e44
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ * consumer_sdl.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_filter.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+#include <sys/time.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+       struct mlt_consumer_s parent;
+       mlt_properties properties;
+       mlt_deque queue;
+       pthread_t thread;
+       int joined;
+       int running;
+       uint8_t audio_buffer[ 4096 * 10 ];
+       int audio_avail;
+       pthread_mutex_t audio_mutex;
+       pthread_cond_t audio_cond;
+       pthread_mutex_t video_mutex;
+       pthread_cond_t video_cond;
+       int window_width;
+       int window_height;
+       int previous_width;
+       int previous_height;
+       int width;
+       int height;
+       int playing;
+       int sdl_flags;
+       SDL_Surface *sdl_screen;
+       SDL_Overlay *sdl_overlay;
+       SDL_Rect rect;
+       uint8_t *buffer;
+       int bpp;
+       int filtered;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static int consumer_get_dimensions( int *width, int *height );
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+
+/** This is what will be called by the factory - anything can be passed in
+       via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_sdl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the consumer object
+       consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+
+       // If no malloc'd and consumer init ok
+       if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+       {
+               // Create the queue
+               this->queue = mlt_deque_init( );
+
+               // Get the parent consumer object
+               mlt_consumer parent = &this->parent;
+
+               // We have stuff to clean up, so override the close method
+               parent->close = consumer_close;
+
+               // get a handle on properties
+               mlt_service service = MLT_CONSUMER_SERVICE( parent );
+               this->properties = MLT_SERVICE_PROPERTIES( service );
+
+               // Set the default volume
+               mlt_properties_set_double( this->properties, "volume", 1.0 );
+
+               // This is the initialisation of the consumer
+               pthread_mutex_init( &this->audio_mutex, NULL );
+               pthread_cond_init( &this->audio_cond, NULL);
+               pthread_mutex_init( &this->video_mutex, NULL );
+               pthread_cond_init( &this->video_cond, NULL);
+               
+               // Default scaler (for now we'll use nearest)
+               mlt_properties_set( this->properties, "rescale", "nearest" );
+
+               // Default buffer for low latency
+               mlt_properties_set_int( this->properties, "buffer", 1 );
+
+               // Default progressive true
+               mlt_properties_set_int( this->properties, "progressive", 0 );
+
+               // Default audio buffer
+               mlt_properties_set_int( this->properties, "audio_buffer", 512 );
+
+               // Ensure we don't join on a non-running object
+               this->joined = 1;
+               
+               // process actual param
+               if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
+               {
+                       this->width = mlt_properties_get_int( this->properties, "width" );
+                       this->height = mlt_properties_get_int( this->properties, "height" );
+               }
+
+               // Set the sdl flags
+               this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
+
+               // Allow thread to be started/stopped
+               parent->start = consumer_start;
+               parent->stop = consumer_stop;
+               parent->is_stopped = consumer_is_stopped;
+
+               // Register specific events
+               mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
+
+               // Return the consumer produced
+               return parent;
+       }
+
+       // malloc or consumer init failed
+       free( this );
+
+       // Indicate failure
+       return NULL;
+}
+
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( SDL_Event * )args[ 0 ] );
+}
+
+int consumer_start( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+
+       if ( !this->running )
+       {
+               int video_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "video_off" );
+               int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+               int display_off = video_off | preview_off;
+               int audio_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" );
+               int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+               consumer_stop( parent );
+
+               this->running = 1;
+               this->joined = 0;
+
+               if ( mlt_properties_get_int( this->properties, "width" ) > 0 )
+                       this->width = mlt_properties_get_int( this->properties, "width" );
+               if ( mlt_properties_get_int( this->properties, "height" ) > 0 )
+                       this->height = mlt_properties_get_int( this->properties, "height" );
+
+               this->bpp = mlt_properties_get_int( this->properties, "bpp" );
+
+               // Attach a colour space converter
+               if ( preview_off && !this->filtered )
+               {
+                       mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( parent ) );
+                       mlt_filter filter = mlt_factory_filter( profile, "avcolour_space", NULL );
+                       if ( filter )
+                       {
+                               mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "forced", mlt_image_yuv422 );
+                               mlt_service_attach( MLT_CONSUMER_SERVICE( parent ), filter );
+                               mlt_filter_close( filter );
+                       }
+                       this->filtered = 1;
+               }
+       
+               if ( sdl_started == 0 && display_off == 0 )
+               {
+                       if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+                       {
+                               fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+                               return -1;
+                       }
+
+                       SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+                       SDL_EnableUNICODE( 1 );
+               }
+               else if ( display_off == 0 )
+               {
+                       this->sdl_screen = SDL_GetVideoSurface( );
+               }
+
+               if ( audio_off == 0 )
+                       SDL_InitSubSystem( SDL_INIT_AUDIO );
+
+               // Default window size
+               double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+               this->window_width = ( double )this->height * display_ratio;
+               this->window_height = this->height;
+
+               if ( this->sdl_screen == NULL && display_off == 0 )
+               {
+                       if ( mlt_properties_get_int( this->properties, "fullscreen" ) )
+                       {
+                               const SDL_VideoInfo *vi = SDL_GetVideoInfo();
+                               this->window_width = vi->current_w;
+                               this->window_height = vi->current_h;
+                               this->sdl_flags |= SDL_FULLSCREEN;
+                               SDL_ShowCursor( SDL_DISABLE );
+                       }
+                       this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+               }
+
+               pthread_create( &this->thread, NULL, consumer_thread, this );
+       }
+
+       return 0;
+}
+
+int consumer_stop( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       if ( this->joined == 0 )
+       {
+               // Kill the thread and clean up
+               this->joined = 1;
+               this->running = 0;
+               if ( this->thread )
+                       pthread_join( this->thread, NULL );
+
+               // internal cleanup
+               if ( this->sdl_overlay != NULL )
+                       SDL_FreeYUVOverlay( this->sdl_overlay );
+               this->sdl_overlay = NULL;
+
+               if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) )
+               {
+                       pthread_mutex_lock( &this->audio_mutex );
+                       pthread_cond_broadcast( &this->audio_cond );
+                       pthread_mutex_unlock( &this->audio_mutex );
+                       SDL_QuitSubSystem( SDL_INIT_AUDIO );
+               }
+
+               if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" ) == 0 )
+                       SDL_Quit( );
+
+               this->sdl_screen = NULL;
+       }
+
+       return 0;
+}
+
+int consumer_is_stopped( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+       return !this->running;
+}
+
+static int sdl_lock_display( )
+{
+       SDL_Surface *screen = SDL_GetVideoSurface( );
+       return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
+}
+
+static void sdl_unlock_display( )
+{
+       SDL_Surface *screen = SDL_GetVideoSurface( );
+       if ( screen != NULL && SDL_MUSTLOCK( screen ) )
+               SDL_UnlockSurface( screen );
+}
+
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+{
+       consumer_sdl this = udata;
+
+       // Get the volume
+       double volume = mlt_properties_get_double( this->properties, "volume" );
+
+       pthread_mutex_lock( &this->audio_mutex );
+
+       // Block until audio received
+       while ( this->running && len > this->audio_avail )
+               pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+
+       if ( this->audio_avail >= len )
+       {
+               // Place in the audio buffer
+               if ( volume != 1.0 )
+                       SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) );
+               else
+                       memcpy( stream, this->audio_buffer, len );
+
+               // Remove len from the audio available
+               this->audio_avail -= len;
+
+               // Remove the samples
+               memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
+       }
+       else
+       {
+               // Just to be safe, wipe the stream first
+               memset( stream, 0, len );
+
+               // Mix the audio 
+               SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) );
+
+               // No audio left
+               this->audio_avail = 0;
+       }
+
+       // We're definitely playing now
+       this->playing = 1;
+
+       pthread_cond_broadcast( &this->audio_cond );
+       pthread_mutex_unlock( &this->audio_mutex );
+}
+
+static int consumer_play_audio( consumer_sdl this, mlt_frame frame, int init_audio, int *duration )
+{
+       // Get the properties of this consumer
+       mlt_properties properties = this->properties;
+       mlt_audio_format afmt = mlt_audio_pcm;
+
+       // Set the preferred params of the test card signal
+       int channels = mlt_properties_get_int( properties, "channels" );
+       int frequency = mlt_properties_get_int( properties, "frequency" );
+       static int counter = 0;
+
+       int samples = mlt_sample_calculator( mlt_properties_get_double( this->properties, "fps" ), frequency, counter++ );
+       
+       int16_t *pcm;
+       int bytes;
+
+       mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+       *duration = ( ( samples * 1000 ) / frequency );
+
+       if ( mlt_properties_get_int( properties, "audio_off" ) )
+       {
+               this->playing = 1;
+               init_audio = 1;
+               return init_audio;
+       }
+
+       if ( init_audio == 1 )
+       {
+               SDL_AudioSpec request;
+               SDL_AudioSpec got;
+
+               int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );
+
+               // specify audio format
+               memset( &request, 0, sizeof( SDL_AudioSpec ) );
+               this->playing = 0;
+               request.freq = frequency;
+               request.format = AUDIO_S16SYS;
+               request.channels = channels;
+               request.samples = audio_buffer;
+               request.callback = sdl_fill_audio;
+               request.userdata = (void *)this;
+               if ( SDL_OpenAudio( &request, &got ) != 0 )
+               {
+                       fprintf( stderr, "SDL failed to open audio: %s\n", SDL_GetError() );
+                       init_audio = 2;
+               }
+               else if ( got.size != 0 )
+               {
+                       SDL_PauseAudio( 0 );
+                       init_audio = 0;
+               }
+       }
+
+       if ( init_audio == 0 )
+       {
+               mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+               bytes = ( samples * channels * 2 );
+               pthread_mutex_lock( &this->audio_mutex );
+               while ( this->running && bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
+                       pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+               if ( this->running )
+               {
+                       if ( mlt_properties_get_double( properties, "_speed" ) == 1 )
+                               memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
+                       else
+                               memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
+                       this->audio_avail += bytes;
+               }
+               pthread_cond_broadcast( &this->audio_cond );
+               pthread_mutex_unlock( &this->audio_mutex );
+       }
+       else
+       {
+               this->playing = 1;
+       }
+
+       return init_audio;
+}
+
+static int consumer_play_video( consumer_sdl this, mlt_frame frame )
+{
+       // Get the properties of this consumer
+       mlt_properties properties = this->properties;
+
+       mlt_image_format vfmt = mlt_image_yuv422;
+       int width = this->width, height = this->height;
+       uint8_t *image;
+       int changed = 0;
+
+       int video_off = mlt_properties_get_int( properties, "video_off" );
+       int preview_off = mlt_properties_get_int( properties, "preview_off" );
+       mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" );
+       int display_off = video_off | preview_off;
+
+       if ( this->running && display_off == 0 )
+       {
+               // Get the image, width and height
+               mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+               mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+               
+               // Handle events
+               if ( this->sdl_screen != NULL )
+               {
+                       SDL_Event event;
+       
+                       sdl_lock_display( );
+                       changed = consumer_get_dimensions( &this->window_width, &this->window_height );
+                       sdl_unlock_display( );
+
+                       while ( SDL_PollEvent( &event ) )
+                       {
+                               mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
+
+                               switch( event.type )
+                               {
+                                       case SDL_VIDEORESIZE:
+                                               this->window_width = event.resize.w;
+                                               this->window_height = event.resize.h;
+                                               changed = 1;
+                                               break;
+                                       case SDL_QUIT:
+                                               this->running = 0;
+                                               break;
+                                       case SDL_KEYDOWN:
+                                               {
+                                                       mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
+                                                       char keyboard[ 2 ] = " ";
+                                                       void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
+                                                       if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+                                                       {
+                                                               keyboard[ 0 ] = ( char )event.key.keysym.unicode;
+                                                               callback( producer, keyboard );
+                                                       }
+                                               }
+                                               break;
+                               }
+                       }
+               }
+       
+               sdl_lock_display();
+
+               if ( width != this->width || height != this->height )
+               {
+                       if ( this->sdl_overlay != NULL )
+                               SDL_FreeYUVOverlay( this->sdl_overlay );
+                       this->sdl_overlay = NULL;
+               }
+
+               if ( this->running && ( this->sdl_screen == NULL || changed ) )
+               {
+                       // Force an overlay recreation
+                       if ( this->sdl_overlay != NULL )
+                               SDL_FreeYUVOverlay( this->sdl_overlay );
+                       this->sdl_overlay = NULL;
+
+                       // open SDL window with video overlay, if possible
+                       this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, this->bpp, this->sdl_flags );
+                       if ( consumer_get_dimensions( &this->window_width, &this->window_height ) )
+                               this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, this->bpp, this->sdl_flags );
+
+                       uint32_t color = mlt_properties_get_int( this->properties, "window_background" );
+                       SDL_FillRect( this->sdl_screen, NULL, color >> 8 );
+                       SDL_Flip( this->sdl_screen );
+               }
+
+               if ( this->running )
+               {
+                       // Determine window's new display aspect ratio
+                       double this_aspect = ( double )this->window_width / this->window_height;
+
+                       // Get the display aspect ratio
+                       double display_ratio = mlt_properties_get_double( properties, "display_ratio" );
+
+                       // Determine frame's display aspect ratio
+                       double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height;
+
+                       // Store the width and height received
+                       this->width = width;
+                       this->height = height;
+
+                       // If using hardware scaler
+                       if ( mlt_properties_get( properties, "rescale" ) != NULL &&
+                               !strcmp( mlt_properties_get( properties, "rescale" ), "none" ) )
+                       {
+                               // Use hardware scaler to normalise display aspect ratio
+                               this->rect.w = frame_aspect / this_aspect * this->window_width;
+                               this->rect.h = this->window_height;
+                               if ( this->rect.w > this->window_width )
+                               {
+                                       this->rect.w = this->window_width;
+                                       this->rect.h = this_aspect / frame_aspect * this->window_height;
+                               }
+                       }
+                       // Special case optimisation to negate odd effect of sample aspect ratio
+                       // not corresponding exactly with image resolution.
+                       else if ( (int)( this_aspect * 1000 ) == (int)( display_ratio * 1000 ) ) 
+                       {
+                               this->rect.w = this->window_width;
+                               this->rect.h = this->window_height;
+                       }
+                       // Use hardware scaler to normalise sample aspect ratio
+                       else if ( this->window_height * display_ratio > this->window_width )
+                       {
+                               this->rect.w = this->window_width;
+                               this->rect.h = this->window_width / display_ratio;
+                       }
+                       else
+                       {
+                               this->rect.w = this->window_height * display_ratio;
+                               this->rect.h = this->window_height;
+                       }
+                       
+                       this->rect.x = ( this->window_width - this->rect.w ) / 2;
+                       this->rect.y = ( this->window_height - this->rect.h ) / 2;
+                       this->rect.x -= this->rect.x % 2;
+
+                       mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
+                       mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
+                       mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
+                       mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
+
+                       SDL_SetClipRect( this->sdl_screen, &this->rect );
+               }
+
+               if ( this->running && this->sdl_screen != NULL && this->sdl_overlay == NULL )
+               {
+                       SDL_SetClipRect( this->sdl_screen, &this->rect );
+                       this->sdl_overlay = SDL_CreateYUVOverlay( width, height, SDL_YUY2_OVERLAY, this->sdl_screen );
+               }
+
+               if ( this->running && this->sdl_screen != NULL && this->sdl_overlay != NULL )
+               {
+                       this->buffer = this->sdl_overlay->pixels[ 0 ];
+                       if ( SDL_LockYUVOverlay( this->sdl_overlay ) >= 0 )
+                       {
+                               if ( image != NULL )
+                                       memcpy( this->buffer, image, width * height * 2 );
+                               SDL_UnlockYUVOverlay( this->sdl_overlay );
+                               SDL_DisplayYUVOverlay( this->sdl_overlay, &this->sdl_screen->clip_rect );
+                       }
+               }
+
+               sdl_unlock_display();
+       }
+       else if ( this->running )
+       {
+               vfmt = preview_format == mlt_image_none ? mlt_image_rgb24a : preview_format;
+               if ( !video_off )
+                       mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+               mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+       }
+
+       return 0;
+}
+
+static void *video_thread( void *arg )
+{
+       // Identify the arg
+       consumer_sdl this = arg;
+
+       // Obtain time of thread start
+       struct timeval now;
+       int64_t start = 0;
+       int64_t elapsed = 0;
+       struct timespec tm;
+       mlt_frame next = NULL;
+       mlt_properties properties = NULL;
+       double speed = 0;
+
+       // Get real time flag
+       int real_time = mlt_properties_get_int( this->properties, "real_time" );
+
+       // Get the current time
+       gettimeofday( &now, NULL );
+
+       // Determine start time
+       start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;
+
+       while ( this->running )
+       {
+               // Pop the next frame
+               pthread_mutex_lock( &this->video_mutex );
+               next = mlt_deque_pop_front( this->queue );
+               while ( next == NULL && this->running )
+               {
+                       pthread_cond_wait( &this->video_cond, &this->video_mutex );
+                       next = mlt_deque_pop_front( this->queue );
+               }
+               pthread_mutex_unlock( &this->video_mutex );
+
+               if ( !this->running || next == NULL ) break;
+
+               // Get the properties
+               properties =  MLT_FRAME_PROPERTIES( next );
+
+               // Get the speed of the frame
+               speed = mlt_properties_get_double( properties, "_speed" );
+
+               // Get the current time
+               gettimeofday( &now, NULL );
+
+               // Get the elapsed time
+               elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;
+
+               // See if we have to delay the display of the current frame
+               if ( mlt_properties_get_int( properties, "rendered" ) == 1 && this->running )
+               {
+                       // Obtain the scheduled playout time
+                       int64_t scheduled = mlt_properties_get_int( properties, "playtime" );
+
+                       // Determine the difference between the elapsed time and the scheduled playout time
+                       int64_t difference = scheduled - elapsed;
+
+                       // Smooth playback a bit
+                       if ( real_time && ( difference > 20000 && speed == 1.0 ) )
+                       {
+                               tm.tv_sec = difference / 1000000;
+                               tm.tv_nsec = ( difference % 1000000 ) * 500;
+                               nanosleep( &tm, NULL );
+                       }
+
+                       // Show current frame if not too old
+                       if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( this->queue ) < 2 ) )
+                               consumer_play_video( this, next );
+
+                       // If the queue is empty, recalculate start to allow build up again
+                       if ( real_time && ( mlt_deque_count( this->queue ) == 0 && speed == 1.0 ) )
+                       {
+                               gettimeofday( &now, NULL );
+                               start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
+                       }
+               }
+
+               // This frame can now be closed
+               mlt_frame_close( next );
+               next = NULL;
+       }
+
+       if ( next != NULL )
+               mlt_frame_close( next );
+
+       mlt_consumer_stopped( &this->parent );
+
+       return NULL;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+       // Identify the arg
+       consumer_sdl this = arg;
+
+       // Get the consumer
+       mlt_consumer consumer = &this->parent;
+
+       // Convenience functionality
+       int terminate_on_pause = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause" );
+       int terminated = 0;
+
+       // Video thread
+       pthread_t thread;
+
+       // internal intialization
+       int init_audio = 1;
+       int init_video = 1;
+       mlt_frame frame = NULL;
+       mlt_properties properties = NULL;
+       int duration = 0;
+       int64_t playtime = 0;
+       struct timespec tm = { 0, 100000 };
+
+       // Loop until told not to
+       while( !terminated && this->running )
+       {
+               // Get a frame from the attached producer
+               frame = mlt_consumer_rt_frame( consumer );
+
+               // Check for termination
+               if ( terminate_on_pause && frame != NULL )
+                       terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
+
+               // Ensure that we have a frame
+               if ( frame != NULL )
+               {
+                       // Get the frame properties
+                       properties =  MLT_FRAME_PROPERTIES( frame );
+
+                       // Play audio
+                       init_audio = consumer_play_audio( this, frame, init_audio, &duration );
+
+                       // Determine the start time now
+                       if ( this->playing && init_video )
+                       {
+                               // Create the video thread
+                               pthread_create( &thread, NULL, video_thread, this );
+
+                               // Video doesn't need to be initialised any more
+                               init_video = 0;
+                       }
+
+                       // Set playtime for this frame
+                       mlt_properties_set_int( properties, "playtime", playtime );
+
+                       while ( this->running && mlt_deque_count( this->queue ) > 15 )
+                               nanosleep( &tm, NULL );
+
+                       // Push this frame to the back of the queue
+                       pthread_mutex_lock( &this->video_mutex );
+                       mlt_deque_push_back( this->queue, frame );
+                       pthread_cond_broadcast( &this->video_cond );
+                       pthread_mutex_unlock( &this->video_mutex );
+
+                       // Calculate the next playtime
+                       playtime += ( duration * 1000 );
+               }
+       }
+
+       this->running = 0;
+
+       // Kill the video thread
+       if ( init_video == 0 )
+       {
+               pthread_mutex_lock( &this->video_mutex );
+               pthread_cond_broadcast( &this->video_cond );
+               pthread_mutex_unlock( &this->video_mutex );
+               pthread_join( thread, NULL );
+       }
+
+       while( mlt_deque_count( this->queue ) )
+               mlt_frame_close( mlt_deque_pop_back( this->queue ) );
+
+       this->sdl_screen = NULL;
+       this->audio_avail = 0;
+
+       return NULL;
+}
+
+static int consumer_get_dimensions( int *width, int *height )
+{
+       int changed = 0;
+
+       // SDL windows manager structure
+       SDL_SysWMinfo wm;
+
+       // Specify the SDL Version
+       SDL_VERSION( &wm.version );
+
+       // Lock the display
+       //sdl_lock_display();
+
+#ifndef __DARWIN__
+       // Get the wm structure
+       if ( SDL_GetWMInfo( &wm ) == 1 )
+       {
+               // Check that we have the X11 wm
+               if ( wm.subsystem == SDL_SYSWM_X11 ) 
+               {
+                       // Get the SDL window
+                       Window window = wm.info.x11.window;
+
+                       // Get the display session
+                       Display *display = wm.info.x11.display;
+
+                       // Get the window attributes
+                       XWindowAttributes attr;
+                       XGetWindowAttributes( display, window, &attr );
+
+                       // Determine whether window has changed
+                       changed = *width != attr.width || *height != attr.height;
+
+                       // Return width and height
+                       *width = attr.width;
+                       *height = attr.height;
+               }
+       }
+#endif
+
+       // Unlock the display
+       //sdl_unlock_display();
+
+       return changed;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       // Stop the consumer
+       ///mlt_consumer_stop( parent );
+
+       // Now clean up the rest
+       mlt_consumer_close( parent );
+
+       // Close the queue
+       mlt_deque_close( this->queue );
+
+       // Destroy mutexes
+       pthread_mutex_destroy( &this->audio_mutex );
+       pthread_cond_destroy( &this->audio_cond );
+               
+       // Finally clean up this
+       free( this );
+}
diff --git a/src/modules/sdl/consumer_sdl_osx_hack.h b/src/modules/sdl/consumer_sdl_osx_hack.h
new file mode 100644 (file)
index 0000000..dfd76d2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Purpose: A dummy thread object to inform Cocoa that it needs to be thread safe.
+ * Author: Zachary Drew
+ *
+ * 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
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface DummyThread : NSObject
+- init;
+- (void)startThread:(id)arg;
+@end
+
+@implementation DummyThread
+- init
+{
+       [super init];
+       return self;
+}
+- (void)startThread:(id)arg
+{
+       return;
+}
+@end
diff --git a/src/modules/sdl/consumer_sdl_preview.c b/src/modules/sdl/consumer_sdl_preview.c
new file mode 100644 (file)
index 0000000..12b7878
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_producer.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+       struct mlt_consumer_s parent;
+       mlt_consumer active;
+       int ignore_change;
+       mlt_consumer play;
+       mlt_consumer still;
+       pthread_t thread;
+       int joined;
+       int running;
+       int sdl_flags;
+       double last_speed;
+
+       pthread_cond_t refresh_cond;
+       pthread_mutex_t refresh_mutex;
+       int refresh_count;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer this, mlt_frame frame );
+static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer this, SDL_Event *event );
+static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer this, char *name );
+
+mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+       if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+       {
+               // Get the parent consumer object
+               mlt_consumer parent = &this->parent;
+
+               // Get the properties
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+
+               // Get the width and height
+               int width = mlt_properties_get_int( properties, "width" );
+               int height = mlt_properties_get_int( properties, "height" );
+
+               // Process actual param
+               if ( arg == NULL || sscanf( arg, "%dx%d", &width, &height ) == 2 )
+               {
+                       mlt_properties_set_int( properties, "width", width );
+                       mlt_properties_set_int( properties, "height", height );
+               }
+
+               // Create child consumers
+               this->play = mlt_factory_consumer( profile, "sdl", arg );
+               this->still = mlt_factory_consumer( profile, "sdl_still", arg );
+               mlt_properties_set( MLT_CONSUMER_PROPERTIES( parent ), "real_time", "0" );
+               mlt_properties_set( MLT_CONSUMER_PROPERTIES( parent ), "rescale", "nearest" );
+               parent->close = consumer_close;
+               parent->start = consumer_start;
+               parent->stop = consumer_stop;
+               parent->is_stopped = consumer_is_stopped;
+               this->joined = 1;
+               mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->play ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
+               mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->still ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
+               mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->play ), this, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
+               mlt_events_listen( MLT_CONSUMER_PROPERTIES( this->still ), this, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
+               pthread_cond_init( &this->refresh_cond, NULL );
+               pthread_mutex_init( &this->refresh_mutex, NULL );
+               mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), this, "property-changed", ( mlt_listener )consumer_refresh_cb );
+               return parent;
+       }
+       free( this );
+       return NULL;
+}
+
+void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame )
+{
+       consumer_sdl this = parent->child;
+       this->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
+       mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-frame-show", frame, NULL );
+}
+
+static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer parent, SDL_Event *event )
+{
+       mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-sdl-event", event, NULL );
+}
+
+static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name )
+{
+       if ( !strcmp( name, "refresh" ) )
+       {
+               consumer_sdl this = parent->child;
+               pthread_mutex_lock( &this->refresh_mutex );
+               this->refresh_count = this->refresh_count <= 0 ? 1 : this->refresh_count ++;
+               pthread_cond_broadcast( &this->refresh_cond );
+               pthread_mutex_unlock( &this->refresh_mutex );
+       }
+}
+
+static int consumer_start( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+
+       if ( !this->running )
+       {
+               // properties
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+               mlt_properties play = MLT_CONSUMER_PROPERTIES( this->play );
+               mlt_properties still = MLT_CONSUMER_PROPERTIES( this->still );
+
+               char *window_id = mlt_properties_get( properties, "window_id" );
+               char *audio_driver = mlt_properties_get( properties, "audio_driver" );
+               char *video_driver = mlt_properties_get( properties, "video_driver" );
+               char *audio_device = mlt_properties_get( properties, "audio_device" );
+               char *output_display = mlt_properties_get( properties, "output_display" );
+               int progressive = mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" );
+
+               consumer_stop( parent );
+
+               this->running = 1;
+               this->joined = 0;
+               this->last_speed = 1;
+
+               if ( output_display != NULL )
+                       setenv( "DISPLAY", output_display, 1 );
+
+               if ( window_id != NULL )
+                       setenv( "SDL_WINDOWID", window_id, 1 );
+
+               if ( video_driver != NULL )
+                       setenv( "SDL_VIDEODRIVER", video_driver, 1 );
+
+               if ( audio_driver != NULL )
+                       setenv( "SDL_AUDIODRIVER", audio_driver, 1 );
+
+               if ( audio_device != NULL )
+                       setenv( "AUDIODEV", audio_device, 1 );
+
+               if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+               {
+                       fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+                       return -1;
+               }
+
+               SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+               SDL_EnableUNICODE( 1 );
+
+               // Pass properties down
+               mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
+
+               mlt_properties_set_int( play, "progressive", progressive );
+               mlt_properties_set_int( still, "progressive", progressive );
+
+               mlt_properties_pass_list( play, properties, "resize,rescale,width,height,aspect_ratio,display_ratio,volume" );
+               mlt_properties_pass_list( still, properties, "resize,rescale,width,height,aspect_ratio,display_ratio" );
+               mlt_properties_pass_list( play, properties, "deinterlace_method" );
+               mlt_properties_pass_list( still, properties, "deinterlace_method" );
+               mlt_properties_pass_list( play, properties, "preview_off,preview_format,window_background" );
+               mlt_properties_pass_list( still, properties, "preview_off,preview_format,window_background" );
+
+               mlt_properties_pass( play, properties, "play." );
+               mlt_properties_pass( still, properties, "still." );
+
+               mlt_properties_set_data( play, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( still, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( play, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
+               mlt_properties_set_data( still, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
+
+               mlt_properties_set_int( play, "put_mode", 1 );
+               mlt_properties_set_int( still, "put_mode", 1 );
+
+               // Start the still producer just to initialise the gui
+               mlt_consumer_start( this->still );
+               this->active = this->still;
+
+               // Inform child consumers that we control the sdl
+               mlt_properties_set_int( play, "sdl_started", 1 );
+               mlt_properties_set_int( still, "sdl_started", 1 );
+
+               pthread_create( &this->thread, NULL, consumer_thread, this );
+       }
+
+       return 0;
+}
+
+static int consumer_stop( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       if ( this->joined == 0 )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
+               int app_locked = mlt_properties_get_int( properties, "app_locked" );
+               void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
+               void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
+
+               if ( app_locked && unlock ) unlock( );
+
+               // Kill the thread and clean up
+               this->running = 0;
+
+               pthread_mutex_lock( &this->refresh_mutex );
+               pthread_cond_broadcast( &this->refresh_cond );
+               pthread_mutex_unlock( &this->refresh_mutex );
+
+               pthread_join( this->thread, NULL );
+               this->joined = 1;
+
+               if ( app_locked && lock ) lock( );
+
+               SDL_Quit( );
+       }
+
+       return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+       return !this->running;
+}
+
+static void *consumer_thread( void *arg )
+{
+       // Identify the arg
+       consumer_sdl this = arg;
+
+       // Get the consumer
+       mlt_consumer consumer = &this->parent;
+
+       // Get the properties
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+
+       // internal intialization
+       int first = 1;
+       mlt_frame frame = NULL;
+       int last_position = -1;
+
+       // Determine if the application is dealing with the preview
+       int preview_off = mlt_properties_get_int( properties, "preview_off" );
+
+       this->refresh_count = 0;
+
+       // Loop until told not to
+       while( this->running )
+       {
+               // Get a frame from the attached producer
+               frame = mlt_consumer_get_frame( consumer );
+
+               // Ensure that we have a frame
+               if ( this->running && frame != NULL )
+               {
+                       // Get the speed of the frame
+                       double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
+
+                       // Lock during the operation
+                       mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
+
+                       // Get refresh request for the current frame
+                       int refresh = mlt_properties_get_int( properties, "refresh" );
+
+                       // Decrement refresh and clear changed
+                       mlt_events_block( properties, properties );
+                       mlt_properties_set_int( properties, "refresh", 0 );
+                       mlt_events_unblock( properties, properties );
+
+                       // Unlock after the operation
+                       mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
+
+                       // Set the changed property on this frame for the benefit of still
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh );
+
+                       // Make sure the recipient knows that this frame isn't really rendered
+                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 );
+
+                       // Optimisation to reduce latency
+                       if ( speed == 1.0 )
+                       {
+                               if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
+                                       mlt_consumer_purge( this->play );
+                               last_position = mlt_frame_get_position( frame );
+                       }
+                       else
+                       {
+                               //mlt_consumer_purge( this->play );
+                               last_position = -1;
+                       }
+
+                       // If we're not the first frame and both consumers are stopped, then stop ourselves
+                       if ( !first && mlt_consumer_is_stopped( this->play ) && mlt_consumer_is_stopped( this->still ) )
+                       {
+                               this->running = 0;
+                               mlt_frame_close( frame );
+                       }
+                       // Allow a little grace time before switching consumers on speed changes
+                       else if ( this->ignore_change -- > 0 && this->active != NULL && !mlt_consumer_is_stopped( this->active ) )
+                       {
+                               mlt_consumer_put_frame( this->active, frame );
+                       }
+                       // If we aren't playing normally, then use the still
+                       else if ( speed != 1 )
+                       {
+                               if ( !mlt_consumer_is_stopped( this->play ) )
+                                       mlt_consumer_stop( this->play );
+                               if ( mlt_consumer_is_stopped( this->still ) )
+                               {
+                                       this->last_speed = speed;
+                                       this->active = this->still;
+                                       this->ignore_change = 0;
+                                       mlt_consumer_start( this->still );
+                               }
+                               mlt_consumer_put_frame( this->still, frame );
+                       }
+                       // Otherwise use the normal player
+                       else
+                       {
+                               if ( !mlt_consumer_is_stopped( this->still ) )
+                                       mlt_consumer_stop( this->still );
+                               if ( mlt_consumer_is_stopped( this->play ) )
+                               {
+                                       this->last_speed = speed;
+                                       this->active = this->play;
+                                       this->ignore_change = 25;
+                                       mlt_consumer_start( this->play );
+                               }
+                               mlt_consumer_put_frame( this->play, frame );
+                       }
+
+                       // Copy the rectangle info from the active consumer
+                       if ( this->running && preview_off == 0 )
+                       {
+                               mlt_properties active = MLT_CONSUMER_PROPERTIES( this->active );
+                               mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
+                               mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) );
+                               mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) );
+                               mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) );
+                               mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) );
+                               mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
+                       }
+
+                       if ( this->active == this->still )
+                       {
+                               pthread_mutex_lock( &this->refresh_mutex );
+                               if ( this->running && speed == 0 && this->refresh_count <= 0 )
+                                       pthread_cond_wait( &this->refresh_cond, &this->refresh_mutex );
+                               this->refresh_count --;
+                               pthread_mutex_unlock( &this->refresh_mutex );
+                       }
+
+                       // We are definitely not waiting on the first frame any more
+                       first = 0;
+               }
+               else
+               {
+                       if ( frame ) mlt_frame_close( frame );
+                       mlt_consumer_put_frame( this->active, NULL );
+                       this->running = 0;
+               }
+       }
+
+       if ( this->play ) mlt_consumer_stop( this->play );
+       if ( this->still ) mlt_consumer_stop( this->still );
+
+       return NULL;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       // Stop the consumer
+       mlt_consumer_stop( parent );
+
+       // Close the child consumers
+       mlt_consumer_close( this->play );
+       mlt_consumer_close( this->still );
+
+       // Now clean up the rest
+       mlt_consumer_close( parent );
+
+       // Finally clean up this
+       free( this );
+}
diff --git a/src/modules/sdl/consumer_sdl_still.c b/src/modules/sdl/consumer_sdl_still.c
new file mode 100644 (file)
index 0000000..3efd8f4
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <framework/mlt_consumer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_filter.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+#include <sys/time.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_sdl_s *consumer_sdl;
+
+struct consumer_sdl_s
+{
+       struct mlt_consumer_s parent;
+       mlt_properties properties;
+       pthread_t thread;
+       int joined;
+       int running;
+       int window_width;
+       int window_height;
+       int width;
+       int height;
+       int playing;
+       int sdl_flags;
+       SDL_Surface *sdl_screen;
+       SDL_Rect rect;
+       uint8_t *buffer;
+       int last_position;
+       mlt_producer last_producer;
+       int filtered;
+};
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_stop( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer parent );
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+static int consumer_get_dimensions( int *width, int *height );
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
+
+/** This is what will be called by the factory - anything can be passed in
+       via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_sdl_still_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the consumer object
+       consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
+
+       // If no malloc'd and consumer init ok
+       if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
+       {
+               // Get the parent consumer object
+               mlt_consumer parent = &this->parent;
+
+               // get a handle on properties
+               mlt_service service = MLT_CONSUMER_SERVICE( parent );
+               this->properties = MLT_SERVICE_PROPERTIES( service );
+
+               // We have stuff to clean up, so override the close method
+               parent->close = consumer_close;
+
+               // Default scaler (for now we'll use nearest)
+               mlt_properties_set( this->properties, "rescale", "nearest" );
+
+               // We're always going to run this in non-realtime mode
+               mlt_properties_set( this->properties, "real_time", "0" );
+
+               // Default progressive true
+               mlt_properties_set_int( this->properties, "progressive", 1 );
+
+               // Ensure we don't join on a non-running object
+               this->joined = 1;
+               
+               // process actual param
+               if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
+               {
+                       this->width = mlt_properties_get_int( this->properties, "width" );
+                       this->height = mlt_properties_get_int( this->properties, "height" );
+               }
+               else
+               {
+                       mlt_properties_set_int( this->properties, "width", this->width );
+                       mlt_properties_set_int( this->properties, "height", this->height );
+               }
+
+               // Set the sdl flags
+               this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
+
+               // Allow thread to be started/stopped
+               parent->start = consumer_start;
+               parent->stop = consumer_stop;
+               parent->is_stopped = consumer_is_stopped;
+
+               // Register specific events
+               mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
+
+               // Return the consumer produced
+               return parent;
+       }
+
+       // malloc or consumer init failed
+       free( this );
+
+       // Indicate failure
+       return NULL;
+}
+
+static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
+{
+       if ( listener != NULL )
+               listener( owner, this, ( SDL_Event * )args[ 0 ] );
+}
+
+static int consumer_start( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+
+       if ( !this->running )
+       {
+               int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+               int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+               // Attach a colour space converter
+               if ( !this->filtered )
+               {
+                       mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( parent ) );
+                       mlt_filter filter = mlt_factory_filter( profile, "avcolour_space", NULL );
+                       mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "forced", mlt_image_yuv422 );
+                       mlt_service_attach( MLT_CONSUMER_SERVICE( parent ), filter );
+                       mlt_filter_close( filter );
+                       this->filtered = 1;
+               }
+       
+               consumer_stop( parent );
+
+               this->last_position = -1;
+               this->running = 1;
+               this->joined = 0;
+
+               // Allow the user to force resizing to window size
+               this->width = mlt_properties_get_int( this->properties, "width" );
+               this->height = mlt_properties_get_int( this->properties, "height" );
+
+               // Default window size
+               double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+               this->window_width = ( double )this->height * display_ratio;
+               this->window_height = this->height;
+
+               if ( sdl_started == 0 && preview_off == 0 )
+               {
+                       if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 )
+                       {
+                               fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
+                               return -1;
+                       }
+
+                       SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+                       SDL_EnableUNICODE( 1 );
+               }
+               else if ( preview_off == 0 )
+               {
+                       if ( SDL_GetVideoSurface( ) != NULL )
+                       {
+                               this->sdl_screen = SDL_GetVideoSurface( );
+                       }
+               }
+
+               if ( this->sdl_screen == NULL && preview_off == 0 )
+                       this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+
+               pthread_create( &this->thread, NULL, consumer_thread, this );
+       }
+
+       return 0;
+}
+
+static int consumer_stop( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       if ( this->joined == 0 )
+       {
+               int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
+               int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
+
+               // Kill the thread and clean up
+               this->running = 0;
+
+               pthread_join( this->thread, NULL );
+               this->joined = 1;
+
+               if ( sdl_started == 0 && preview_off == 0 )
+                       SDL_Quit( );
+
+               this->sdl_screen = NULL;
+       }
+
+       return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer parent )
+{
+       consumer_sdl this = parent->child;
+       return !this->running;
+}
+
+static int sdl_lock_display( )
+{
+       SDL_Surface *screen = SDL_GetVideoSurface( );
+       return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
+}
+
+static void sdl_unlock_display( )
+{
+       SDL_Surface *screen = SDL_GetVideoSurface( );
+       if ( screen != NULL && SDL_MUSTLOCK( screen ) )
+               SDL_UnlockSurface( screen );
+}
+
+static inline void display_1( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+       // Generate the affine transform scaling values
+       if ( rect.w == 0 || rect.h == 0 ) return;
+       int scale_width = ( width << 16 ) / rect.w;
+       int scale_height = ( height << 16 ) / rect.h;
+       int stride = width * 3;
+       int x, y, row_index;
+       uint8_t *q, *row;
+
+       // Constants defined for clarity and optimisation
+       int scanlength = screen->pitch;
+       uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
+       uint8_t *p;
+
+       // Iterate through the screen using a very basic scaling algorithm
+       for ( y = 0; y < rect.h; y ++ )
+       {
+               p = start;
+               row_index = ( 32768 + scale_height * y ) >> 16;
+               row = image + stride * row_index;
+               for ( x = 0; x < rect.w; x ++ )
+               {
+                       q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+                       *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+               }
+               start += scanlength;
+       }
+}
+
+static inline void display_2( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+       // Generate the affine transform scaling values
+       if ( rect.w == 0 || rect.h == 0 ) return;
+       int scale_width = ( width << 16 ) / rect.w;
+       int scale_height = ( height << 16 ) / rect.h;
+       int stride = width * 3;
+       int x, y, row_index;
+       uint8_t *q, *row;
+
+       // Constants defined for clarity and optimisation
+       int scanlength = screen->pitch / 2;
+       uint16_t *start = ( uint16_t * )screen->pixels + rect.y * scanlength + rect.x;
+       uint16_t *p;
+
+       // Iterate through the screen using a very basic scaling algorithm
+       for ( y = 0; y < rect.h; y ++ )
+       {
+               p = start;
+               row_index = ( 32768 + scale_height * y ) >> 16;
+               row = image + stride * row_index;
+               for ( x = 0; x < rect.w; x ++ )
+               {
+                       q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+                       *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+               }
+               start += scanlength;
+       }
+}
+
+static inline void display_3( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+       // Generate the affine transform scaling values
+       if ( rect.w == 0 || rect.h == 0 ) return;
+       int scale_width = ( width << 16 ) / rect.w;
+       int scale_height = ( height << 16 ) / rect.h;
+       int stride = width * 3;
+       int x, y, row_index;
+       uint8_t *q, *row;
+
+       // Constants defined for clarity and optimisation
+       int scanlength = screen->pitch;
+       uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
+       uint8_t *p;
+       uint32_t pixel;
+
+       // Iterate through the screen using a very basic scaling algorithm
+       for ( y = 0; y < rect.h; y ++ )
+       {
+               p = start;
+               row_index = ( 32768 + scale_height * y ) >> 16;
+               row = image + stride * row_index;
+               for ( x = 0; x < rect.w; x ++ )
+               {
+                       q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+                       pixel = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+                       *p ++ = (pixel & 0xFF0000) >> 16;
+                       *p ++ = (pixel & 0x00FF00) >> 8;
+                       *p ++ = (pixel & 0x0000FF);
+               }
+               start += scanlength;
+       }
+}
+
+static inline void display_4( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
+{
+       // Generate the affine transform scaling values
+       if ( rect.w == 0 || rect.h == 0 ) return;
+       int scale_width = ( width << 16 ) / rect.w;
+       int scale_height = ( height << 16 ) / rect.h;
+       int stride = width * 3;
+       int x, y, row_index;
+       uint8_t *q, *row;
+
+       // Constants defined for clarity and optimisation
+       int scanlength = screen->pitch / 4;
+       uint32_t *start = ( uint32_t * )screen->pixels + rect.y * scanlength + rect.x;
+       uint32_t *p;
+
+       // Iterate through the screen using a very basic scaling algorithm
+       for ( y = 0; y < rect.h; y ++ )
+       {
+               p = start;
+               row_index = ( 32768 + scale_height * y ) >> 16;
+               row = image + stride * row_index;
+               for ( x = 0; x < rect.w; x ++ )
+               {
+                       q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 3 );
+                       *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
+               }
+               start += scanlength;
+       }
+}
+
+static int consumer_play_video( consumer_sdl this, mlt_frame frame )
+{
+       // Get the properties of this consumer
+       mlt_properties properties = this->properties;
+
+       mlt_image_format vfmt = mlt_image_rgb24;
+       int height = this->height;
+       int width = this->width;
+       uint8_t *image = NULL;
+       int changed = 0;
+       double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
+
+       void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
+       void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
+
+       if ( lock != NULL ) lock( );
+
+       sdl_lock_display();
+       
+       // Handle events
+       if ( this->sdl_screen != NULL )
+       {
+               SDL_Event event;
+
+               changed = consumer_get_dimensions( &this->window_width, &this->window_height );
+
+               while ( SDL_PollEvent( &event ) )
+               {
+                       mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
+
+                       switch( event.type )
+                       {
+                               case SDL_VIDEORESIZE:
+                                       this->window_width = event.resize.w;
+                                       this->window_height = event.resize.h;
+                                       changed = 1;
+                                       break;
+                               case SDL_QUIT:
+                                       this->running = 0;
+                                       break;
+                               case SDL_KEYDOWN:
+                                       {
+                                               mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
+                                               char keyboard[ 2 ] = " ";
+                                               void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
+                                               if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
+                                               {
+                                                       keyboard[ 0 ] = ( char )event.key.keysym.unicode;
+                                                       callback( producer, keyboard );
+                                               }
+                                       }
+                                       break;
+                       }
+               }
+       }
+
+       if ( this->sdl_screen == NULL || changed )
+       {
+               // open SDL window 
+               this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+               if ( consumer_get_dimensions( &this->window_width, &this->window_height ) )
+                       this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
+               uint32_t color = mlt_properties_get_int( this->properties, "window_background" );
+               SDL_FillRect( this->sdl_screen, NULL, color >> 8 );
+               changed = 1;
+       }
+       else
+       {
+               changed = 1;
+       }
+
+       if ( changed == 0 &&
+                this->last_position == mlt_frame_get_position( frame ) &&
+                this->last_producer == mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer", NULL ) )
+       {
+               sdl_unlock_display( );
+               if ( unlock != NULL ) unlock( );
+               return 0;
+       }
+
+       // Update last frame shown info
+       this->last_position = mlt_frame_get_position( frame );
+       this->last_producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer", NULL );
+
+       // Get the image, width and height
+       mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+       mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+
+       if ( image != NULL )
+       {
+               char *rescale = mlt_properties_get( properties, "rescale" );
+               if ( rescale != NULL && strcmp( rescale, "none" ) )
+               {
+                       double this_aspect = display_ratio / ( ( double )this->window_width / ( double )this->window_height );
+                       this->rect.w = this_aspect * this->window_width;
+                       this->rect.h = this->window_height;
+                       if ( this->rect.w > this->window_width )
+                       {
+                               this->rect.w = this->window_width;
+                               this->rect.h = ( 1.0 / this_aspect ) * this->window_height;
+                       }
+               }
+               else
+               {
+                       double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height;
+                       this->rect.w = frame_aspect * this->window_height;
+                       this->rect.h = this->window_height;
+                       if ( this->rect.w > this->window_width )
+                       {
+                               this->rect.w = this->window_width;
+                               this->rect.h = ( 1.0 / frame_aspect ) * this->window_width;
+                       }
+               }
+
+               this->rect.x = ( this->window_width - this->rect.w ) / 2;
+               this->rect.y = ( this->window_height - this->rect.h ) / 2;
+
+               mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
+               mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
+               mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
+               mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
+       }
+       
+       if ( !mlt_consumer_is_stopped( &this->parent ) && SDL_GetVideoSurface( ) != NULL && this->sdl_screen != NULL && this->sdl_screen->pixels != NULL )
+       {
+               switch( this->sdl_screen->format->BytesPerPixel )
+               {
+                       case 1:
+                               display_1( this->sdl_screen, this->rect, image, width, height );
+                               break;
+                       case 2:
+                               display_2( this->sdl_screen, this->rect, image, width, height );
+                               break;
+                       case 3:
+                               display_3( this->sdl_screen, this->rect, image, width, height );
+                               break;
+                       case 4:
+                               display_4( this->sdl_screen, this->rect, image, width, height );
+                               break;
+                       default:
+                               fprintf( stderr, "Unsupported video depth %d\n", this->sdl_screen->format->BytesPerPixel );
+                               break;
+               }
+
+               // Flip it into sight
+               SDL_Flip( this->sdl_screen );
+       }
+
+       sdl_unlock_display();
+
+       if ( unlock != NULL ) unlock( );
+
+       return 1;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+       // Identify the arg
+       consumer_sdl this = arg;
+
+       // Get the consumer
+       mlt_consumer consumer = &this->parent;
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+
+       // internal intialization
+       mlt_frame frame = NULL;
+       mlt_image_format vfmt = mlt_image_rgb24a;
+       int height = this->height;
+       int width = this->width;
+       uint8_t *image = NULL;
+
+       // Allow the hosting app to provide the preview
+       int preview_off = mlt_properties_get_int( properties, "preview_off" );
+       mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" );
+
+       // Check if a specific colour space has been requested
+       if ( preview_off && preview_format != mlt_image_none )
+               vfmt = preview_format;
+
+       // Loop until told not to
+       while( this->running )
+       {
+               // Get a frame from the attached producer
+               frame = mlt_consumer_rt_frame( consumer );
+
+               // Ensure that we have a frame
+               if ( this->running && frame != NULL )
+               {
+                       if ( preview_off == 0 )
+                       {
+                               consumer_play_video( this, frame );
+                       }
+                       else
+                       {
+                               mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
+                               mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
+                               mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
+                       }
+                       mlt_frame_close( frame );
+               }
+               else
+               {
+                       if ( frame ) mlt_frame_close( frame );
+                       this->running = 0;
+               }
+       }
+
+       return NULL;
+}
+
+static int consumer_get_dimensions( int *width, int *height )
+{
+       int changed = 0;
+
+       // SDL windows manager structure
+       SDL_SysWMinfo wm;
+
+       // Specify the SDL Version
+       SDL_VERSION( &wm.version );
+
+       // Get the wm structure
+       if ( SDL_GetWMInfo( &wm ) == 1 )
+       {
+#ifndef __DARWIN__
+               // Check that we have the X11 wm
+               if ( wm.subsystem == SDL_SYSWM_X11 ) 
+               {
+                       // Get the SDL window
+                       Window window = wm.info.x11.window;
+
+                       // Get the display session
+                       Display *display = wm.info.x11.display;
+
+                       // Get the window attributes
+                       XWindowAttributes attr;
+                       XGetWindowAttributes( display, window, &attr );
+
+                       // Determine whether window has changed
+                       changed = *width != attr.width || *height != attr.height;
+
+                       // Return width and height
+                       *width = attr.width;
+                       *height = attr.height;
+               }
+#endif
+       }
+
+       return changed;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+       // Get the actual object
+       consumer_sdl this = parent->child;
+
+       // Stop the consumer
+       mlt_consumer_stop( parent );
+
+       // Now clean up the rest
+       mlt_consumer_close( parent );
+
+       // Finally clean up this
+       free( this );
+}
diff --git a/src/modules/sdl/factory.c b/src/modules/sdl/factory.c
new file mode 100644 (file)
index 0000000..68301c2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_sdl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_consumer consumer_sdl_still_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+#ifdef WITH_SDL_IMAGE
+extern mlt_producer producer_sdl_image_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+#endif
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "sdl", consumer_sdl_init );
+       MLT_REGISTER( consumer_type, "sdl_preview", consumer_sdl_preview_init );
+       MLT_REGISTER( consumer_type, "sdl_still", consumer_sdl_still_init );
+#ifdef WITH_SDL_IMAGE
+       MLT_REGISTER( producer_type, "sdl_image", producer_sdl_image_init );
+#endif
+}
diff --git a/src/modules/sdl/producer_sdl_image.c b/src/modules/sdl/producer_sdl_image.c
new file mode 100644 (file)
index 0000000..19f4f4b
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * producer_sdl_image.c -- Image loader which wraps SDL_image
+ * Copyright (C) 2005 Visual Media FX
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * 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 <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_pool.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <math.h>
+
+#include <SDL_image.h>
+
+static int producer_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+       SDL_Surface *surface = mlt_properties_get_data( properties, "surface", NULL );
+       SDL_Surface *converted = NULL;
+       uint8_t *alpha;
+
+       *width = surface->w;
+       *height = surface->h;
+       *format = mlt_image_yuv422;
+       *image = mlt_pool_alloc( *width * *height * 2 );
+       alpha = mlt_pool_alloc( *width * *height );
+
+       if ( surface->format->BitsPerPixel != 32 && surface->format->BitsPerPixel != 24 )
+       {
+               SDL_PixelFormat fmt;
+               fmt.BitsPerPixel = 24;
+               fmt.BytesPerPixel = 3;
+               fmt.Rshift = 16;
+               fmt.Gshift = 8;
+               fmt.Bshift = 0;
+               fmt.Rmask = 0xff << 16;
+               fmt.Gmask = 0xff << 8;
+               fmt.Bmask = 0xff;
+               converted = SDL_ConvertSurface( surface, &fmt, 0 );
+       }
+
+       switch( surface->format->BitsPerPixel )
+       {
+               case 32:
+                       mlt_convert_rgb24a_to_yuv422( surface->pixels, *width, *height, surface->pitch, *image, alpha );
+                       break;
+               case 24:
+                       mlt_convert_rgb24_to_yuv422( surface->pixels, *width, *height, surface->pitch, *image );
+                       memset( alpha, 255, *width * *height );
+                       break;
+               default:
+                       mlt_convert_rgb24_to_yuv422( converted->pixels, *width, *height, converted->pitch, *image );
+                       memset( alpha, 255, *width * *height );
+                       break;
+       }
+
+       if ( converted )
+               SDL_FreeSurface( converted );
+
+       // Update the frame
+       mlt_properties_set_data( properties, "image", *image, *width * *height * 2, mlt_pool_release, NULL );
+       mlt_properties_set_data( properties, "alpha", alpha, *width * *height, mlt_pool_release, NULL );
+       mlt_properties_set_int( properties, "width", *width );
+       mlt_properties_set_int( properties, "height", *height );
+
+       return 0;
+}
+
+static int filter_files( const struct dirent *de )
+{
+       return de->d_name[ 0 ] != '.';
+}
+
+static mlt_properties parse_file_names( char *resource )
+{
+       mlt_properties properties = mlt_properties_new( );
+
+       if ( strstr( resource, "/.all." ) != NULL )
+       {
+               char *dir_name = strdup( resource );
+               char *extension = strrchr( resource, '.' );
+               *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
+               char fullname[ 1024 ];
+               strcpy( fullname, dir_name );
+               struct dirent **de = NULL;
+               int n = scandir( fullname, &de, filter_files, alphasort );
+               int i;
+               struct stat info;
+
+               for (i = 0; i < n; i++ )
+               {
+                       snprintf( fullname, 1023, "%s%s", dir_name, de[i]->d_name );
+                       if ( strstr( fullname, extension ) && lstat( fullname, &info ) == 0 &&
+                               ( S_ISREG( info.st_mode ) || info.st_mode | S_IXUSR ) )
+                       {
+                               char temp[ 20 ];
+                               sprintf( temp, "%d", i );
+                               mlt_properties_set( properties, temp, fullname );
+                       }
+                       free( de[ i ] );
+               }
+
+               free( de );
+               free( dir_name );
+       }
+       else
+       {
+               mlt_properties_set( properties, "0", resource );
+       }
+
+       return properties;
+}
+
+static SDL_Surface *load_image( mlt_producer producer )
+{
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+       char *resource = mlt_properties_get( properties, "resource" );
+       char *last_resource = mlt_properties_get( properties, "_last_resource" );
+       int image_idx = 0;
+       char *this_resource = NULL;
+       double ttl = mlt_properties_get_int( properties, "ttl" );
+       mlt_position position = mlt_producer_position( producer );
+       SDL_Surface *surface = mlt_properties_get_data( properties, "_surface", NULL );
+       mlt_properties filenames = mlt_properties_get_data( properties, "_filenames", NULL );
+
+       if ( filenames == NULL )
+       {
+               filenames = parse_file_names( resource );
+               mlt_properties_set_data( properties, "_surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, 0 );
+       }
+
+       if ( mlt_properties_count( filenames ) ) 
+       {
+               image_idx = ( int )floor( ( double )position / ttl ) % mlt_properties_count( filenames );
+               this_resource = mlt_properties_get_value( filenames, image_idx );
+
+               if ( surface == NULL || last_resource == NULL || strcmp( last_resource, this_resource ) )
+               {
+                       surface = IMG_Load( this_resource );
+                       if ( surface != NULL )
+                       {
+                               surface->refcount ++;
+                               mlt_properties_set_data( properties, "_surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, 0 );
+                               mlt_properties_set( properties, "_last_resource", this_resource );
+                               mlt_properties_set_int( properties, "_real_width", surface->w );
+                               mlt_properties_set_int( properties, "_real_height", surface->h );
+                       }
+               }
+               else if ( surface != NULL )
+               {
+                       surface->refcount ++;
+               }
+       }
+
+       return surface;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Generate a frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       if ( *frame != NULL )
+       {
+               // Create the surface for the current image
+               SDL_Surface *surface = load_image( producer );
+
+               if ( surface != NULL )
+               {
+                       // Obtain properties of frame and producer
+                       mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+                       // Obtain properties of producer
+                       mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+                       // Update timecode on the frame we're creating
+                       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+                       // Set producer-specific frame properties
+                       mlt_properties_set_int( properties, "progressive", 1 );
+                       mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) );
+                       mlt_properties_set_data( properties, "surface", surface, 0, ( mlt_destructor )SDL_FreeSurface, NULL );
+                       mlt_properties_set_int( properties, "real_width", surface->w );
+                       mlt_properties_set_int( properties, "real_height", surface->h );
+
+                       // Push the get_image method
+                       mlt_frame_push_get_image( *frame, producer_get_image );
+               }
+       }
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer producer )
+{
+       producer->close = NULL;
+       mlt_producer_close( producer );
+       free( producer );
+}
+
+mlt_producer producer_sdl_image_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+       mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) );
+       if ( producer != NULL && mlt_producer_init( producer, NULL ) == 0 )
+       {
+               // Get the properties interface
+               mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+       
+               // Callback registration
+               producer->get_frame = producer_get_frame;
+               producer->close = ( mlt_destructor )producer_close;
+
+               // Set the default properties
+               mlt_properties_set( properties, "resource", file );
+               mlt_properties_set( properties, "_resource", "" );
+               mlt_properties_set_double( properties, "aspect_ratio", 1 );
+               mlt_properties_set_int( properties, "ttl", 25 );
+               mlt_properties_set_int( properties, "progressive", 1 );
+               
+               // Validate the resource
+               SDL_Surface *surface = NULL;
+               if ( file && ( surface = load_image( producer ) ) )
+               {
+                       SDL_FreeSurface( surface );
+                       mlt_properties_set_data( properties, "_surface", NULL, 0, NULL, NULL );
+               }
+               else
+               {
+                       producer_close( producer );
+                       producer = NULL;
+               }
+               return producer;
+       }
+       free( producer );
+       return NULL;
+}
+
+
diff --git a/src/modules/sox/Makefile b/src/modules/sox/Makefile
new file mode 100644 (file)
index 0000000..472c591
--- /dev/null
@@ -0,0 +1,35 @@
+include ../../../config.mak
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+include config.mak
+
+TARGET = ../libmltsox$(LIBSUF)
+
+OBJS = factory.o \
+          filter_sox.o 
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/sox/configure b/src/modules/sox/configure
new file mode 100755 (executable)
index 0000000..9df730a
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+       # Determine how to lookup dependencies of executable for  OS
+       targetos=$(uname -s)
+       case $targetos in
+       Darwin)
+               LDD="otool -L"
+               ;;
+       Linux|FreeBSD)
+               LDD="ldd"
+               ;;
+       *)
+               ;;
+       esac
+
+       pkg-config sox
+       if [ $? -eq 0 ]
+       then
+               disable_sox=0
+               echo "CFLAGS += $(pkg-config --cflags sox) -I$(pkg-config --variable=prefix sox)" > config.mak
+               echo "LDFLAGS += $(pkg-config --libs sox)" >> config.mak
+               [ $(pkg-config --modversion sox | cut -d. -f1) -gt 13 ] && echo "CFLAGS += -DSOX14" >> config.mak
+       else
+               which libst-config > /dev/null 2>&1
+               if [ $? -eq 0 ]
+               then
+                       disable_sox=0
+
+                       # determine if we need libsndfile
+                       $LDD $(which sox) | grep libsndfile > /dev/null
+                       [ $? -eq 0 ] && libsndfile="-lsndfile"
+
+                       # determine if we need libsamplerate
+                       $LDD $(which sox) | grep libsamplerate > /dev/null
+                       [ $? -eq 0 ] && libsamplerate="-lsamplerate"
+
+                       echo "CFLAGS += $(libst-config --cflags) -I../.." > config.mak
+                       echo "LDFLAGS += -lst $(libst-config --libs) $libsndfile $libsamplerate" >> config.mak
+               else
+                       sox --version 2> /dev/null | grep 'v14.' > /dev/null
+                       disable_sox=$?
+                       if [ $disable_sox -eq 0 ]
+                       then
+                               LIBDIR=lib
+                               bits=$(uname -m)
+                               case $bits in
+                               x86_64)
+                                       [ -d /usr/lib/lib64 ] && export LIBDIR=lib64 || export LIBDIR=lib
+                                       ;;
+                               *)
+                                       export LIBDIR=lib
+                                       ;;
+                               esac
+
+                               sox=$(which sox)
+                               # chop sox
+                               soxdir=$(dirname $sox)
+                               # chop bin
+                               soxdir=$(dirname $soxdir)
+
+                               # determine if we need libsamplerate
+                               $LDD "$sox" | grep libsamplerate > /dev/null
+                               [ $? -eq 0 ] && libsamplerate="-lsamplerate"
+
+                               # determine if we need libsfx
+                               $LDD $(which sox) | grep libsfx  > /dev/null
+                               [ $? -eq 0 ] && libsfx="-lsfx"
+
+                               echo "CFLAGS += -DSOX14 -I$soxdir/include" > config.mak
+                               echo "LDFLAGS += -L$soxdir/$LIBDIR -lsox $libsfx $libsamplerate" >> config.mak
+                       fi
+               fi
+       fi
+
+       if [ "$disable_sox" != "0" ]
+       then
+               echo "- sox not found: disabling"
+               touch ../disable-sox
+       fi
+
+       exit 0
+fi
diff --git a/src/modules/sox/factory.c b/src/modules/sox/factory.c
new file mode 100644 (file)
index 0000000..048d3bc
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_sox_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "sox", filter_sox_init );
+}
diff --git a/src/modules/sox/filter_sox.c b/src/modules/sox/filter_sox.c
new file mode 100644 (file)
index 0000000..66cc54d
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * filter_sox.c -- apply any number of SOX effects using libst
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_tokeniser.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+// TODO: does not support multiple effects with SoX v14.1.0+
+
+#ifdef SOX14
+#      include <sox.h>
+#      define ST_EOF SOX_EOF
+#      define ST_SUCCESS SOX_SUCCESS
+#      define st_sample_t sox_sample_t
+#      define eff_t sox_effect_t*
+#      define ST_LIB_VERSION_CODE SOX_LIB_VERSION_CODE
+#      define ST_LIB_VERSION SOX_LIB_VERSION
+#      if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,2,0))
+#              define st_size_t size_t
+#      else
+#              define st_size_t sox_size_t
+#      endif
+#      define ST_SIGNED_WORD_TO_SAMPLE(d,clips) SOX_SIGNED_16BIT_TO_SAMPLE(d,clips)
+#      if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+#              define ST_SSIZE_MIN SOX_SAMPLE_MIN
+#      else
+#              define ST_SSIZE_MIN SOX_SSIZE_MIN
+#      endif
+#              define ST_SAMPLE_TO_SIGNED_WORD(d,clips) SOX_SAMPLE_TO_SIGNED_16BIT(d,clips)
+#else
+#      include <st.h>
+#endif
+
+#define BUFFER_LEN 8192
+#define AMPLITUDE_NORM 0.2511886431509580 /* -12dBFS */
+#define AMPLITUDE_MIN 0.00001
+
+/** Compute the mean of a set of doubles skipping unset values flagged as -1
+*/
+static inline double mean( double *buf, int count )
+{
+       double mean = 0;
+       int i;
+       int j = 0;
+       
+       for ( i = 0; i < count; i++ )
+       {
+               if ( buf[ i ] != -1.0 )
+               {
+                       mean += buf[ i ];
+                       j ++;
+               }
+       }
+       if ( j > 0 )
+               mean /= j;
+       
+       return mean;
+}
+
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+static void delete_effect( eff_t effp )
+{
+       free( effp->priv );
+       free( (void*)effp->in_encoding );
+       free( effp );
+}
+#endif
+
+/** Create an effect state instance for a channels
+*/
+static int create_effect( mlt_filter this, char *value, int count, int channel, int frequency )
+{
+       mlt_tokeniser tokeniser = mlt_tokeniser_init();
+       char id[ 256 ];
+       int error = 1;
+
+       // Tokenise the effect specification
+       mlt_tokeniser_parse_new( tokeniser, value, " " );
+       if ( tokeniser->count < 1 )
+               return error;
+
+       // Locate the effect
+       mlt_destructor effect_destructor = mlt_pool_release;
+#ifdef SOX14
+       //fprintf(stderr, "%s: effect %s count %d\n", __FUNCTION__, tokeniser->tokens[0], tokeniser->count );
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+       sox_effect_handler_t const *eff_handle = sox_find_effect( tokeniser->tokens[0] );
+       if (eff_handle == NULL ) return error;
+       eff_t eff = sox_create_effect( eff_handle );
+       effect_destructor = ( mlt_destructor ) delete_effect;
+       sox_encodinginfo_t *enc = calloc( 1, sizeof( sox_encodinginfo_t ) );
+       enc->encoding = SOX_ENCODING_SIGN2;
+       enc->bits_per_sample = 16;
+       eff->in_encoding = eff->out_encoding = enc;
+#else
+       eff_t eff = mlt_pool_alloc( sizeof( sox_effect_t ) );
+       sox_create_effect( eff, sox_find_effect( tokeniser->tokens[0] ) );
+#endif
+       int opt_count = tokeniser->count - 1;
+#else
+       eff_t eff = mlt_pool_alloc( sizeof( struct st_effect ) );
+       int opt_count = st_geteffect_opt( eff, tokeniser->count, tokeniser->tokens );
+#endif
+       
+       // If valid effect
+       if ( opt_count != ST_EOF )
+       {
+               // Supply the effect parameters
+#ifdef SOX14
+               if ( ( * eff->handler.getopts )( eff, opt_count, &tokeniser->tokens[ tokeniser->count > 1 ? 1 : 0  ] ) == ST_SUCCESS )
+#else
+               if ( ( * eff->h->getopts )( eff, opt_count, &tokeniser->tokens[ tokeniser->count - opt_count ] ) == ST_SUCCESS )
+#endif
+               {
+                       // Set the sox signal parameters
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+                       eff->in_signal.rate = frequency;
+                       eff->out_signal.rate = frequency;
+                       eff->in_signal.channels = 1;
+                       eff->out_signal.channels = 1;
+                       eff->in_signal.precision = 16;
+                       eff->out_signal.precision = 16;
+                       eff->in_signal.length = 0;
+                       eff->out_signal.length = 0;
+#else
+                       eff->ininfo.rate = frequency;
+                       eff->outinfo.rate = frequency;
+                       eff->ininfo.channels = 1;
+                       eff->outinfo.channels = 1;
+#endif
+                       
+                       // Start the effect
+#ifdef SOX14
+                       if ( ( * eff->handler.start )( eff ) == ST_SUCCESS )
+#else
+                       if ( ( * eff->h->start )( eff ) == ST_SUCCESS )
+#endif
+                       {
+                               // Construct id
+                               sprintf( id, "_effect_%d_%d", count, channel );
+
+                               // Save the effect state
+                               mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), id, eff, 0, effect_destructor, NULL );
+                               error = 0;
+                       }
+               }
+       }
+       // Some error occurred so delete the temp effect state
+       if ( error == 1 )
+               effect_destructor( eff );
+       
+       mlt_tokeniser_close( tokeniser );
+       
+       return error;
+}
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties of the frame
+       mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Get the filter service
+       mlt_filter filter = mlt_frame_pop_audio( frame );
+
+       // Get the filter properties
+       mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );
+
+       // Get the properties
+       st_sample_t *input_buffer = mlt_properties_get_data( filter_properties, "input_buffer", NULL );
+       st_sample_t *output_buffer = mlt_properties_get_data( filter_properties, "output_buffer", NULL );
+       int channels_avail = *channels;
+       int i; // channel
+       int count = mlt_properties_get_int( filter_properties, "_effect_count" );
+
+       // Get the producer's audio
+       mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples );
+
+       // Duplicate channels as necessary
+       if ( channels_avail < *channels )
+       {
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               int j, k = 0;
+               
+               // Duplicate the existing channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       for ( j = 0; j < *channels; j++ )
+                       {
+                               new_buffer[ ( i * *channels ) + j ] = (*buffer)[ ( i * channels_avail ) + k ];
+                               k = ( k + 1 ) % channels_avail;
+                       }
+               }
+               
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+       else if ( channels_avail == 6 && *channels == 2 )
+       {
+               // Nasty hack for ac3 5.1 audio - may be a cause of failure?
+               int size = *channels * *samples * sizeof( int16_t );
+               int16_t *new_buffer = mlt_pool_alloc( size );
+               
+               // Drop all but the first *channels
+               for ( i = 0; i < *samples; i++ )
+               {
+                       new_buffer[ ( i * *channels ) + 0 ] = (*buffer)[ ( i * channels_avail ) + 2 ];
+                       new_buffer[ ( i * *channels ) + 1 ] = (*buffer)[ ( i * channels_avail ) + 3 ];
+               }
+
+               // Update the audio buffer now - destroys the old
+               mlt_properties_set_data( properties, "audio", new_buffer, size, ( mlt_destructor )mlt_pool_release, NULL );
+               
+               *buffer = new_buffer;
+       }
+
+       // Even though some effects are multi-channel aware, it is not reliable
+       // We must maintain a separate effect state for each channel
+       for ( i = 0; i < *channels; i++ )
+       {
+               char id[ 256 ];
+               sprintf( id, "_effect_0_%d", i );
+               
+               // Get an existing effect state
+               eff_t e = mlt_properties_get_data( filter_properties, id, NULL );
+               
+               // Validate the existing effect state
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,1,0))
+               if ( e != NULL && ( e->in_signal.rate != *frequency || 
+                                                       e->out_signal.rate != *frequency ) )
+#else
+               if ( e != NULL && ( e->ininfo.rate != *frequency || 
+                                                       e->outinfo.rate != *frequency ) )
+#endif
+                       e = NULL;
+               
+               // (Re)Create the effect state
+               if ( e == NULL )
+               {
+                       int j = 0;
+                       
+                       // Reset the count
+                       count = 0;
+       
+                       // Loop over all properties
+                       for ( j = 0; j < mlt_properties_count( filter_properties ); j ++ )
+                       {
+                               // Get the name of this property
+                               char *name = mlt_properties_get_name( filter_properties, j );
+       
+                               // If the name does not contain a . and matches effect
+                               if ( !strncmp( name, "effect", 6 ) )
+                               {
+                                       // Get the effect specification
+                                       char *value = mlt_properties_get( filter_properties, name );
+       
+                                       // Create an instance
+                                       if ( create_effect( filter, value, count, i, *frequency ) == 0 )
+                                               count ++;
+                               }
+                       }
+                       
+                       // Save the number of filters
+                       mlt_properties_set_int( filter_properties, "_effect_count", count );
+                       
+               }
+               if ( *samples > 0 && count > 0 )
+               {
+                       st_sample_t *p = input_buffer;
+                       st_sample_t *end = p + *samples;
+                       int16_t *q = *buffer + i;
+                       st_size_t isamp = *samples;
+                       st_size_t osamp = *samples;
+                       double rms = 0;
+                       int j;
+                       char *normalise = mlt_properties_get( filter_properties, "normalise" );
+                       double normalised_gain = 1.0;
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+                       st_sample_t dummy_clipped_count = 0;
+#endif
+                       
+                       // Convert to sox encoding
+                       while( p != end )
+                       {
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+                               *p = ST_SIGNED_WORD_TO_SAMPLE( *q, dummy_clipped_count );
+#else
+                               *p = ST_SIGNED_WORD_TO_SAMPLE( *q );
+#endif
+                               // Compute rms amplitude while we are accessing each sample
+                               rms += ( double )*p * ( double )*p;
+                               
+                               p ++;
+                               q += *channels;
+                       }
+                       
+                       // Compute final rms amplitude
+                       rms = sqrt( rms / *samples / ST_SSIZE_MIN / ST_SSIZE_MIN );
+                       
+                       if ( normalise )
+                       {
+                               int window = mlt_properties_get_int( filter_properties, "window" );
+                               double *smooth_buffer = mlt_properties_get_data( filter_properties, "smooth_buffer", NULL );
+                               double max_gain = mlt_properties_get_double( filter_properties, "max_gain" );
+                               
+                               // Default the maximum gain factor to 20dBFS
+                               if ( max_gain == 0 )
+                                       max_gain = 10.0;
+                               
+                               // The smoothing buffer prevents radical shifts in the gain level
+                               if ( window > 0 && smooth_buffer != NULL )
+                               {
+                                       int smooth_index = mlt_properties_get_int( filter_properties, "_smooth_index" );
+                                       smooth_buffer[ smooth_index ] = rms;
+                                       
+                                       // Ignore very small values that adversely affect the mean
+                                       if ( rms > AMPLITUDE_MIN )
+                                               mlt_properties_set_int( filter_properties, "_smooth_index", ( smooth_index + 1 ) % window );
+                                       
+                                       // Smoothing is really just a mean over the past N values
+                                       normalised_gain = AMPLITUDE_NORM / mean( smooth_buffer, window );
+                               }
+                               else if ( rms > 0 )
+                               {
+                                       // Determine gain to apply as current amplitude
+                                       normalised_gain = AMPLITUDE_NORM / rms;
+                               }
+                                       
+                               //printf("filter_sox: rms %.3f gain %.3f\n", rms, normalised_gain );
+                               
+                               // Govern the maximum gain
+                               if ( normalised_gain > max_gain )
+                                       normalised_gain = max_gain;
+                       }
+                       
+                       // For each effect
+                       for ( j = 0; j < count; j++ )
+                       {
+                               sprintf( id, "_effect_%d_%d", j, i );
+                               e = mlt_properties_get_data( filter_properties, id, NULL );
+                               
+                               // We better have this guy
+                               if ( e != NULL )
+                               {
+                                       float saved_gain = 1.0;
+                                       
+                                       // XXX: hack to apply the normalised gain level to the vol effect
+#ifdef SOX14
+                                       if ( normalise && strcmp( e->handler.name, "vol" ) == 0 )
+#else
+                                       if ( normalise && strcmp( e->name, "vol" ) == 0 )
+#endif
+                                       {
+                                               float *f = ( float * )( e->priv );
+                                               saved_gain = *f;
+                                               *f = saved_gain * normalised_gain;
+                                       }
+                                       
+                                       // Apply the effect
+#ifdef SOX14
+                                       if ( ( * e->handler.flow )( e, input_buffer, output_buffer, &isamp, &osamp ) == ST_SUCCESS )
+#else
+                                       if ( ( * e->h->flow )( e, input_buffer, output_buffer, &isamp, &osamp ) == ST_SUCCESS )
+#endif
+                                       {
+                                               // Swap input and output buffer pointers for subsequent effects
+                                               p = input_buffer;
+                                               input_buffer = output_buffer;
+                                               output_buffer = p;
+                                       }
+                                       
+                                       // XXX: hack to restore the original vol gain to prevent accumulation
+#ifdef SOX14
+                                       if ( normalise && strcmp( e->handler.name, "vol" ) == 0 )
+#else
+                                       if ( normalise && strcmp( e->name, "vol" ) == 0 )
+#endif
+                                       {
+                                               float *f = ( float * )( e->priv );
+                                               *f = saved_gain;
+                                       }
+                               }
+                       }
+                       
+                       // Convert back to signed 16bit
+                       p = input_buffer;
+                       q = *buffer + i;
+                       end = p + *samples;
+                       while ( p != end )
+                       {
+#if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(13,0,0))
+                               *q = ST_SAMPLE_TO_SIGNED_WORD( *p ++, dummy_clipped_count );
+#else
+                               *q = ST_SAMPLE_TO_SIGNED_WORD( *p ++ );
+#endif
+                               q += *channels;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       if ( mlt_frame_is_test_audio( frame ) == 0 )
+       {
+               // Add the filter to the frame
+               mlt_frame_push_audio( frame, this );
+               mlt_frame_push_audio( frame, filter_get_audio );
+               
+               // Parse the window property and allocate smoothing buffer if needed
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               int window = mlt_properties_get_int( properties, "window" );
+               if ( mlt_properties_get( properties, "smooth_buffer" ) == NULL && window > 1 )
+               {
+                       // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
+                       double *smooth_buffer = (double*) calloc( window, sizeof( double ) );
+                       int i;
+                       for ( i = 0; i < window; i++ )
+                               smooth_buffer[ i ] = -1.0;
+                       mlt_properties_set_data( properties, "smooth_buffer", smooth_buffer, 0, free, NULL );
+               }
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_sox_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               void *input_buffer = mlt_pool_alloc( BUFFER_LEN );
+               void *output_buffer = mlt_pool_alloc( BUFFER_LEN );
+               mlt_properties properties = MLT_FILTER_PROPERTIES( this );
+               
+               this->process = filter_process;
+               
+               if ( arg != NULL )
+                       mlt_properties_set( properties, "effect", arg );
+               mlt_properties_set_data( properties, "input_buffer", input_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+               mlt_properties_set_data( properties, "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL );
+               mlt_properties_set_int( properties, "window", 75 );
+       }
+       return this;
+}
+
+// What to do when a libst internal failure occurs
+void cleanup(void){}
+
+// Is there a build problem with my sox-devel package?
+#ifndef gsm_create
+void gsm_create(void){}
+#endif
+#ifndef gsm_decode
+void gsm_decode(void){}
+#endif
+#ifndef gdm_encode
+void gsm_encode(void){}
+#endif
+#ifndef gsm_destroy
+void gsm_destroy(void){}
+#endif
+#ifndef gsm_option
+void gsm_option(void){}
+#endif
diff --git a/src/modules/valerie/Makefile b/src/modules/valerie/Makefile
new file mode 100644 (file)
index 0000000..9cdd020
--- /dev/null
@@ -0,0 +1,34 @@
+include ../../../config.mak
+
+TARGET = ../libmltvalerie$(LIBSUF)
+
+OBJS = factory.o \
+          consumer_valerie.o
+
+CFLAGS += -I../.. 
+
+LDFLAGS += -L../../valerie -lvalerie
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/valerie/consumer_valerie.c b/src/modules/valerie/consumer_valerie.c
new file mode 100644 (file)
index 0000000..4d4c9a3
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * consumer_valerie.c -- pushes a service via valerie
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author:  Charles Yates <charles.yates@telenet.be>
+ *
+ * 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 <valerie/valerie.h>
+#include <valerie/valerie_remote.h>
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+
+static int consumer_is_stopped( mlt_consumer this );
+static int consumer_start( mlt_consumer this );
+
+/** This is what will be called by the factory
+*/
+
+mlt_consumer consumer_valerie_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the consumer object
+       mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
+
+       // If no malloc'd and consumer init ok
+       if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+       {
+               if ( arg != NULL && strchr( arg, ':' ) )
+               {
+                       char *temp = NULL;
+                       int port = atoi( strchr( arg, ':' ) + 1 );
+                       mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "server", arg );
+                       temp = mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "server" );
+                       *( strchr( temp, ':' ) ) = '\0';
+                       mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "port", port );
+               }
+               else
+               {
+                       mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "server", arg == NULL ? "localhost" : arg );
+                       mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "port", 5250 );
+               }
+
+               mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "unit", 0 );
+               mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "command", "append" );
+
+               // Allow thread to be started/stopped
+               this->start = consumer_start;
+               this->is_stopped = consumer_is_stopped;
+
+               // Return the consumer produced
+               return this;
+       }
+
+       // malloc or consumer init failed
+       free( this );
+
+       // Indicate failure
+       return NULL;
+}
+
+static int consumer_start( mlt_consumer this )
+{
+       // Get the producer service
+       mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
+
+       // Get the properties object
+       mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+
+       // Get all the properties now
+       char *server = mlt_properties_get( properties, "server" );
+       int port = mlt_properties_get_int( properties, "port" );
+       char *cmd = mlt_properties_get( properties, "command" );
+       int unit = mlt_properties_get_int( properties, "unit" );
+       char *title = mlt_properties_get( properties, "title" );
+       char command[ 2048 ];
+
+       // If this is a reuse, then a valerie object will exist
+       valerie connection = mlt_properties_get_data( properties, "connection", NULL );
+
+       // Special case - we can get a doc too...
+       char *doc = mlt_properties_get( properties, "westley" );
+
+       // Set the title if provided
+       if ( service != NULL )
+       {
+               if ( title != NULL )
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", title );
+               else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
+               title = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" );
+       }
+
+       strcpy( command, cmd == NULL ? "" : cmd );
+       if ( strstr( command, "title=" ) == NULL && title != NULL )
+       {
+               strcat( command, " title=\"" );
+               strcat( command, title );
+               strcat( command, "\"" );
+       }
+
+       if ( service != NULL || doc != NULL )
+       {
+               // Initiate the connection if required
+               if ( connection == NULL )
+               {
+                       valerie_parser parser = valerie_parser_init_remote( server, port );
+                       connection = valerie_init( parser );
+                       if ( valerie_connect( connection ) == valerie_ok )
+                       {
+                               mlt_properties_set_data( properties, "connection", connection, 0, ( mlt_destructor )valerie_close, NULL );
+                               mlt_properties_set_data( properties, "parser", parser, 0, ( mlt_destructor )valerie_parser_close, NULL );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Unable to connect to the server at %s:%d\n", server, port );
+                               mlt_properties_set_int( properties, "_error", 1 );
+                               valerie_close( connection );
+                               valerie_parser_close( parser );
+                               connection = NULL;
+                       }
+               }
+
+               // If we have connection, push the service over
+               if ( connection != NULL )
+               {
+                       if ( doc == NULL )
+                       {
+                               int error;
+
+                               // Push the service
+                               error = valerie_unit_push( connection, unit, command, service );
+
+                               // Report error
+                               if ( error != valerie_ok )
+                                       fprintf( stderr, "Push failed on %s:%d %s u%d (%d)\n", server, port, command, unit, error );
+                       }
+                       else
+                       {
+                               // Push the service
+                               int error = valerie_unit_receive( connection, unit, command, doc );
+
+                               // Report error
+                               if ( error != valerie_ok )
+                                       fprintf( stderr, "Send failed on %s:%d %s u%d (%d)\n", server, port, command, unit, error );
+                       }
+               }
+       }
+       
+       mlt_consumer_stop( this );
+       mlt_consumer_stopped( this );
+
+       return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+       return 1;
+}
diff --git a/src/modules/valerie/factory.c b/src/modules/valerie/factory.c
new file mode 100644 (file)
index 0000000..6dcae27
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_valerie_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "valerie", consumer_valerie_init );
+}
diff --git a/src/modules/vmfx/Makefile b/src/modules/vmfx/Makefile
new file mode 100644 (file)
index 0000000..d18f568
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltvmfx$(LIBSUF)
+
+OBJS = factory.o \
+          filter_chroma.o \
+          filter_chroma_hold.o \
+          filter_mono.o \
+          filter_shape.o \
+          producer_pgm.o
+
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/vmfx/factory.c b/src/modules/vmfx/factory.c
new file mode 100644 (file)
index 0000000..3477bb8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_chroma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_chroma_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_shape_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_pgm_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "chroma", filter_chroma_init );
+       MLT_REGISTER( filter_type, "chroma_hold", filter_chroma_hold_init );
+       MLT_REGISTER( filter_type, "threshold", filter_mono_init );
+       MLT_REGISTER( filter_type, "shape", filter_shape_init );
+       MLT_REGISTER( producer_type, "pgm", producer_pgm_init );
+}
diff --git a/src/modules/vmfx/filter_chroma.c b/src/modules/vmfx/filter_chroma.c
new file mode 100644 (file)
index 0000000..f13838b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * filter_chroma.c -- Maps a chroma key to the alpha channel
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <stdlib.h>
+#include <math.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+static inline int in_range( uint8_t v, uint8_t c, int var )
+{
+       return ( ( int )v >= c - var ) && ( ( int )v <= c + var );
+}
+
+static inline uint8_t alpha_value( uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd )
+{
+       if ( odd == 0 )
+               return ( in_range( *( p + 1 ), u, var ) && in_range( *( p + 3 ), v, var ) ) ? 0 : a;
+       else
+               return ( in_range( ( *( p + 1 ) + *( p + 5 ) ) / 2, u, var ) && in_range( ( *( p + 3 ) + *( p + 7 ) ) / 2, v, var ) ) ? 0 : a;
+}
+
+/** Get the images and map the chroma to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_filter this = mlt_frame_pop_service( frame );
+       int variance = 200 * mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "variance" );
+       int32_t key_val = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "key" );
+       uint8_t r = ( key_val >> 24 ) & 0xff;
+       uint8_t g = ( key_val >> 16 ) & 0xff;
+       uint8_t b = ( key_val >>  8 ) & 0xff;
+       uint8_t y, u, v;
+
+       RGB2YUV( r, g, b, y, u, v );
+
+       if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 )
+       {
+               uint8_t *alpha = mlt_frame_get_alpha_mask( frame );
+               uint8_t *p = *image;
+               int size = *width * *height / 2;
+               while ( size -- )
+               {
+                       *alpha = alpha_value( *alpha, p, u, v, variance, 0 );
+                       alpha ++;
+                       *alpha = alpha_value( *alpha, p, u, v, variance, 1 );
+                       alpha ++;
+                       p += 4;
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_service( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_chroma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "key", arg == NULL ? "0x0000ff00" : arg );
+               mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "variance", 0.15 );
+               this->process = filter_process;
+       }
+       return this;
+}
+
diff --git a/src/modules/vmfx/filter_chroma_hold.c b/src/modules/vmfx/filter_chroma_hold.c
new file mode 100644 (file)
index 0000000..07ca4a8
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * filter_chroma.c -- Maps a chroma key to the alpha channel
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <stdlib.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+static inline int in_range( uint8_t v, uint8_t c, int var )
+{
+       return ( ( int )v >= c - var ) && ( ( int )v <= c + var );
+}
+
+static inline uint8_t alpha_value( uint8_t a, uint8_t *p, uint8_t u, uint8_t v, int var, int odd )
+{
+       if ( odd == 0 )
+               return ( in_range( *( p + 1 ), u, var ) && in_range( *( p + 3 ), v, var ) ) ? 0 : a;
+       else
+               return ( in_range( ( *( p + 1 ) + *( p + 5 ) ) / 2, u, var ) && in_range( ( *( p + 3 ) + *( p + 7 ) ) / 2, v, var ) ) ? 0 : a;
+}
+
+/** Get the images and map the chroma to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_filter this = mlt_frame_pop_service( frame );
+       int variance = 200 * mlt_properties_get_double( MLT_FILTER_PROPERTIES( this ), "variance" );
+       int32_t key_val = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "key" );
+       uint8_t r = ( key_val >> 24 ) & 0xff;
+       uint8_t g = ( key_val >> 16 ) & 0xff;
+       uint8_t b = ( key_val >>  8 ) & 0xff;
+       uint8_t y, u, v;
+
+       RGB2YUV( r, g, b, y, u, v );
+
+       if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 )
+       {
+               uint8_t alpha = 0;
+               uint8_t *p = *image;
+               int size = *width * *height / 2;
+               while ( size -- )
+               {
+                       alpha = alpha_value( 255, p, u, v, variance, 0 );
+                       if ( alpha ) 
+                               *( p + 1 )= 128;
+                       alpha = alpha_value( 255, p, u, v, variance, 1 );
+                       if ( alpha ) 
+                               *( p + 3 ) = 128;
+                       p += 4;
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, this );
+       mlt_frame_push_service( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_chroma_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "key", arg == NULL ? "0xc0000000" : arg );
+               mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "variance", 0.15 );
+               this->process = filter_process;
+       }
+       return this;
+}
+
diff --git a/src/modules/vmfx/filter_mono.c b/src/modules/vmfx/filter_mono.c
new file mode 100644 (file)
index 0000000..f7ea11d
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * filter_mono.c -- Arbitrary alpha channel shaping
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <string.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+/** Get the images and apply the luminance of the mask to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int use_alpha = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+       int midpoint = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+       int invert = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) );
+
+       // Render the frame
+       if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 )
+       {
+               uint8_t *p = *image;
+               uint8_t A = invert? 235 : 16;
+               uint8_t B = invert? 16 : 235;
+               int size = *width * *height;
+
+               if ( !use_alpha )
+               {
+                       while( size -- )
+                       {
+                               if ( *p < midpoint )
+                                       *p ++ = A;
+                               else
+                                       *p ++ = B;
+                               *p ++ = 128;
+                       }
+               }
+               else
+               {
+                       uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+                       while( size -- )
+                       {
+                               if ( *alpha ++ < midpoint )
+                                       *p ++ = A;
+                               else
+                                       *p ++ = B;
+                               *p ++ = 128;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       int midpoint = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "midpoint" );
+       int use_alpha = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "use_alpha" );
+       int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "invert" );
+       mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), invert );
+       mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), midpoint );
+       mlt_deque_push_back_int( MLT_FRAME_IMAGE_STACK( frame ), use_alpha );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_mono_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "midpoint", 128 );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "use_alpha", 0 );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "invert", 0 );
+               this->process = filter_process;
+       }
+       return this;
+}
+
diff --git a/src/modules/vmfx/filter_shape.c b/src/modules/vmfx/filter_shape.c
new file mode 100644 (file)
index 0000000..fdd33e4
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * filter_shape.c -- Arbitrary alpha channel shaping
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt.h>
+#include <string.h>
+#include <framework/mlt_factory.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_producer.h>
+#include <framework/mlt_geometry.h>
+
+inline double smoothstep( const double e1, const double e2, const double a )
+{
+    if ( a < e1 ) return 0.0;
+    if ( a > e2 ) return 1.0;
+    double v = ( a - e1 ) / ( e2 - e1 );
+    return ( v * v * ( 3 - 2 * v ) );
+}
+
+/** Get the images and apply the luminance of the mask to the alpha of the frame.
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       // Fetch the data from the stack (mix, mask, filter)
+       double mix = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( this ) );
+       mlt_frame mask = mlt_frame_pop_service( this );
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Obtain the constants
+       double softness = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "softness" );
+       int use_luminance = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "use_luminance" );
+       int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ) * 255;
+
+       // Render the frame
+       if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 && ( !use_luminance || ( int )mix != 1 ) )
+       {
+               // Get the alpha mask of the source
+               uint8_t *alpha = mlt_frame_get_alpha_mask( this );
+
+               // Obtain a scaled/distorted mask to match
+               uint8_t *mask_img = NULL;
+               mlt_image_format mask_fmt = mlt_image_yuv422;
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( mask ), "distort", 1 );
+               mlt_properties_pass_list( MLT_FRAME_PROPERTIES( mask ), MLT_FRAME_PROPERTIES( this ), "deinterlace,deinterlace_method,rescale.interp" );
+
+               if ( mlt_frame_get_image( mask, &mask_img, &mask_fmt, width, height, 0 ) == 0 )
+               {
+                       int size = *width * *height;
+                       uint8_t *p = alpha;
+                       double a = 0;
+                       double b = 0;
+                       if ( !use_luminance )
+                       {
+                               uint8_t *q = mlt_frame_get_alpha_mask( mask );
+                               while( size -- )
+                               {
+                                       a = ( double )*q ++ / 255.0;
+                       b = 1.0 - smoothstep( a, a + softness, mix );
+                                       *p = ( uint8_t )( *p * b ) ^ invert;
+                                       p ++;
+                               }
+                       }
+                       else if ( ( int )mix != 1 )
+                       {
+                               uint8_t *q = mask_img;
+                               // Ensure softness tends to zero has mix tends to 1
+                               softness *= ( 1.0 - mix );
+                               while( size -- )
+                               {
+                                       a = ( ( double )*q - 16 ) / 235.0;
+                       b = smoothstep( a, a + softness, mix );
+                                       *p = ( uint8_t )( *p * b ) ^ invert;
+                                       p ++;
+                                       q += 2;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+       // Obtain the shape instance
+       char *resource = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "resource" );
+       char *last_resource = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_resource" );
+       mlt_producer producer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "instance", NULL );
+
+       // Get the key framed values
+       mlt_geometry alpha = mlt_properties_get_data( MLT_FILTER_PROPERTIES( this ), "_alpha", NULL );
+       char *alpha_data = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "mix" );
+       double alpha_mix = 0.0;
+
+       // Calculate the position and length
+       int position = mlt_frame_get_position( frame ) - mlt_filter_get_in( this );
+       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;
+
+       // If we haven't created the instance or it's changed
+       if ( producer == NULL || strcmp( resource, last_resource ) )
+       {
+               char temp[ 512 ];
+               char *extension = strrchr( resource, '.' );
+
+               // Store the last resource now
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "_resource", resource );
+
+               // This is a hack - the idea is that we can indirectly reference the
+               // luma modules pgm or png images by a short cut like %luma01.pgm - we then replace
+               // the % with the full path to the image and use it if it exists, if not, check for
+               // the file ending in a .png, and failing that, default to a fade in
+               if ( strchr( resource, '%' ) )
+               {
+                       FILE *test;
+                       sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 );
+                       test = fopen( temp, "r" );
+
+                       if ( test == NULL )
+                       {
+                               strcat( temp, ".png" );
+                               test = fopen( temp, "r" );
+                       }
+
+                       if ( test )
+                               fclose( test ); 
+                       else
+                               strcpy( temp, "colour:0x00000080" );
+
+                       resource = temp;
+                       extension = strrchr( resource, '.' );
+               }
+
+               mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( this ) );
+               producer = mlt_factory_producer( profile, NULL, resource );
+               if ( producer != NULL )
+                       mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
+               mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "instance", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+       }
+
+       // Construct the geometry item if needed, otherwise refresh it
+       if ( alpha == NULL )
+       {
+               alpha = mlt_geometry_init( );
+               mlt_properties_set_data( MLT_FILTER_PROPERTIES( this ), "_alpha", alpha, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+               mlt_geometry_parse( alpha, alpha_data, length, 100, 100 );
+       }
+       else
+       {
+               mlt_geometry_refresh( alpha, alpha_data, length, 100, 100 );
+       }
+
+       // We may still not have a producer in which case, we do nothing
+       if ( producer != NULL )
+       {
+               mlt_frame mask = NULL;
+               struct mlt_geometry_item_s item;
+               mlt_geometry_fetch( alpha, &item, position );
+               alpha_mix = item.x;
+               mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), MLT_FILTER_PROPERTIES( this ), "producer." );
+               mlt_producer_seek( producer, position );
+               if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &mask, 0 ) == 0 )
+               {
+                       char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" );
+                       mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), name, mask, 0, ( mlt_destructor )mlt_frame_close, NULL );
+                       mlt_frame_push_service( frame, this );
+                       mlt_frame_push_service( frame, mask );
+                       mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( frame ), alpha_mix / 100.0 );
+                       mlt_frame_push_get_image( frame, filter_get_image );
+                       if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( this ), "audio_match" ) )
+                       {
+                               mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "meta.mixdown", 1 );
+                               mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), "meta.volume", alpha_mix / 100.0 );
+                       }
+               }
+       }
+
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_shape_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "resource", arg );
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "mix", "100" );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "audio_match", 1 );
+               mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "invert", 0 );
+               mlt_properties_set_double( MLT_FILTER_PROPERTIES( this ), "softness", 0.1 );
+               this->process = filter_process;
+       }
+       return this;
+}
+
diff --git a/src/modules/vmfx/producer_pgm.c b/src/modules/vmfx/producer_pgm.c
new file mode 100644 (file)
index 0000000..6713317
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * producer_pgm.c -- PGM producer
+ * Copyright (C) 2005 Visual Media Fx Inc.
+ * Author: Charles Yates <charles.yates@gmail.com>
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int read_pgm( char *name, uint8_t **image, int *width, int *height, int *maxval );
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
+static void producer_close( mlt_producer parent );
+
+mlt_producer producer_pgm_init( mlt_profile profile, mlt_service_type type, const char *id, char *resource )
+{
+       mlt_producer this = NULL;
+       uint8_t *image = NULL;
+       int width = 0;
+       int height = 0;
+       int maxval = 0;
+
+       if ( read_pgm( resource, &image, &width, &height, &maxval ) == 0 )
+       {
+               this = calloc( 1, sizeof( struct mlt_producer_s ) );
+               if ( this != NULL && mlt_producer_init( this, NULL ) == 0 )
+               {
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+                       this->get_frame = producer_get_frame;
+                       this->close = ( mlt_destructor )producer_close;
+                       mlt_properties_set( properties, "resource", resource );
+                       mlt_properties_set_data( properties, "image", image, 0, mlt_pool_release, NULL );
+                       mlt_properties_set_int( properties, "real_width", width );
+                       mlt_properties_set_int( properties, "real_height", height );
+               }
+               else
+               {
+                       mlt_pool_release( image );
+                       free( this );
+                       this = NULL;
+               }
+       }
+
+       return this;
+}
+
+/** Load the PGM file.
+*/
+
+static int read_pgm( char *name, uint8_t **image, int *width, int *height, int *maxval )
+{
+       uint8_t *input = NULL;
+       int error = 0;
+       FILE *f = fopen( name, "r" );
+       char data[ 512 ];
+
+       // Initialise
+       *image = NULL;
+       *width = 0;
+       *height = 0;
+       *maxval = 0;
+
+       // Get the magic code
+       if ( f != NULL && fgets( data, 511, f ) != NULL && data[ 0 ] == 'P' && data[ 1 ] == '5' )
+       {
+               char *p = data + 2;
+               int i = 0;
+               int val = 0;
+
+               // PGM Header parser (probably needs to be strengthened)
+               for ( i = 0; !error && i < 3; i ++ )
+               {
+                       if ( *p != '\0' && *p != '\n' )
+                               val = strtol( p, &p, 10 );
+                       else
+                               p = NULL;
+
+                       while ( error == 0 && p == NULL )
+                       {
+                               if ( fgets( data, 511, f ) == NULL )
+                                       error = 1;
+                               else if ( data[ 0 ] != '#' )
+                                       val = strtol( data, &p, 10 );
+                       }
+
+                       switch( i )
+                       {
+                               case 0: *width = val; break;
+                               case 1: *height = val; break;
+                               case 2: *maxval = val; break;
+                       }
+               }
+               
+               if ( !error )
+               {
+                       // Determine if this is one or two bytes per pixel
+                       int bpp = *maxval > 255 ? 2 : 1;
+                       int size = *width * *height * bpp;
+                       uint8_t *p;
+
+                       // Allocate temporary storage for the data and the image
+                       input = mlt_pool_alloc( *width * *height * bpp );
+                       *image = mlt_pool_alloc( *width * *height * sizeof( uint8_t ) * 2 );
+                       p = *image;
+
+                       error = *image == NULL || input == NULL;
+
+                       if ( !error )
+                       {
+                               // Read the raw data
+                               error = fread( input, *width * *height * bpp, 1, f ) != 1;
+
+                               if ( !error )
+                               {
+                                       // Convert to yuv422 (very lossy - need to extend this to allow 16 bit alpha out)
+                                       for ( i = 0; i < size; i += bpp )
+                                       {
+                                               *p ++ = 16 + ( input[ i ] * 219 ) / 255;
+                                               *p ++ = 128;
+                                       }
+                               }
+                       }
+               }
+
+               if ( error )
+                       mlt_pool_release( *image );
+               mlt_pool_release( input );
+       }
+       else
+       {
+               error = 1;
+       }
+
+       if ( f != NULL )
+               fclose( f );
+
+       return error;
+}
+
+static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_producer producer = mlt_frame_pop_service( this );
+       int real_width = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "real_width" );
+       int real_height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "real_height" );
+       int size = real_width * real_height;
+       uint8_t *image = mlt_pool_alloc( size * 2 );
+       uint8_t *source = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( producer ), "image", NULL );
+
+       mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, size * 2, mlt_pool_release, NULL );
+
+       *width = real_width;
+       *height = real_height;
+       *format = mlt_image_yuv422;
+       *buffer = image;
+
+       if ( image != NULL && source != NULL )
+               memcpy( image, source, size * 2 );
+
+       return 0;
+}
+
+static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
+{
+       // Construct a test frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+
+       // Get the frames properties
+       mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
+
+       // Pass the data on the frame properties
+       mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( producer ), "real_width,real_height" );
+       mlt_properties_set_int( properties, "has_image", 1 );
+       mlt_properties_set_int( properties, "progressive", 1 );
+       mlt_properties_set_double( properties, "aspect_ratio", 1 );
+
+       // Push the image callback
+       mlt_frame_push_service( *frame, producer );
+       mlt_frame_push_get_image( *frame, producer_get_image );
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( producer );
+
+       return 0;
+}
+
+static void producer_close( mlt_producer parent )
+{
+       parent->close = NULL;
+       mlt_producer_close( parent );
+       free( parent );
+}
diff --git a/src/modules/vorbis/Makefile b/src/modules/vorbis/Makefile
new file mode 100644 (file)
index 0000000..f159a2a
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltvorbis$(LIBSUF)
+
+OBJS = factory.o \
+          producer_vorbis.o
+
+CFLAGS += -I../..
+CFLAGS += `pkg-config --cflags vorbis`
+CFLAGS += `pkg-config --cflags vorbisfile`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `pkg-config --libs vorbis`
+LDFLAGS += `pkg-config --libs vorbisfile`
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/vorbis/configure b/src/modules/vorbis/configure
new file mode 100755 (executable)
index 0000000..2f8c117
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       pkg-config vorbisfile 2> /dev/null
+       disable_vorbis=$?
+
+       if [ "$disable_vorbis" != "0" ]
+       then
+               echo "- ogg vorbis not found: disabling"
+               touch ../disable-vorbis
+       fi
+       exit 0
+fi
+
diff --git a/src/modules/vorbis/factory.c b/src/modules/vorbis/factory.c
new file mode 100644 (file)
index 0000000..3df2e75
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( producer_type, "vorbis", producer_vorbis_init );
+}
diff --git a/src/modules/vorbis/producer_vorbis.c b/src/modules/vorbis/producer_vorbis.c
new file mode 100644 (file)
index 0000000..1c1e7ce
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * producer_vorbis.c -- vorbis producer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// MLT Header files
+#include <framework/mlt_producer.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+
+// vorbis Header files
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+// System header files
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+// Forward references.
+static int producer_open( mlt_producer this, mlt_profile profile, char *file );
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index );
+
+/** Structure for metadata reading 
+*/
+
+typedef struct _sw_metadata sw_metadata;
+
+struct _sw_metadata {
+       char * name;
+       char * content;
+};
+
+static sw_metadata *vorbis_metadata_from_str (char * str)
+{
+       sw_metadata * meta = NULL;
+       int i;
+
+       for (i = 0; str[i]; i++) {
+               str[i] = tolower(str[i]);
+               if (str[i] == '=') {
+                       str[i] = '\0';
+                       meta = malloc (sizeof (sw_metadata));
+                       meta->name = malloc( strlen(str) + 18 );
+                       sprintf(meta->name, "meta.attr.%s.markup", str);
+                       meta->content = strdup (&str[i+1]);
+                       break;
+               }
+       }
+       return meta;
+}
+
+/** Constructor for libvorbis.
+*/
+
+mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *file )
+{
+       mlt_producer this = NULL;
+
+       // Check that we have a non-NULL argument
+       if ( file != NULL )
+       {
+               // Construct the producer
+               this = calloc( 1, sizeof( struct mlt_producer_s ) );
+
+               // Initialise it
+               if ( mlt_producer_init( this, NULL ) == 0 )
+               {
+                       // Get the properties
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+                       // Set the resource property (required for all producers)
+                       mlt_properties_set( properties, "resource", file );
+
+                       // Register our get_frame implementation
+                       this->get_frame = producer_get_frame;
+
+                       // Open the file
+                       if ( producer_open( this, profile, file ) != 0 )
+                       {
+                               // Clean up
+                               mlt_producer_close( this );
+                               this = NULL;
+                       }
+               }
+       }
+
+       return this;
+}
+
+/** Destuctor for ogg files.
+*/
+
+static void producer_file_close( void *file )
+{
+       if ( file != NULL )
+       {
+               // Close the ogg vorbis structure
+               ov_clear( file );
+
+               // Free the memory
+               free( file );
+       }
+}
+
+/** Open the file.
+*/
+
+static int producer_open( mlt_producer this, mlt_profile profile, char *file )
+{
+       // FILE pointer for file
+       FILE *input = fopen( file, "r" );
+
+       // Error code to return
+       int error = input == NULL;
+
+       // Continue if file is open
+       if ( error == 0 )
+       {
+               // OggVorbis file structure
+               OggVorbis_File *ov = calloc( 1, sizeof( OggVorbis_File ) );
+
+               // Attempt to open the stream
+               error = ov == NULL || ov_open( input, ov, NULL, 0 ) != 0;
+
+               // Assign to producer properties if successful
+               if ( error == 0 )
+               {
+                       // Get the properties
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+                       // Assign the ov structure
+                       mlt_properties_set_data( properties, "ogg_vorbis_file", ov, 0, producer_file_close, NULL );
+
+                       // Read metadata
+                       sw_metadata * metadata = NULL;
+                       char **ptr = ov_comment(ov, -1)->user_comments;
+                       while(*ptr) {
+                               metadata = vorbis_metadata_from_str (*ptr);
+                               if (metadata != NULL)
+                                       mlt_properties_set(properties, metadata->name, metadata->content);
+                               ++ptr;
+                       }
+
+                       if ( ov_seekable( ov ) )
+                       {
+                               // Get the length of the file
+                       double length = ov_time_total( ov, -1 );
+
+                               // We will treat everything with the producer fps
+                               double fps = mlt_profile_fps( profile );
+
+                               // Set out and length of file
+                               mlt_properties_set_position( properties, "out", ( length * fps ) - 1 );
+                               mlt_properties_set_position( properties, "length", ( length * fps ) );
+
+                               // Get the vorbis info
+                               vorbis_info *vi = ov_info( ov, -1 );
+                               mlt_properties_set_int( properties, "frequency", (int) vi->rate );
+                               mlt_properties_set_int( properties, "channels", vi->channels );
+
+                               // Set some media metadata
+                               mlt_properties_set_int( properties, "meta.media.nb_streams", 1 );
+                               mlt_properties_set_int( properties, "audio_index", 0 );
+                               mlt_properties_set( properties, "meta.media.0.stream.type", "audio" );
+                               mlt_properties_set( properties, "meta.media.0.codec.name", "vorbis" );
+                               mlt_properties_set( properties, "meta.media.0.codec.long_name", "Vorbis" );
+                       }
+               }
+               else
+               {
+                       // Clean up
+                       free( ov );
+
+                       // Must close input file when open fails
+                       fclose( input );
+               }
+       }
+
+       return error;
+}
+
+/** Convert a frame position to a time code.
+*/
+
+static double producer_time_of_frame( mlt_producer this, mlt_position position )
+{
+       return ( double )position / mlt_producer_get_fps( this );
+}
+
+/** Get the audio from a frame.
+*/
+
+static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+       // Get the properties from the frame
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
+
+       // Obtain the frame number of this frame
+       mlt_position position = mlt_properties_get_position( frame_properties, "vorbis_position" );
+
+       // Get the producer 
+       mlt_producer this = mlt_frame_pop_audio( frame );
+
+       // Get the producer properties
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( this );
+
+       // Get the ogg vorbis file
+       OggVorbis_File *ov = mlt_properties_get_data( properties, "ogg_vorbis_file", NULL );
+
+       // Obtain the expected frame numer
+       mlt_position expected = mlt_properties_get_position( properties, "audio_expected" );
+
+       // Get the fps for this producer
+       double fps = mlt_producer_get_fps( this );
+
+       // Get the vorbis info
+       vorbis_info *vi = ov_info( ov, -1 );
+
+       // Obtain the audio buffer
+       int16_t *audio_buffer = mlt_properties_get_data( properties, "audio_buffer", NULL );
+
+       // Get amount of audio used
+       int audio_used =  mlt_properties_get_int( properties, "audio_used" );
+
+       // Number of frames to ignore (for ffwd)
+       int ignore = 0;
+
+       // Flag for paused (silence) 
+       int paused = 0;
+
+       // Check for audio buffer and create if necessary
+       if ( audio_buffer == NULL )
+       {
+               // Allocate the audio buffer
+               audio_buffer = mlt_pool_alloc( 131072 * sizeof( int16_t ) );
+
+               // And store it on properties for reuse
+               mlt_properties_set_data( properties, "audio_buffer", audio_buffer, 0, mlt_pool_release, NULL );
+       }
+
+       // Seek if necessary
+       if ( position != expected )
+       {
+               if ( position + 1 == expected )
+               {
+                       // We're paused - silence required
+                       paused = 1;
+               }
+               else if ( position > expected && ( position - expected ) < 250 )
+               {
+                       // Fast forward - seeking is inefficient for small distances - just ignore following frames
+                       ignore = position - expected;
+               }
+               else
+               {
+                       // Seek to the required position
+                       ov_time_seek( ov, producer_time_of_frame( this, position ) );
+                       expected = position;
+                       audio_used = 0;
+               }
+       }
+
+       // Return info in frame
+       *frequency = vi->rate;
+       *channels = vi->channels;
+
+       // Get the audio if required
+       if ( !paused )
+       {
+               // Bitstream section
+               int current_section;
+
+               // Get the number of samples for the current frame
+               *samples = mlt_sample_calculator( fps, *frequency, expected ++ );
+
+               while( *samples > audio_used  )
+               {
+                       // Read the samples
+                       int bytes = ov_read( ov, ( char * )( &audio_buffer[ audio_used * 2 ] ), 4096, 0, 2, 1, &current_section );
+
+                       // Break if error or eof
+                       if ( bytes <= 0 )
+                               break;
+
+                       // Increment number of samples used
+                       audio_used += bytes / ( sizeof( int16_t ) * *channels );
+
+                       // Handle ignore
+                       while ( ignore && audio_used >= *samples )
+                       {
+                               ignore --;
+                               audio_used -= *samples;
+                               memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * sizeof( int16_t ) );
+                               *samples = mlt_sample_calculator( fps, *frequency, expected ++ );
+                       }
+               }
+
+               // Now handle the audio if we have enough
+               if ( audio_used >= *samples )
+               {
+                       *buffer = mlt_pool_alloc( *samples * *channels * sizeof( int16_t ) );
+                       memcpy( *buffer, audio_buffer, *samples * *channels * sizeof( int16_t ) );
+                       audio_used -= *samples;
+                       memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) );
+                       mlt_properties_set_data( frame_properties, "audio", *buffer, 0, mlt_pool_release, NULL );
+               }
+               else
+               {
+                       mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+                       audio_used = 0;
+               }
+               
+               // Store the number of audio samples still available
+               mlt_properties_set_int( properties, "audio_used", audio_used );
+       }
+       else
+       {
+               // Get silence and don't touch the context
+               *samples = mlt_sample_calculator( fps, *frequency, position );
+               mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+       }
+
+       // Regardless of speed, we expect to get the next frame (cos we ain't too bright)
+       mlt_properties_set_position( properties, "audio_expected", position + 1 );
+
+       return 0;
+}
+
+/** Our get frame implementation.
+*/
+
+static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index )
+{
+       // Create an empty frame
+       *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) );
+
+       // Update timecode on the frame we're creating
+       mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+
+       // Set the position of this producer
+       mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
+       mlt_properties_set_position( frame_properties, "vorbis_position", mlt_producer_frame( this ) );
+
+       // Set up the audio
+       mlt_frame_push_audio( *frame, this );
+       mlt_frame_push_audio( *frame, producer_get_audio );
+
+       // Pass audio properties to the frame
+       mlt_properties_pass_list( frame_properties, MLT_PRODUCER_PROPERTIES( this ), "frequency, channels" );
+
+       // Calculate the next timecode
+       mlt_producer_prepare_next( this );
+
+       return 0;
+}
diff --git a/src/modules/westley/Makefile b/src/modules/westley/Makefile
new file mode 100644 (file)
index 0000000..2ab4ed8
--- /dev/null
@@ -0,0 +1,37 @@
+include ../../../config.mak
+
+TARGET = ../libmltwestley$(LIBSUF)
+
+OBJS = factory.o \
+          consumer_westley.o \
+          producer_westley.o
+
+CFLAGS += -I../..
+CFLAGS += `xml2-config --cflags`
+
+LDFLAGS += -L../../framework -lmlt
+LDFLAGS += `xml2-config --libs`
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+       install -m 644 westley.dtd "$(DESTDIR)$(prefix)/share/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/westley/configure b/src/modules/westley/configure
new file mode 100755 (executable)
index 0000000..53aacd2
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ "$help" != "1" ]
+then
+
+       which xml2-config > /dev/null 2>&1
+       disable_xml2=$?
+
+       if [ "$disable_xml2" != "0" ]
+       then
+               echo "- xml2 not found: disabling westley modules"
+               touch ../disable-westley
+       fi
+       exit 0
+fi
+
diff --git a/src/modules/westley/consumer_westley.c b/src/modules/westley/consumer_westley.c
new file mode 100644 (file)
index 0000000..29174be
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * consumer_westley.c -- a libxml2 serialiser of mlt service networks
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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 <framework/mlt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <libxml/tree.h>
+
+#define ID_SIZE 128
+
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+// This maintains counters for adding ids to elements
+struct serialise_context_s
+{
+       mlt_properties id_map;
+       int producer_count;
+       int multitrack_count;
+       int playlist_count;
+       int tractor_count;
+       int filter_count;
+       int transition_count;
+       int pass;
+       mlt_properties hide_map;
+       char *root;
+       char *store;
+};
+typedef struct serialise_context_s* serialise_context;
+
+/** Forward references to static functions.
+*/
+
+static int consumer_start( mlt_consumer parent );
+static int consumer_is_stopped( mlt_consumer this );
+static void serialise_service( serialise_context context, mlt_service service, xmlNode *node );
+
+typedef enum 
+{
+       westley_existing,
+       westley_producer,
+       westley_multitrack,
+       westley_playlist,
+       westley_tractor,
+       westley_filter,
+       westley_transition
+}
+westley_type;
+
+/** Create or retrieve an id associated to this service.
+*/
+
+static char *westley_get_id( serialise_context context, mlt_service service, westley_type type )
+{
+       char *id = NULL;
+       int i = 0;
+       mlt_properties map = context->id_map;
+
+       // Search the map for the service
+       for ( i = 0; i < mlt_properties_count( map ); i ++ )
+               if ( mlt_properties_get_data_at( map, i, NULL ) == service )
+                       break;
+
+       // If the service is not in the map, and the type indicates a new id is needed...
+       if ( i >= mlt_properties_count( map ) && type != westley_existing )
+       {
+               // Attempt to reuse existing id
+               id = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "id" );
+
+               // If no id, or the id is used in the map (for another service), then 
+               // create a new one.
+               if ( id == NULL || mlt_properties_get_data( map, id, NULL ) != NULL )
+               {
+                       char temp[ ID_SIZE ];
+                       do
+                       {
+                               switch( type )
+                               {
+                                       case westley_producer:
+                                               sprintf( temp, "producer%d", context->producer_count ++ );
+                                               break;
+                                       case westley_multitrack:
+                                               sprintf( temp, "multitrack%d", context->multitrack_count ++ );
+                                               break;
+                                       case westley_playlist:
+                                               sprintf( temp, "playlist%d", context->playlist_count ++ );
+                                               break;
+                                       case westley_tractor:
+                                               sprintf( temp, "tractor%d", context->tractor_count ++ );
+                                               break;
+                                       case westley_filter:
+                                               sprintf( temp, "filter%d", context->filter_count ++ );
+                                               break;
+                                       case westley_transition:
+                                               sprintf( temp, "transition%d", context->transition_count ++ );
+                                               break;
+                                       case westley_existing:
+                                               // Never gets here
+                                               break;
+                               }
+                       }
+                       while( mlt_properties_get_data( map, temp, NULL ) != NULL );
+
+                       // Set the data at the generated name
+                       mlt_properties_set_data( map, temp, service, 0, NULL, NULL );
+
+                       // Get the pointer to the name (i is the end of the list)
+                       id = mlt_properties_get_name( map, i );
+               }
+               else
+               {
+                       // Store the existing id in the map
+                       mlt_properties_set_data( map, id, service, 0, NULL, NULL );
+               }
+       }
+       else if ( type == westley_existing )
+       {
+               id = mlt_properties_get_name( map, i );
+       }
+
+       return id;
+}
+
+/** This is what will be called by the factory - anything can be passed in
+       via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       // Create the consumer object
+       mlt_consumer this = calloc( sizeof( struct mlt_consumer_s ), 1 );
+
+       // If no malloc'd and consumer init ok
+       if ( this != NULL && mlt_consumer_init( this, NULL, profile ) == 0 )
+       {
+               // Allow thread to be started/stopped
+               this->start = consumer_start;
+               this->is_stopped = consumer_is_stopped;
+
+               mlt_properties_set( MLT_CONSUMER_PROPERTIES( this ), "resource", arg );
+
+               // Return the consumer produced
+               return this;
+       }
+
+       // malloc or consumer init failed
+       free( this );
+
+       // Indicate failure
+       return NULL;
+}
+
+static void serialise_properties( serialise_context context, mlt_properties properties, xmlNode *node )
+{
+       int i;
+       xmlNode *p;
+       
+       // Enumerate the properties
+       for ( i = 0; i < mlt_properties_count( properties ); i++ )
+       {
+               char *name = mlt_properties_get_name( properties, i );
+               if ( name != NULL &&
+                        name[ 0 ] != '_' &&
+                        mlt_properties_get_value( properties, i ) != NULL &&
+                        strcmp( name, "westley" ) != 0 &&
+                        strcmp( name, "in" ) != 0 &&
+                        strcmp( name, "out" ) != 0 && 
+                        strcmp( name, "id" ) != 0 && 
+                        strcmp( name, "title" ) != 0 && 
+                        strcmp( name, "root" ) != 0 && 
+                        strcmp( name, "width" ) != 0 &&
+                        strcmp( name, "height" ) != 0 )
+               {
+                       char *value = mlt_properties_get_value( properties, i );
+                       if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
+                               value += strlen( context->root ) + 1;
+                       p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
+                       xmlNewProp( p, _x("name"), _x(name) );
+               }
+       }
+}
+
+static void serialise_store_properties( serialise_context context, mlt_properties properties, xmlNode *node, const char *store )
+{
+       int i;
+       xmlNode *p;
+       
+       // Enumerate the properties
+       for ( i = 0; store != NULL && i < mlt_properties_count( properties ); i++ )
+       {
+               char *name = mlt_properties_get_name( properties, i );
+               if ( !strncmp( name, store, strlen( store ) ) )
+               {
+                       char *value = mlt_properties_get_value( properties, i );
+                       if ( value != NULL )
+                       {
+                               if ( strcmp( context->root, "" ) && !strncmp( value, context->root, strlen( context->root ) ) )
+                                       value += strlen( context->root ) + 1;
+                               p = xmlNewTextChild( node, NULL, _x("property"), _x(value) );
+                               xmlNewProp( p, _x("name"), _x(name) );
+                       }
+               }
+       }
+}
+
+static inline void serialise_service_filters( serialise_context context, mlt_service service, xmlNode *node )
+{
+       int i;
+       xmlNode *p;
+       mlt_filter filter = NULL;
+       
+       // Enumerate the filters
+       for ( i = 0; ( filter = mlt_producer_filter( MLT_PRODUCER( service ), i ) ) != NULL; i ++ )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+               if ( mlt_properties_get_int( properties, "_fezzik" ) == 0 )
+               {
+                       // Get a new id - if already allocated, do nothing
+                       char *id = westley_get_id( context, MLT_FILTER_SERVICE( filter ), westley_filter );
+                       if ( id != NULL )
+                       {
+                               int in = mlt_properties_get_position( properties, "in" );
+                               int out = mlt_properties_get_position( properties, "out" );
+                               p = xmlNewChild( node, NULL, _x("filter"), NULL );
+                               xmlNewProp( p, _x("id"), _x(id) );
+                               if ( mlt_properties_get( properties, "title" ) )
+                                       xmlNewProp( p, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+                               if ( in != 0 || out != 0 )
+                               {
+                                       char temp[ 20 ];
+                                       sprintf( temp, "%d", in );
+                                       xmlNewProp( p, _x("in"), _x(temp) );
+                                       sprintf( temp, "%d", out );
+                                       xmlNewProp( p, _x("out"), _x(temp) );
+                               }
+                               serialise_properties( context, properties, p );
+                               serialise_service_filters( context, MLT_FILTER_SERVICE( filter ), p );
+                       }
+               }
+       }
+}
+
+static void serialise_producer( serialise_context context, mlt_service service, xmlNode *node )
+{
+       xmlNode *child = node;
+       mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( MLT_PRODUCER( service ) ) );
+       
+       if ( context->pass == 0 )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, parent, westley_producer );
+               if ( id == NULL )
+                       return;
+
+               child = xmlNewChild( node, NULL, _x("producer"), NULL );
+
+               // Set the id
+               xmlNewProp( child, _x("id"), _x(id) );
+               if ( mlt_properties_get( properties, "title" ) )
+                       xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+               xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+               xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+               serialise_properties( context, properties, child );
+               serialise_service_filters( context, service, child );
+
+               // Add producer to the map
+               mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
+       }
+       else
+       {
+               char *id = westley_get_id( context, parent, westley_existing );
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+               xmlNewProp( node, _x("parent"), _x(id) );
+               xmlNewProp( node, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+               xmlNewProp( node, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+       }
+}
+
+static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node );
+
+static void serialise_multitrack( serialise_context context, mlt_service service, xmlNode *node )
+{
+       int i;
+       
+       if ( context->pass == 0 )
+       {
+               // Iterate over the tracks to collect the producers
+               for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
+               {
+                       mlt_producer producer = mlt_producer_cut_parent( mlt_multitrack_track( MLT_MULTITRACK( service ), i ) );
+                       serialise_service( context, MLT_SERVICE( producer ), node );
+               }
+       }
+       else
+       {
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, service, westley_multitrack );
+               if ( id == NULL )
+                       return;
+
+               // Serialise the tracks
+               for ( i = 0; i < mlt_multitrack_count( MLT_MULTITRACK( service ) ); i++ )
+               {
+                       xmlNode *track = xmlNewChild( node, NULL, _x("track"), NULL );
+                       int hide = 0;
+                       mlt_producer producer = mlt_multitrack_track( MLT_MULTITRACK( service ), i );
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
+
+                       mlt_service parent = MLT_SERVICE( mlt_producer_cut_parent( producer ) );
+
+                       char *id = westley_get_id( context, MLT_SERVICE( parent ), westley_existing );
+                       xmlNewProp( track, _x("producer"), _x(id) );
+                       if ( mlt_producer_is_cut( producer ) )
+                       {
+                               xmlNewProp( track, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+                               xmlNewProp( track, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+                               serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store );
+                               serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." );
+                               serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track );
+                       }
+                       
+                       hide = mlt_properties_get_int( context->hide_map, id );
+                       if ( hide )
+                               xmlNewProp( track, _x("hide"), _x( hide == 1 ? "video" : ( hide == 2 ? "audio" : "both" ) ) );
+               }
+               serialise_service_filters( context, service, node );
+       }
+}
+
+static void serialise_playlist( serialise_context context, mlt_service service, xmlNode *node )
+{
+       int i;
+       xmlNode *child = node;
+       mlt_playlist_clip_info info;
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       
+       if ( context->pass == 0 )
+       {
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, service, westley_playlist );
+               if ( id == NULL )
+                       return;
+
+               // Iterate over the playlist entries to collect the producers
+               for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
+               {
+                       if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
+                       {
+                               if ( info.producer != NULL )
+                               {
+                                       mlt_producer producer = mlt_producer_cut_parent( info.producer );
+                                       char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
+                                       char *resource_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource" );
+                                       if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
+                                               serialise_playlist( context, MLT_SERVICE( producer ), node );
+                                       else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
+                                               serialise_service( context, MLT_SERVICE( producer ), node );
+                               }
+                       }
+               }
+               
+               child = xmlNewChild( node, NULL, _x("playlist"), NULL );
+
+               // Set the id
+               xmlNewProp( child, _x("id"), _x(id) );
+               if ( mlt_properties_get( properties, "title" ) )
+                       xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+
+               // Store application specific properties
+               serialise_store_properties( context, properties, child, context->store );
+               serialise_store_properties( context, properties, child, "meta." );
+
+               // Add producer to the map
+               mlt_properties_set_int( context->hide_map, id, mlt_properties_get_int( properties, "hide" ) );
+       
+               // Iterate over the playlist entries
+               for ( i = 0; i < mlt_playlist_count( MLT_PLAYLIST( service ) ); i++ )
+               {
+                       if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
+                       {
+                               mlt_producer producer = mlt_producer_cut_parent( info.producer );
+                               char *service_s = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "mlt_service" );
+                               if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
+                               {
+                                       char length[ 20 ];
+                                       length[ 19 ] = '\0';
+                                       xmlNode *entry = xmlNewChild( child, NULL, _x("blank"), NULL );
+                                       snprintf( length, 19, "%d", (int)info.frame_count );
+                                       xmlNewProp( entry, _x("length"), _x(length) );
+                               }
+                               else
+                               {
+                                       char temp[ 20 ];
+                                       xmlNode *entry = xmlNewChild( child, NULL, _x("entry"), NULL );
+                                       id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
+                                       xmlNewProp( entry, _x("producer"), _x(id) );
+                                       sprintf( temp, "%d", (int)info.frame_in );
+                                       xmlNewProp( entry, _x("in"), _x(temp) );
+                                       sprintf( temp, "%d", (int)info.frame_out );
+                                       xmlNewProp( entry, _x("out"), _x(temp) );
+                                       if ( info.repeat > 1 )
+                                       {
+                                               sprintf( temp, "%d", info.repeat );
+                                               xmlNewProp( entry, _x("repeat"), _x(temp) );
+                                       }
+                                       if ( mlt_producer_is_cut( info.cut ) )
+                                       {
+                                               serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store );
+                                               serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." );
+                                               serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry );
+                                       }
+                               }
+                       }
+               }
+
+               serialise_service_filters( context, service, child );
+       }
+       else if ( xmlStrcmp( node->name, _x("tractor") ) != 0 )
+       {
+               char *id = westley_get_id( context, service, westley_existing );
+               xmlNewProp( node, _x("producer"), _x(id) );
+       }
+}
+
+static void serialise_tractor( serialise_context context, mlt_service service, xmlNode *node )
+{
+       xmlNode *child = node;
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       
+       if ( context->pass == 0 )
+       {
+               // Recurse on connected producer
+               serialise_service( context, mlt_service_producer( service ), node );
+       }
+       else
+       {
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, service, westley_tractor );
+               if ( id == NULL )
+                       return;
+
+               child = xmlNewChild( node, NULL, _x("tractor"), NULL );
+
+               // Set the id
+               xmlNewProp( child, _x("id"), _x(id) );
+               if ( mlt_properties_get( properties, "title" ) )
+                       xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+               if ( mlt_properties_get( properties, "global_feed" ) )
+                       xmlNewProp( child, _x("global_feed"), _x(mlt_properties_get( properties, "global_feed" )) );
+               xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+               xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+               // Store application specific properties
+               serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store );
+               serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." );
+
+               // Recurse on connected producer
+               serialise_service( context, mlt_service_producer( service ), child );
+               serialise_service_filters( context, service, child );
+       }
+}
+
+static void serialise_filter( serialise_context context, mlt_service service, xmlNode *node )
+{
+       xmlNode *child = node;
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       
+       // Recurse on connected producer
+       serialise_service( context, mlt_service_producer( service ), node );
+
+       if ( context->pass == 1 )
+       {
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, service, westley_filter );
+               if ( id == NULL )
+                       return;
+
+               child = xmlNewChild( node, NULL, _x("filter"), NULL );
+
+               // Set the id
+               xmlNewProp( child, _x("id"), _x(id) );
+               if ( mlt_properties_get( properties, "title" ) )
+                       xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+               xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+               xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+               serialise_properties( context, properties, child );
+               serialise_service_filters( context, service, child );
+       }
+}
+
+static void serialise_transition( serialise_context context, mlt_service service, xmlNode *node )
+{
+       xmlNode *child = node;
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       
+       // Recurse on connected producer
+       serialise_service( context, MLT_SERVICE( MLT_TRANSITION( service )->producer ), node );
+
+       if ( context->pass == 1 )
+       {
+               // Get a new id - if already allocated, do nothing
+               char *id = westley_get_id( context, service, westley_transition );
+               if ( id == NULL )
+                       return;
+
+               child = xmlNewChild( node, NULL, _x("transition"), NULL );
+       
+               // Set the id
+               xmlNewProp( child, _x("id"), _x(id) );
+               if ( mlt_properties_get( properties, "title" ) )
+                       xmlNewProp( child, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+               xmlNewProp( child, _x("in"), _x(mlt_properties_get( properties, "in" )) );
+               xmlNewProp( child, _x("out"), _x(mlt_properties_get( properties, "out" )) );
+
+               serialise_properties( context, properties, child );
+               serialise_service_filters( context, service, child );
+       }
+}
+
+static void serialise_service( serialise_context context, mlt_service service, xmlNode *node )
+{
+       // Iterate over consumer/producer connections
+       while ( service != NULL )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+               char *mlt_type = mlt_properties_get( properties, "mlt_type" );
+               
+               // Tell about the producer
+               if ( strcmp( mlt_type, "producer" ) == 0 )
+               {
+                       char *mlt_service = mlt_properties_get( properties, "mlt_service" );
+                       if ( mlt_properties_get( properties, "westley" ) == NULL && ( mlt_service != NULL && !strcmp( mlt_service, "tractor" ) ) )
+                       {
+                               context->pass = 0;
+                               serialise_tractor( context, service, node );
+                               context->pass = 1;
+                               serialise_tractor( context, service, node );
+                               context->pass = 0;
+                               break;
+                       }
+                       else
+                       {
+                               serialise_producer( context, service, node );
+                       }
+                       if ( mlt_properties_get( properties, "westley" ) != NULL )
+                               break;
+               }
+
+               // Tell about the framework container producers
+               else if ( strcmp( mlt_type, "mlt_producer" ) == 0 )
+               {
+                       char *resource = mlt_properties_get( properties, "resource" );
+                       
+                       // Recurse on multitrack's tracks
+                       if ( strcmp( resource, "<multitrack>" ) == 0 )
+                       {
+                               serialise_multitrack( context, service, node );
+                               break;
+                       }
+                       
+                       // Recurse on playlist's clips
+                       else if ( strcmp( resource, "<playlist>" ) == 0 )
+                       {
+                               serialise_playlist( context, service, node );
+                       }
+                       
+                       // Recurse on tractor's producer
+                       else if ( strcmp( resource, "<tractor>" ) == 0 )
+                       {
+                               context->pass = 0;
+                               serialise_tractor( context, service, node );
+                               context->pass = 1;
+                               serialise_tractor( context, service, node );
+                               context->pass = 0;
+                               break;
+                       }
+
+                       // Treat it as a normal producer
+                       else
+                       {
+                               serialise_producer( context, service, node );
+                       }
+               }
+               
+               // Tell about a filter
+               else if ( strcmp( mlt_type, "filter" ) == 0 )
+               {
+                       serialise_filter( context, service, node );
+                       break;
+               }
+               
+               // Tell about a transition
+               else if ( strcmp( mlt_type, "transition" ) == 0 )
+               {
+                       serialise_transition( context, service, node );
+                       break;
+               }
+               
+               // Get the next connected service
+               service = mlt_service_producer( service );
+       }
+}
+
+xmlDocPtr westley_make_doc( mlt_consumer consumer, mlt_service service )
+{
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       xmlDocPtr doc = xmlNewDoc( _x("1.0") );
+       xmlNodePtr root = xmlNewNode( NULL, _x("westley") );
+       struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) );
+       
+       xmlDocSetRootElement( doc, root );
+
+       // If we have root, then deal with it now
+       if ( mlt_properties_get( properties, "root" ) != NULL )
+       {
+               xmlNewProp( root, _x("root"), _x(mlt_properties_get( properties, "root" )) );
+               context->root = strdup( mlt_properties_get( properties, "root" ) );
+       }
+       else
+       {
+               context->root = strdup( "" );
+       }
+
+       // Assign the additional 'storage' pattern for properties
+       context->store = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "store" );
+
+       // Assign a title property
+       if ( mlt_properties_get( properties, "title" ) != NULL )
+               xmlNewProp( root, _x("title"), _x(mlt_properties_get( properties, "title" )) );
+       mlt_properties_set_int( properties, "global_feed", 1 );
+
+       // Construct the context maps
+       context->id_map = mlt_properties_new();
+       context->hide_map = mlt_properties_new();
+       
+       // Ensure producer is a framework producer
+       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "mlt_type", "mlt_producer" );
+
+       // In pass one, we serialise the end producers and playlists,
+       // adding them to a map keyed by address.
+       serialise_service( context, service, root );
+
+       // In pass two, we serialise the tractor and reference the
+       // producers and playlists
+       context->pass++;
+       serialise_service( context, service, root );
+
+       // Cleanup resource
+       mlt_properties_close( context->id_map );
+       mlt_properties_close( context->hide_map );
+       free( context->root );
+       free( context );
+       
+       return doc;
+}
+
+static int consumer_start( mlt_consumer this )
+{
+       xmlDocPtr doc = NULL;
+       
+       // Get the producer service
+       mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( this ) );
+       if ( service != NULL )
+       {
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
+               char *resource =  mlt_properties_get( properties, "resource" );
+
+               // Set the title if provided
+               if ( mlt_properties_get( properties, "title" ) )
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", mlt_properties_get( properties, "title" ) );
+               else if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "title" ) == NULL )
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "title", "Anonymous Submission" );
+
+               // Check for a root on the consumer properties and pass to service
+               if ( mlt_properties_get( properties, "root" ) )
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", mlt_properties_get( properties, "root" ) );
+
+               // Specify roots in other cases...
+               if ( resource != NULL && mlt_properties_get( properties, "root" ) == NULL )
+               {
+                       // Get the current working directory
+                       char *cwd = getcwd( NULL, 0 );
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "root", cwd );
+                       free( cwd );
+               }
+
+               // Make the document
+               doc = westley_make_doc( this, service );
+
+               // Handle the output
+               if ( resource == NULL || !strcmp( resource, "" ) )
+               {
+                       xmlDocFormatDump( stdout, doc, 1 );
+               }
+               else if ( strchr( resource, '.' ) == NULL )
+               {
+                       xmlChar *buffer = NULL;
+                       int length = 0;
+                       xmlDocDumpMemoryEnc( doc, &buffer, &length, "utf-8" );
+                       mlt_properties_set( properties, resource, _s(buffer) );
+                       xmlFree( buffer );
+               }
+               else
+               {
+                       xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 );
+               }
+               
+               // Close the document
+               xmlFreeDoc( doc );
+       }
+       
+       mlt_consumer_stop( this );
+
+       mlt_consumer_stopped( this );
+
+       return 0;
+}
+
+static int consumer_is_stopped( mlt_consumer this )
+{
+       return 1;
+}
diff --git a/src/modules/westley/factory.c b/src/modules/westley/factory.c
new file mode 100644 (file)
index 0000000..87853f8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 <string.h>
+#include <framework/mlt.h>
+
+extern mlt_consumer consumer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_producer producer_westley_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( consumer_type, "westley", consumer_westley_init );
+       MLT_REGISTER( producer_type, "westley", producer_westley_init );
+       MLT_REGISTER( producer_type, "westley-xml", producer_westley_init );
+}
diff --git a/src/modules/westley/producer_westley.c b/src/modules/westley/producer_westley.c
new file mode 100644 (file)
index 0000000..35f7739
--- /dev/null
@@ -0,0 +1,1515 @@
+/*
+ * producer_westley.c -- a libxml2 parser of mlt service networks
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * 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
+ */
+
+// TODO: destroy unreferenced producers (they are currently destroyed
+//       when the returned producer is closed).
+
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
+#include <libxml/tree.h>
+
+#define STACK_SIZE 1000
+#define BRANCH_SIG_LEN 4000
+
+#define _x (const xmlChar*)
+#define _s (const char*)
+
+#undef DEBUG
+#ifdef DEBUG
+extern xmlDocPtr westley_make_doc( mlt_service service );
+#endif
+
+enum service_type
+{
+       mlt_invalid_type,
+       mlt_unknown_type,
+       mlt_producer_type,
+       mlt_playlist_type,
+       mlt_entry_type,
+       mlt_tractor_type,
+       mlt_multitrack_type,
+       mlt_filter_type,
+       mlt_transition_type,
+       mlt_consumer_type,
+       mlt_field_type,
+       mlt_services_type,
+       mlt_dummy_filter_type,
+       mlt_dummy_transition_type,
+       mlt_dummy_producer_type,
+};
+
+struct deserialise_context_s
+{
+       enum service_type stack_types[ STACK_SIZE ];
+       mlt_service stack_service[ STACK_SIZE ];
+       int stack_service_size;
+       mlt_properties producer_map;
+       mlt_properties destructors;
+       char *property;
+       int is_value;
+       xmlDocPtr value_doc;
+       xmlNodePtr stack_node[ STACK_SIZE ];
+       int stack_node_size;
+       xmlDocPtr entity_doc;
+       int entity_is_replace;
+       int depth;
+       int branch[ STACK_SIZE ];
+       const xmlChar *publicId;
+       const xmlChar *systemId;
+       mlt_properties params;
+       mlt_profile profile;
+};
+typedef struct deserialise_context_s *deserialise_context;
+
+/** Convert the numerical current branch address to a dot-delimited string.
+*/
+static char *serialise_branch( deserialise_context this, char *s )
+{
+       int i;
+       
+       s[0] = 0;
+       for ( i = 0; i < this->depth; i++ )
+       {
+               int len = strlen( s );
+               snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
+       }
+       return s;
+}
+
+/** Push a service.
+*/
+
+static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
+{
+       int ret = this->stack_service_size >= STACK_SIZE - 1;
+       if ( ret == 0 )
+       {
+               this->stack_service[ this->stack_service_size ] = that;
+               this->stack_types[ this->stack_service_size++ ] = type;
+               
+               // Record the tree branch on which this service lives
+               if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_westley_branch" ) == NULL )
+               {
+                       char s[ BRANCH_SIG_LEN ];
+                       mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_westley_branch", serialise_branch( this, s ) );
+               }
+       }
+       return ret;
+}
+
+/** Pop a service.
+*/
+
+static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
+{
+       mlt_service result = NULL;
+       if ( this->stack_service_size > 0 )
+       {
+               result = this->stack_service[ -- this->stack_service_size ];
+               if ( type != NULL )
+                       *type = this->stack_types[ this->stack_service_size ];
+       }
+       return result;
+}
+
+/** Push a node.
+*/
+
+static int context_push_node( deserialise_context this, xmlNodePtr node )
+{
+       int ret = this->stack_node_size >= STACK_SIZE - 1;
+       if ( ret == 0 )
+               this->stack_node[ this->stack_node_size ++ ] = node;
+       return ret;
+}
+
+/** Pop a node.
+*/
+
+static xmlNodePtr context_pop_node( deserialise_context this )
+{
+       xmlNodePtr result = NULL;
+       if ( this->stack_node_size > 0 )
+               result = this->stack_node[ -- this->stack_node_size ];
+       return result;
+}
+
+
+// Set the destructor on a new service
+static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
+{
+       int registered = mlt_properties_get_int( properties, "registered" );
+       char *key = mlt_properties_get( properties, "registered" );
+       mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+       mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+
+// Prepend the property value with the document root
+static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
+{
+       char *resource = mlt_properties_get( properties, name );
+       if ( resource != NULL && resource[0] )
+       {
+               // Qualify file name properties 
+               char *root = mlt_properties_get( context->producer_map, "root" );
+               if ( root != NULL && strcmp( root, "" ) )
+               {
+                       char *full_resource = malloc( strlen( root ) + strlen( resource ) + 2 );
+                       if ( resource[ 0 ] != '/' && strchr( resource, ':' ) == NULL )
+                       {
+                               strcpy( full_resource, root );
+                               strcat( full_resource, "/" );
+                               strcat( full_resource, resource );
+                       }
+                       else
+                       {
+                               strcpy( full_resource, resource );
+                       }
+                       mlt_properties_set( properties, name, full_resource );
+                       free( full_resource );
+               }
+       }
+}
+
+
+/** This function adds a producer to a playlist or multitrack when
+    there is no entry or track element.
+*/
+
+static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
+{
+       // Return value (0 = service remains top of stack, 1 means it can be removed)
+       int result = 0;
+
+       // Get the parent producer
+       enum service_type type = mlt_invalid_type;
+       mlt_service container = context_pop_service( context, &type );
+       int contained = 0;
+
+       if ( service != NULL && container != NULL )
+       {
+               char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_westley_branch" );
+               char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_westley_branch" );
+               contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
+       }
+
+       if ( contained )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+               char *hide_s = mlt_properties_get( properties, "hide" );
+
+               // Indicate that this service is no longer top of stack
+               result = 1;
+
+               switch( type )
+               {
+                       case mlt_tractor_type: 
+                               {
+                                       mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
+                                       mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
+                               }
+                               break;
+                       case mlt_multitrack_type:
+                               {
+                                       mlt_multitrack_connect( MLT_MULTITRACK( container ),
+                                               MLT_PRODUCER( service ),
+                                               mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
+                               }
+                               break;
+                       case mlt_playlist_type:
+                               {
+                                       mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
+                               }
+                               break;
+                       default:
+                               result = 0;
+                               fprintf( stderr, "Producer defined inside something that isn't a container\n" );
+                               break;
+               };
+
+               // Set the hide state of the track producer
+               if ( hide_s != NULL )
+               {
+                       if ( strcmp( hide_s, "video" ) == 0 )
+                               mlt_properties_set_int( properties, "hide", 1 );
+                       else if ( strcmp( hide_s, "audio" ) == 0 )
+                               mlt_properties_set_int( properties, "hide", 2 );
+                       else if ( strcmp( hide_s, "both" ) == 0 )
+                               mlt_properties_set_int( properties, "hide", 3 );
+               }
+       }
+
+       // Put the parent producer back
+       if ( container != NULL )
+               context_push_service( context, container, type );
+
+       return result;
+}
+
+/** Attach filters defined on that to this.
+*/
+
+static void attach_filters( mlt_service this, mlt_service that )
+{
+       if ( that != NULL )
+       {
+               int i = 0;
+               mlt_filter filter = NULL;
+               for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
+               {
+                       mlt_service_attach( this, filter );
+                       attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
+               }
+       }
+}
+
+static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       mlt_tractor tractor = mlt_tractor_new( );
+       mlt_service service = MLT_TRACTOR_SERVICE( tractor );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
+
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+       mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
+
+       if ( mlt_properties_get( properties, "id" ) != NULL )
+               mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
+       
+       context_push_service( context, service, mlt_tractor_type );
+}
+
+static void on_end_tractor( deserialise_context context, const xmlChar *name )
+{
+       // Get the tractor
+       enum service_type type;
+       mlt_service tractor = context_pop_service( context, &type );
+
+       if ( tractor != NULL && type == mlt_tractor_type )
+       {
+               // See if the tractor should be added to a playlist or multitrack
+               if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
+                       context_push_service( context, tractor, type );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid state for tractor\n" );
+       }
+}
+
+static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       enum service_type type;
+       mlt_service parent = context_pop_service( context, &type );
+
+       // If we don't have a parent, then create one now, providing we're in a state where we can
+       if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
+       {
+               mlt_tractor tractor = NULL;
+               // Push the parent back
+               if ( parent != NULL )
+                       context_push_service( context, parent, type );
+
+               // Create a tractor to contain the multitrack
+               tractor = mlt_tractor_new( );
+               parent = MLT_TRACTOR_SERVICE( tractor );
+               track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
+               type = mlt_tractor_type;
+
+               // Flag it as a synthesised tractor for clean up later
+               mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "fezzik_synth", 1 );
+       }
+
+       if ( type == mlt_tractor_type )
+       {
+               mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+               for ( ; atts != NULL && *atts != NULL; atts += 2 )
+                       mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+               if ( mlt_properties_get( properties, "id" ) != NULL )
+                       mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
+
+               context_push_service( context, parent, type );
+               context_push_service( context, service, mlt_multitrack_type );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid multitrack position\n" );
+       }
+}
+
+static void on_end_multitrack( deserialise_context context, const xmlChar *name )
+{
+       // Get the multitrack from the stack
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+
+       if ( service == NULL || type != mlt_multitrack_type )
+               fprintf( stderr, "End multitrack in the wrong state...\n" );
+}
+
+static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       mlt_playlist playlist = mlt_playlist_init( );
+       mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
+
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+       {
+               mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+
+               // Out will be overwritten later as we append, so we need to save it
+               if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
+                       mlt_properties_set( properties, "_westley.out", ( const char* )atts[ 1 ] );
+       }
+
+       if ( mlt_properties_get( properties, "id" ) != NULL )
+               mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
+
+       context_push_service( context, service, mlt_playlist_type );
+}
+
+static void on_end_playlist( deserialise_context context, const xmlChar *name )
+{
+       // Get the playlist from the stack
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+
+       if ( service != NULL && type == mlt_playlist_type )
+       {
+               mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+               mlt_position in = mlt_properties_get_position( properties, "in" );
+               mlt_position out = mlt_properties_get_position( properties, "out" );
+
+               // See if the playlist should be added to a playlist or multitrack
+               if ( add_producer( context, service, in, out ) == 0 )
+                       context_push_service( context, service, type );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid state of playlist end\n" );
+       }
+}
+
+static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       // use a dummy service to hold properties to allow arbitrary nesting
+       mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+       mlt_service_init( service, NULL );
+
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       context_push_service( context, service, mlt_dummy_producer_type );
+
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+}
+
+// Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames
+static mlt_position parse_clock_value( char *value, double fps )
+{
+       // This implementation expects a fully specified clock value - no optional
+       // parts (e.g. 1:05)
+       char *pos, *copy = strdup( value );
+       int hh, mm, ss, ms;
+       mlt_position result = -1;
+
+       value = copy;
+       pos = strchr( value, ':' );
+       if ( !pos )
+               return result;
+       *pos = '\0';
+       hh = atoi( value );
+       value = pos + 1;
+
+       pos = strchr( value, ':' );
+       if ( !pos )
+               return result;
+       *pos = '\0';
+       mm = atoi( value );
+       value = pos + 1;
+       
+       pos = strchr( value, '.' );
+       if ( !pos )
+               return result;
+       *pos = '\0';
+       ss = atoi( value );
+       value = pos + 1;
+       
+       ms = atoi( value );
+       free( copy );
+       result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000  + ms ) / 1000 + 0.5 );
+       
+       return result;
+}
+
+static void on_end_producer( deserialise_context context, const xmlChar *name )
+{
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       if ( service != NULL && type == mlt_dummy_producer_type )
+       {
+               mlt_service producer = NULL;
+
+               qualify_property( context, properties, "resource" );
+               char *resource = mlt_properties_get( properties, "resource" );
+
+               // Let Kino-SMIL src be a synonym for resource
+               if ( resource == NULL )
+               {
+                       qualify_property( context, properties, "src" );
+                       resource = mlt_properties_get( properties, "src" );
+               }
+
+               // Instantiate the producer
+               if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
+               {
+                       char temp[ 1024 ];
+                       strncpy( temp, mlt_properties_get( properties, "mlt_service" ), 1024 );
+                       if ( resource != NULL )
+                       {
+                               strcat( temp, ":" );
+                               strncat( temp, resource, 1023 - strlen( temp ) );
+                       }
+                       producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", temp ) );
+               }
+
+               // Just in case the plugin requested doesn't exist...
+               if ( producer == NULL && resource != NULL )
+                       producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", resource ) );
+       
+               if ( producer == NULL )
+                       producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", "+INVALID.txt" ) );
+
+               if ( producer == NULL )
+                       producer = MLT_SERVICE( mlt_factory_producer( context->profile, "fezzik", "colour:red" ) );
+
+               // Track this producer
+               track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
+
+               // Propogate the properties
+               qualify_property( context, properties, "resource" );
+               qualify_property( context, properties, "luma" );
+               qualify_property( context, properties, "luma.resource" );
+               qualify_property( context, properties, "composite.luma" );
+               qualify_property( context, properties, "producer.resource" );
+
+               // Handle in/out properties separately
+               mlt_position in = -1;
+               mlt_position out = -1;
+       
+               // Get in
+               if ( mlt_properties_get( properties, "in" ) != NULL )
+                       in = mlt_properties_get_position( properties, "in" );
+               // Let Kino-SMIL clipBegin be a synonym for in
+               if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
+               {
+                       if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
+                               // Parse clock value
+                               in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
+                                       mlt_producer_get_fps( MLT_PRODUCER(  producer ) ) );
+                       else
+                               // Parse frames value
+                               in = mlt_properties_get_position( properties, "clipBegin" );
+               }
+               // Get out
+               if ( mlt_properties_get( properties, "out" ) != NULL )
+                       out = mlt_properties_get_position( properties, "out" );
+               // Let Kino-SMIL clipEnd be a synonym for out
+               if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
+               {
+                       if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
+                               // Parse clock value
+                               out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
+                                       mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
+                       else
+                               // Parse frames value
+                               out = mlt_properties_get_position( properties, "clipEnd" );
+               }
+               // Remove in and out
+               mlt_properties_set( properties, "in", NULL );
+               mlt_properties_set( properties, "out", NULL );
+
+               // Inherit the properties
+               mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
+
+               // Attach all filters from service onto producer
+               attach_filters( producer, service );
+
+               // Add the producer to the producer map
+               if ( mlt_properties_get( properties, "id" ) != NULL )
+                       mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
+
+               // See if the producer should be added to a playlist or multitrack
+               if ( add_producer( context, producer, in, out ) == 0 )
+               {
+                       // Otherwise, set in and out on...
+                       if ( in != -1 || out != -1 )
+                       {
+                               // Get the parent service
+                               enum service_type type;
+                               mlt_service parent = context_pop_service( context, &type );
+                               if ( parent != NULL )
+                               {
+                                       // Get the parent properties
+                                       properties = MLT_SERVICE_PROPERTIES( parent );
+                               
+                                       char *resource = mlt_properties_get( properties, "resource" );
+                               
+                                       // Put the parent producer back
+                                       context_push_service( context, parent, type );
+                                       
+                                       // If the parent is a track or entry
+                                       if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
+                                       {
+                                               mlt_properties_set_position( properties, "in", in );
+                                               mlt_properties_set_position( properties, "out", out );
+                                       }
+                                       else
+                                       {
+                                               // Otherwise, set in and out on producer directly
+                                               mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
+                                       }
+                               }
+                               else
+                               {
+                                       // Otherwise, set in and out on producer directly
+                                       mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
+                               }
+                       }
+
+                       // Push the producer onto the stack
+                       context_push_service( context, producer, mlt_producer_type );
+               }
+
+               mlt_service_close( service );
+       }
+}
+
+static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       // Get the playlist from the stack
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_position length = 0;
+       
+       if ( type == mlt_playlist_type && service != NULL )
+       {
+               // Look for the length attribute
+               for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               {
+                       if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
+                       {
+                               length = atoll( _s(atts[1]) );
+                               break;
+                       }
+               }
+
+               // Append a blank to the playlist
+               mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
+
+               // Push the playlist back onto the stack
+               context_push_service( context, service, type );
+       }
+       else
+       {
+               fprintf( stderr, "blank without a playlist - a definite no no\n" );
+       }
+}
+
+static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       mlt_producer entry = NULL;
+       mlt_properties temp = mlt_properties_new( );
+
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+       {
+               mlt_properties_set( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+               
+               // Look for the producer attribute
+               if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
+               {
+                       mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
+                       if ( producer !=  NULL )
+                               mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
+               }
+       }
+
+       // If we have a valid entry
+       if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
+       {
+               mlt_playlist_clip_info info;
+               enum service_type parent_type = invalid_type;
+               mlt_service parent = context_pop_service( context, &parent_type );
+               mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
+
+               if ( parent_type == mlt_playlist_type )
+               {
+                       // Append the producer to the playlist
+                       if ( mlt_properties_get( temp, "in" ) != NULL || mlt_properties_get( temp, "out" ) != NULL )
+                       {
+                               mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
+                                       mlt_properties_get_position( temp, "in" ), 
+                                       mlt_properties_get_position( temp, "out" ) );
+                       }
+                       else
+                       {
+                               mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
+                       }
+
+                       // Handle the repeat property
+                       if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
+                       {
+                               mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
+                                                                                 mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
+                                                                                 mlt_properties_get_int( temp, "repeat" ) );
+                       }
+
+                       mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
+                       entry = info.cut;
+               }
+               else
+               {
+                       fprintf( stderr, "Entry not part of a playlist...\n" );
+               }
+
+               context_push_service( context, parent, parent_type );
+       }
+
+       // Push the cut onto the stack
+       context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
+
+       mlt_properties_close( temp );
+}
+
+static void on_end_entry( deserialise_context context, const xmlChar *name )
+{
+       // Get the entry from the stack
+       enum service_type entry_type = invalid_type;
+       mlt_service entry = context_pop_service( context, &entry_type );
+
+       if ( entry == NULL && entry_type != mlt_entry_type )
+       {
+               fprintf( stderr, "Invalid state at end of entry\n" );
+       }
+}
+
+static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       // use a dummy service to hold properties to allow arbitrary nesting
+       mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+       mlt_service_init( service, NULL );
+
+       // Push the dummy service onto the stack
+       context_push_service( context, service, mlt_entry_type );
+       
+       mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
+       
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+       {
+               mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
+               
+               // Look for the producer attribute
+               if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
+               {
+                       mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
+                       if ( producer !=  NULL )
+                               mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
+               }
+       }
+}
+
+static void on_end_track( deserialise_context context, const xmlChar *name )
+{
+       // Get the track from the stack
+       enum service_type track_type;
+       mlt_service track = context_pop_service( context, &track_type );
+
+       if ( track != NULL && track_type == mlt_entry_type )
+       {
+               mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
+               enum service_type parent_type = invalid_type;
+               mlt_service parent = context_pop_service( context, &parent_type );
+               mlt_multitrack multitrack = NULL;
+
+               mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
+               mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
+
+               if ( parent_type == mlt_tractor_type )
+                       multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
+               else if ( parent_type == mlt_multitrack_type )
+                       multitrack = MLT_MULTITRACK( parent );
+               else
+                       fprintf( stderr, "track contained in an invalid container\n" );
+
+               if ( multitrack != NULL )
+               {
+                       // Set producer i/o if specified
+                       if ( mlt_properties_get( track_props, "in" ) != NULL ||
+                                mlt_properties_get( track_props, "out" ) != NULL )
+                       {
+                               mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ),
+                                       mlt_properties_get_position( track_props, "in" ),
+                                       mlt_properties_get_position( track_props, "out" ) );
+                               mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
+                               mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
+                               track_props = MLT_PRODUCER_PROPERTIES( cut );
+                               mlt_producer_close( cut );
+                       }
+                       else
+                       {
+                               mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
+                       }
+
+                       // Set the hide state of the track producer
+                       char *hide_s = mlt_properties_get( track_props, "hide" );
+                       if ( hide_s != NULL )
+                       {
+                               if ( strcmp( hide_s, "video" ) == 0 )
+                                       mlt_properties_set_int( producer_props, "hide", 1 );
+                               else if ( strcmp( hide_s, "audio" ) == 0 )
+                                       mlt_properties_set_int( producer_props, "hide", 2 );
+                               else if ( strcmp( hide_s, "both" ) == 0 )
+                                       mlt_properties_set_int( producer_props, "hide", 3 );
+                       }
+               }
+
+               if ( parent != NULL )
+                       context_push_service( context, parent, parent_type );
+
+               mlt_service_close( track );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid state at end of track\n" );
+       }
+}
+
+static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       // use a dummy service to hold properties to allow arbitrary nesting
+       mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+       mlt_service_init( service, NULL );
+
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       context_push_service( context, service, mlt_dummy_filter_type );
+
+       // Set the properties
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
+}
+
+static void on_end_filter( deserialise_context context, const xmlChar *name )
+{
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       enum service_type parent_type = invalid_type;
+       mlt_service parent = context_pop_service( context, &parent_type );
+
+       if ( service != NULL && type == mlt_dummy_filter_type )
+       {
+               mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
+               mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
+
+               track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
+
+               // Propogate the properties
+               qualify_property( context, properties, "resource" );
+               qualify_property( context, properties, "luma" );
+               qualify_property( context, properties, "luma.resource" );
+               qualify_property( context, properties, "composite.luma" );
+               qualify_property( context, properties, "producer.resource" );
+               mlt_properties_inherit( filter_props, properties );
+
+               // Attach all filters from service onto filter
+               attach_filters( filter, service );
+
+               // Associate the filter with the parent
+               if ( parent != NULL )
+               {
+                       if ( parent_type == mlt_tractor_type )
+                       {
+                               mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
+                               mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
+                               mlt_filter_set_in_and_out( MLT_FILTER( filter ), 
+                                                                                  mlt_properties_get_int( properties, "in" ),
+                                                                                  mlt_properties_get_int( properties, "out" ) );
+                       }
+                       else
+                       {
+                               mlt_service_attach( parent, MLT_FILTER( filter ) );
+                       }
+
+                       // Put the parent back on the stack
+                       context_push_service( context, parent, parent_type );
+               }
+               else
+               {
+                       fprintf( stderr, "filter closed with invalid parent...\n" );
+               }
+
+               // Close the dummy filter service
+               mlt_service_close( service );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid top of stack on filter close\n" );
+       }
+}
+
+static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       // use a dummy service to hold properties to allow arbitrary nesting
+       mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
+       mlt_service_init( service, NULL );
+
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       context_push_service( context, service, mlt_dummy_transition_type );
+
+       // Set the properties
+       for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
+}
+
+static void on_end_transition( deserialise_context context, const xmlChar *name )
+{
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       enum service_type parent_type = invalid_type;
+       mlt_service parent = context_pop_service( context, &parent_type );
+
+       if ( service != NULL && type == mlt_dummy_transition_type )
+       {
+               char *id = mlt_properties_get( properties, "mlt_service" );
+               mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
+               mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
+
+               track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
+
+               // Propogate the properties
+               qualify_property( context, properties, "resource" );
+               qualify_property( context, properties, "luma" );
+               qualify_property( context, properties, "luma.resource" );
+               qualify_property( context, properties, "composite.luma" );
+               qualify_property( context, properties, "producer.resource" );
+               mlt_properties_inherit( effect_props, properties );
+
+               // Attach all filters from service onto effect
+               attach_filters( effect, service );
+
+               // Associate the filter with the parent
+               if ( parent != NULL )
+               {
+                       if ( parent_type == mlt_tractor_type )
+                       {
+                               mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
+                               if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
+                                       mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
+                               mlt_field_plant_transition( field, MLT_TRANSITION( effect ), 
+                                                                                       mlt_properties_get_int( properties, "a_track" ),
+                                                                                       mlt_properties_get_int( properties, "b_track" ) );
+                               mlt_transition_set_in_and_out( MLT_TRANSITION( effect ), 
+                                                                                  mlt_properties_get_int( properties, "in" ),
+                                                                                  mlt_properties_get_int( properties, "out" ) );
+                       }
+                       else
+                       {
+                               fprintf( stderr, "Misplaced transition - ignoring\n" );
+                       }
+
+                       // Put the parent back on the stack
+                       context_push_service( context, parent, parent_type );
+               }
+               else
+               {
+                       fprintf( stderr, "transition closed with invalid parent...\n" );
+               }
+
+               // Close the dummy filter service
+               mlt_service_close( service );
+       }
+       else
+       {
+               fprintf( stderr, "Invalid top of stack on transition close\n" );
+       }
+}
+
+static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+       const char *value = NULL;
+
+       if ( service != NULL )
+       {
+               // Set the properties
+               for ( ; atts != NULL && *atts != NULL; atts += 2 )
+               {
+                       if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
+                               context->property = strdup( _s(atts[ 1 ]) );
+                       else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
+                               value = _s(atts[ 1 ]);
+               }
+
+               if ( context->property != NULL )
+                       mlt_properties_set( properties, context->property, value == NULL ? "" : value );
+
+               // Tell parser to collect any further nodes for serialisation
+               context->is_value = 1;
+
+               context_push_service( context, service, type );
+       }
+       else
+       {
+               fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
+       }
+}
+
+static void on_end_property( deserialise_context context, const xmlChar *name )
+{
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       if ( service != NULL )
+       {
+               // Tell parser to stop building a tree
+               context->is_value = 0;
+       
+               // See if there is a xml tree for the value
+               if ( context->property != NULL && context->value_doc != NULL )
+               {
+                       xmlChar *value;
+                       int size;
+               
+                       // Serialise the tree to get value
+                       xmlDocDumpMemory( context->value_doc, &value, &size );
+                       mlt_properties_set( properties, context->property, _s(value) );
+                       xmlFree( value );
+                       xmlFreeDoc( context->value_doc );
+                       context->value_doc = NULL;
+               }
+
+               // Close this property handling
+               free( context->property );
+               context->property = NULL;
+
+               context_push_service( context, service, type );
+       }
+       else
+       {
+               fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
+       }
+}
+
+static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       
+//printf("on_start_element: %s\n", name );
+       context->branch[ context->depth ] ++;
+       context->depth ++;
+       
+       // Build a tree from nodes within a property value
+       if ( context->is_value == 1 )
+       {
+               xmlNodePtr node = xmlNewNode( NULL, name );
+               
+               if ( context->value_doc == NULL )
+               {
+                       // Start a new tree
+                       context->value_doc = xmlNewDoc( _x("1.0") );
+                       xmlDocSetRootElement( context->value_doc, node );
+               }
+               else
+               {
+                       // Append child to tree
+                       xmlAddChild( context->stack_node[ context->stack_node_size - 1 ], node );
+               }
+               context_push_node( context, node );
+               
+               // Set the attributes
+               for ( ; atts != NULL && *atts != NULL; atts += 2 )
+                       xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
+       }
+       else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
+               on_start_tractor( context, name, atts );
+       else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
+               on_start_multitrack( context, name, atts );
+       else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
+               on_start_playlist( context, name, atts );
+       else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
+               on_start_producer( context, name, atts );
+       else if ( xmlStrcmp( name, _x("blank") ) == 0 )
+               on_start_blank( context, name, atts );
+       else if ( xmlStrcmp( name, _x("entry") ) == 0 )
+               on_start_entry( context, name, atts );
+       else if ( xmlStrcmp( name, _x("track") ) == 0 )
+               on_start_track( context, name, atts );
+       else if ( xmlStrcmp( name, _x("filter") ) == 0 )
+               on_start_filter( context, name, atts );
+       else if ( xmlStrcmp( name, _x("transition") ) == 0 )
+               on_start_transition( context, name, atts );
+       else if ( xmlStrcmp( name, _x("property") ) == 0 )
+               on_start_property( context, name, atts );
+       else if ( xmlStrcmp( name, _x("westley") ) == 0 )
+               for ( ; atts != NULL && *atts != NULL; atts += 2 )
+                       mlt_properties_set( context->producer_map, ( const char * )atts[ 0 ], ( const char * )atts[ 1 ] );
+}
+
+static void on_end_element( void *ctx, const xmlChar *name )
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       
+//printf("on_end_element: %s\n", name );
+       if ( context->is_value == 1 && xmlStrcmp( name, _x("property") ) != 0 )
+               context_pop_node( context );
+       else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
+               on_end_multitrack( context, name );
+       else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
+               on_end_playlist( context, name );
+       else if ( xmlStrcmp( name, _x("track") ) == 0 )
+               on_end_track( context, name );
+       else if ( xmlStrcmp( name, _x("entry") ) == 0 )
+               on_end_entry( context, name );
+       else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
+               on_end_tractor( context, name );
+       else if ( xmlStrcmp( name, _x("property") ) == 0 )
+               on_end_property( context, name );
+       else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
+               on_end_producer( context, name );
+       else if ( xmlStrcmp( name, _x("filter") ) == 0 )
+               on_end_filter( context, name );
+       else if ( xmlStrcmp( name, _x("transition") ) == 0 )
+               on_end_transition( context, name );
+
+       context->branch[ context->depth ] = 0;
+       context->depth --;
+}
+
+static void on_characters( void *ctx, const xmlChar *ch, int len )
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       char *value = calloc( len + 1, 1 );
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+
+       if ( service != NULL )
+               context_push_service( context, service, type );
+
+       value[ len ] = 0;
+       strncpy( value, (const char*) ch, len );
+
+       if ( context->stack_node_size > 0 )
+               xmlNodeAddContent( context->stack_node[ context->stack_node_size - 1 ], ( xmlChar* )value );
+
+       // libxml2 generates an on_characters immediately after a get_entity within
+       // an element value, and we ignore it because it is called again during
+       // actual substitution.
+       else if ( context->property != NULL && context->entity_is_replace == 0 )
+       {
+               char *s = mlt_properties_get( properties, context->property );
+               if ( s != NULL )
+               {
+                       // Append new text to existing content
+                       char *new = calloc( strlen( s ) + len + 1, 1 );
+                       strcat( new, s );
+                       strcat( new, value );
+                       mlt_properties_set( properties, context->property, new );
+                       free( new );
+               }
+               else
+                       mlt_properties_set( properties, context->property, value );
+       }
+       context->entity_is_replace = 0;
+       
+       free( value);
+}
+
+/** Convert parameters parsed from resource into entity declarations.
+*/
+static void params_to_entities( deserialise_context context )
+{
+       if ( context->params != NULL )
+       {       
+               int i;
+               
+               // Add our params as entitiy declarations
+               for ( i = 0; i < mlt_properties_count( context->params ); i++ )
+               {
+                       xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
+                       xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
+                               context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
+               }
+
+               // Flag completion
+               mlt_properties_close( context->params );
+               context->params = NULL;
+       }
+}
+
+// The following 3 facilitate entity substitution in the SAX parser
+static void on_internal_subset( void *ctx, const xmlChar* name,
+       const xmlChar* publicId, const xmlChar* systemId )
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       
+       context->publicId = publicId;
+       context->systemId = systemId;
+       xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
+       
+       // Override default entities with our parameters
+       params_to_entities( context );
+}
+
+// TODO: Check this with Dan... I think this is for westley parameterisation
+// but it's breaking standard escaped entities (like &lt; etc).
+static void on_entity_declaration( void *ctx, const xmlChar* name, int type, 
+       const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       
+       xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
+}
+
+// TODO: Check this functionality (see on_entity_declaration)
+static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
+{
+       struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
+       deserialise_context context = ( deserialise_context )( xmlcontext->_private );
+       xmlEntityPtr e = NULL;
+
+       // Setup for entity declarations if not ready
+       if ( xmlGetIntSubset( context->entity_doc ) == NULL )
+       {
+               xmlCreateIntSubset( context->entity_doc, _x("westley"), _x(""), _x("") );
+               context->publicId = _x("");
+               context->systemId = _x("");
+       }
+
+       // Add our parameters if not already
+       params_to_entities( context );
+       
+       e = xmlGetPredefinedEntity( name );
+       
+       // Send signal to on_characters that an entity substitutin is pending
+       if ( e == NULL )
+       {
+               e = xmlGetDocEntity( context->entity_doc, name );
+               if ( e != NULL )
+                       context->entity_is_replace = 1;
+       }
+       
+       return e;
+}
+
+/** Convert a hexadecimal character to its value.
+*/
+static int tohex( char p )
+{
+       return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
+}
+
+/** Decode a url-encoded string containing hexadecimal character sequences.
+*/
+static char *url_decode( char *dest, char *src )
+{
+       char *p = dest;
+       
+       while ( *src )
+       {
+               if ( *src == '%' )
+               {
+                       *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
+                       src += 3;
+               }
+               else
+               {
+                       *p ++ = *src ++;
+               }
+       }
+
+       *p = *src;
+       return dest;
+}
+
+/** Extract the filename from a URL attaching parameters to a properties list.
+*/
+static void parse_url( mlt_properties properties, char *url )
+{
+       int i;
+       int n = strlen( url );
+       char *name = NULL;
+       char *value = NULL;
+       
+       for ( i = 0; i < n; i++ )
+       {
+               switch ( url[ i ] )
+               {
+                       case '?':
+                               url[ i++ ] = '\0';
+                               name = &url[ i ];
+                               break;
+                       
+                       case ':':
+                       case '=':
+                               url[ i++ ] = '\0';
+                               value = &url[ i ];
+                               break;
+                       
+                       case '&':
+                               url[ i++ ] = '\0';
+                               if ( name != NULL && value != NULL )
+                                       mlt_properties_set( properties, name, value );
+                               name = &url[ i ];
+                               value = NULL;
+                               break;
+               }
+       }
+       if ( name != NULL && value != NULL )
+               mlt_properties_set( properties, name, value );
+}
+
+// Quick workaround to avoid unecessary libxml2 warnings
+static int file_exists( char *file )
+{
+       char *name = strdup( file );
+       int exists = 0;
+       if ( name != NULL && strchr( name, '?' ) )
+               *( strchr( name, '?' ) ) = '\0';
+       if ( name != NULL )
+       {
+               FILE *f = fopen( name, "r" );
+               exists = f != NULL;
+               if ( exists ) fclose( f );
+       }
+       free( name );
+       return exists;
+}
+
+mlt_producer producer_westley_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
+{
+       xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
+       struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
+       mlt_properties properties = NULL;
+       int i = 0;
+       struct _xmlParserCtxt *xmlcontext;
+       int well_formed = 0;
+       char *filename = NULL;
+       int info = strcmp( id, "westley-xml" ) ? 0 : 1;
+
+       if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
+               return NULL;
+
+       context = calloc( 1, sizeof( struct deserialise_context_s ) );
+       if ( context == NULL )
+               return NULL;
+
+       context->producer_map = mlt_properties_new();
+       context->destructors = mlt_properties_new();
+       context->params = mlt_properties_new();
+       context->profile = profile;
+
+       // Decode URL and parse parameters
+       mlt_properties_set( context->producer_map, "root", "" );
+       if ( info == 0 )
+       {
+               filename = strdup( data );
+               parse_url( context->params, url_decode( filename, data ) );
+
+               // We need the directory prefix which was used for the westley
+               if ( strchr( filename, '/' ) )
+               {
+                       char *root = NULL;
+                       mlt_properties_set( context->producer_map, "root", filename );
+                       root = mlt_properties_get( context->producer_map, "root" );
+                       *( strrchr( root, '/' ) ) = '\0';
+
+                       // If we don't have an absolute path here, we're heading for disaster...
+                       if ( root[ 0 ] != '/' )
+                       {
+                               char *cwd = getcwd( NULL, 0 );
+                               char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
+                               sprintf( real, "%s/%s", cwd, root );
+                               mlt_properties_set( context->producer_map, "root", real );
+                               free( real );
+                               free( cwd );
+                       }
+               }
+       }
+
+       // We need to track the number of registered filters
+       mlt_properties_set_int( context->destructors, "registered", 0 );
+
+       // Setup SAX callbacks
+       sax->startElement = on_start_element;
+       sax->endElement = on_end_element;
+       sax->characters = on_characters;
+       sax->cdataBlock = on_characters;
+       sax->internalSubset = on_internal_subset;
+       sax->entityDecl = on_entity_declaration;
+       sax->getEntity = on_get_entity;
+
+       // Setup libxml2 SAX parsing
+       xmlInitParser(); 
+       xmlSubstituteEntitiesDefault( 1 );
+       // This is used to facilitate entity substitution in the SAX parser
+       context->entity_doc = xmlNewDoc( _x("1.0") );
+       if ( info == 0 )
+               xmlcontext = xmlCreateFileParserCtxt( filename );
+       else
+               xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
+
+       // Invalid context - clean up and return NULL
+       if ( xmlcontext == NULL )
+       {
+               mlt_properties_close( context->producer_map );
+               mlt_properties_close( context->destructors );
+               mlt_properties_close( context->params );
+               free( context );
+               free( sax );
+               free( filename );
+               return NULL;
+       }
+
+       xmlcontext->sax = sax;
+       xmlcontext->_private = ( void* )context;
+       
+       // Parse
+       xmlParseDocument( xmlcontext );
+       well_formed = xmlcontext->wellFormed;
+       
+       // Cleanup after parsing
+       xmlFreeDoc( context->entity_doc );
+       free( sax );
+       xmlcontext->sax = NULL;
+       xmlcontext->_private = NULL;
+       xmlFreeParserCtxt( xmlcontext );
+       xmlMemoryDump( ); // for debugging
+
+       // Get the last producer on the stack
+       enum service_type type;
+       mlt_service service = context_pop_service( context, &type );
+       if ( well_formed && service != NULL )
+       {
+               // Verify it is a producer service (mlt_type="mlt_producer")
+               // (producer, playlist, multitrack)
+               char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
+               if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
+                       service = NULL;
+       }
+
+#ifdef DEBUG
+       xmlDocPtr doc = westley_make_doc( service );
+       xmlDocFormatDump( stdout, doc, 1 );
+       xmlFreeDoc( doc );
+       service = NULL;
+#endif
+       
+       if ( well_formed && service != NULL )
+       {
+               char *title = mlt_properties_get( context->producer_map, "title" );
+               
+               // Need the complete producer list for various reasons
+               properties = context->destructors;
+
+               // Now make sure we don't have a reference to the service in the properties
+               for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
+               {
+                       char *name = mlt_properties_get_name( properties, i );
+                       if ( mlt_properties_get_data( properties, name, NULL ) == service )
+                       {
+                               mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
+                               break;
+                       }
+               }
+
+               // We are done referencing destructor property list
+               // Set this var to service properties for convenience
+               properties = MLT_SERVICE_PROPERTIES( service );
+       
+               // Assign the title
+               mlt_properties_set( properties, "title", title );
+
+               // Optimise for overlapping producers
+               mlt_producer_optimise( MLT_PRODUCER( service ) );
+
+               // Handle deep copies
+               if ( getenv( "MLT_WESTLEY_DEEP" ) == NULL )
+               {
+                       // Now assign additional properties
+                       if ( info == 0 )
+                               mlt_properties_set( properties, "resource", data );
+
+                       // This tells consumer_westley not to deep copy
+                       mlt_properties_set( properties, "westley", "was here" );
+               }
+               else
+               {
+                       // Allow the project to be edited
+                       mlt_properties_set( properties, "_westley", "was here" );
+                       mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
+               }
+       }
+       else
+       {
+               // Return null if not well formed
+               service = NULL;
+       }
+
+       // Clean up
+       mlt_properties_close( context->producer_map );
+       if ( context->params != NULL )
+               mlt_properties_close( context->params );
+       mlt_properties_close( context->destructors );
+       free( context );
+       free( filename );
+
+       return MLT_PRODUCER( service );
+}
diff --git a/src/modules/westley/westley.dtd b/src/modules/westley/westley.dtd
new file mode 100644 (file)
index 0000000..4b926d7
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version='1.0' encoding='utf-8'?>
+
+<!-- MLT westley DTD v0.1.0 -->
+
+<!ELEMENT westley (producer | playlist | tractor | multitrack)+ >
+<!ELEMENT property ANY >
+<!ATTLIST property 
+    name     CDATA    #REQUIRED
+    value    CDATA    #IMPLIED
+>
+<!ELEMENT producer (property)* >
+<!ATTLIST producer
+    id       ID       #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+    mlt_service  CDATA    #IMPLIED
+>
+<!ELEMENT filter (property)* >
+<!ATTLIST filter
+    id       ID       #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+    mlt_service  CDATA    #IMPLIED
+    track    CDATA    #IMPLIED
+>
+<!ELEMENT transition (property)* >
+<!ATTLIST transition
+    id       ID       #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+    mlt_service  CDATA    #IMPLIED
+    a_track  CDATA    #IMPLIED
+    b_track  CDATA    #IMPLIED
+>
+<!ELEMENT playlist (entry | blank | producer | playlist | tractor | multitrack)+ >
+<!ATTLIST playlist
+    id       ID       #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+>
+<!ELEMENT entry (producer | playlist | tractor | multitrack | filter | transition)* >
+<!ATTLIST entry
+    producer IDREF    #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+>
+<!ELEMENT blank EMPTY >
+<!ATTLIST blank
+    length   CDATA    #REQUIRED
+>
+<!ELEMENT tractor (multitrack, (filter | transition)*) >
+<!ATTLIST tractor
+    id       ID       #IMPLIED
+    in       CDATA    #IMPLIED
+    out      CDATA    #IMPLIED
+>
+<!ELEMENT multitrack (track | producer | playlist | tractor | multitrack)+ >
+<!ATTLIST multitrack
+    id       ID       #IMPLIED
+>
+<!ELEMENT track (producer | playlist | tractor | multitrack | filter | transition)* >
+<!ATTLIST track
+    producer IDREF    #IMPLIED
+>
diff --git a/src/modules/xine/Makefile b/src/modules/xine/Makefile
new file mode 100644 (file)
index 0000000..5efb718
--- /dev/null
@@ -0,0 +1,38 @@
+include ../../../config.mak
+
+TARGET = ../libmltxine$(LIBSUF)
+
+OBJS = factory.o \
+          deinterlace.o \
+          filter_deinterlace.o
+
+ifdef MMX_FLAGS
+OBJS += cpu_accel.o
+endif
+
+CFLAGS += -I../../ -DARCH_X86
+
+LDFLAGS += -L../../framework -lmlt
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) 
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/xine/attributes.h b/src/modules/xine/attributes.h
new file mode 100644 (file)
index 0000000..c468b7b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * attributes.h
+ * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *
+ * mpeg2dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpeg2dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* use gcc attribs to align critical data structures */
+
+#ifndef ATTRIBUTE_H_
+#define ATTRIBUTE_H_
+
+#ifdef ATTRIBUTE_ALIGNED_MAX
+#define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align)))
+#else
+#define ATTR_ALIGN(align)
+#endif
+
+/* disable GNU __attribute__ extension, when not compiling with GNU C */
+#if defined(__GNUC__) || defined (__ICC)
+#ifndef ATTRIBUTE_PACKED
+#define        ATTRIBUTE_PACKED 1
+#endif 
+#else
+#undef ATTRIBUTE_PACKED
+#ifndef __attribute__
+#define        __attribute__(x)        /**/
+#endif /* __attribute __*/
+#endif
+
+#endif /* ATTRIBUTE_H_ */
+
diff --git a/src/modules/xine/cpu_accel.c b/src/modules/xine/cpu_accel.c
new file mode 100644 (file)
index 0000000..f8c0b7a
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * cpu_accel.c
+ * Copyright (C) 1999-2001 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ *
+ * This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *
+ * mpeg2dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpeg2dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+//#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <dlfcn.h>
+
+#define LOG_MODULE "cpu_accel"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xineutils.h"
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+#if defined __x86_64__
+static uint32_t arch_accel (void)
+{
+  uint32_t caps;
+  /* No need to test for this on AMD64, we know what the 
+     platform has.  */
+  caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT | MM_ACCEL_X86_SSE2;
+
+  return caps;
+}
+#else
+static uint32_t arch_accel (void)
+{
+#ifndef _MSC_VER
+
+  uint32_t eax, ebx, ecx, edx;
+  int AMD;
+  uint32_t caps;
+
+#ifndef PIC
+#define cpuid(op,eax,ebx,ecx,edx)       \
+    __asm__ ("cpuid"                        \
+         : "=a" (eax),                  \
+           "=b" (ebx),                  \
+           "=c" (ecx),                  \
+           "=d" (edx)                   \
+         : "a" (op)                     \
+         : "cc")
+#else   /* PIC version : save ebx */
+#define cpuid(op,eax,ebx,ecx,edx)       \
+    __asm__ ("pushl %%ebx\n\t"              \
+         "cpuid\n\t"                    \
+         "movl %%ebx,%1\n\t"            \
+         "popl %%ebx"                   \
+         : "=a" (eax),                  \
+           "=r" (ebx),                  \
+           "=c" (ecx),                  \
+           "=d" (edx)                   \
+         : "a" (op)                     \
+         : "cc")
+#endif
+
+  __asm__ ("pushfl\n\t"
+       "pushfl\n\t"
+       "popl %0\n\t"
+       "movl %0,%1\n\t"
+       "xorl $0x200000,%0\n\t"
+       "pushl %0\n\t"
+       "popfl\n\t"
+       "pushfl\n\t"
+       "popl %0\n\t"
+       "popfl"
+       : "=r" (eax),
+       "=r" (ebx)
+       :
+       : "cc");
+
+  if (eax == ebx)             /* no cpuid */
+    return 0;
+
+  cpuid (0x00000000, eax, ebx, ecx, edx);
+  if (!eax)                   /* vendor string only */
+    return 0;
+
+  AMD = (ebx == 0x68747541) && (ecx == 0x444d4163) && (edx == 0x69746e65);
+
+  cpuid (0x00000001, eax, ebx, ecx, edx);
+  if (! (edx & 0x00800000))   /* no MMX */
+    return 0;
+
+  caps = MM_ACCEL_X86_MMX;
+  if (edx & 0x02000000)       /* SSE - identical to AMD MMX extensions */
+    caps |= MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT;
+
+  if (edx & 0x04000000)       /* SSE2 */
+    caps |= MM_ACCEL_X86_SSE2;  
+    
+  cpuid (0x80000000, eax, ebx, ecx, edx);
+  if (eax < 0x80000001)       /* no extended capabilities */
+    return caps;
+
+  cpuid (0x80000001, eax, ebx, ecx, edx);
+
+  if (edx & 0x80000000)
+    caps |= MM_ACCEL_X86_3DNOW;
+
+  if (AMD && (edx & 0x00400000))      /* AMD MMX extensions */
+    caps |= MM_ACCEL_X86_MMXEXT;
+
+  return caps;
+#else /* _MSC_VER */
+  return 0;
+#endif
+}
+#endif /* x86_64 */
+
+static jmp_buf sigill_return;
+
+static void sigill_handler (int n) {
+  longjmp(sigill_return, 1);
+}
+#endif /* ARCH_X86 */
+
+#if defined (ARCH_PPC) && defined (ENABLE_ALTIVEC)
+static sigjmp_buf jmpbuf;
+static volatile sig_atomic_t canjump = 0;
+
+static void sigill_handler (int sig)
+{
+    if (!canjump) {
+       signal (sig, SIG_DFL);
+       raise (sig);
+    }
+
+    canjump = 0;
+    siglongjmp (jmpbuf, 1);
+}
+
+static uint32_t arch_accel (void)
+{
+    signal (SIGILL, sigill_handler);
+    if (sigsetjmp (jmpbuf, 1)) {
+       signal (SIGILL, SIG_DFL);
+       return 0;
+    }
+
+    canjump = 1;
+
+    __asm__ volatile ("mtspr 256, %0\n\t"
+                 "vand %%v0, %%v0, %%v0"
+                 :
+                 : "r" (-1));
+
+    signal (SIGILL, SIG_DFL);
+    return MM_ACCEL_PPC_ALTIVEC;
+}
+#endif /* ARCH_PPC */
+
+uint32_t xine_mm_accel (void)
+{
+  static int initialized = 0;
+  static uint32_t accel;
+
+  if (!initialized) {
+#if defined (ARCH_X86) || (defined (ARCH_PPC) && defined (ENABLE_ALTIVEC))
+    accel = arch_accel ();
+#elif defined (HAVE_MLIB)
+#ifdef MLIB_LAZYLOAD
+    void *hndl;
+
+    if ((hndl = dlopen("libmlib.so.2", RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE)) == NULL) {
+      accel = 0;
+    }
+    else {
+      dlclose(hndl);
+      accel = MM_ACCEL_MLIB;
+    }
+#else
+    accel = MM_ACCEL_MLIB;
+#endif
+#else
+    accel = 0;
+#endif
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+#ifndef _MSC_VER
+    /* test OS support for SSE */
+    if( accel & MM_ACCEL_X86_SSE ) {
+      void (*old_sigill_handler)(int);
+
+      old_sigill_handler = signal (SIGILL, sigill_handler); 
+
+      if (setjmp(sigill_return)) {
+       lprintf ("OS doesn't support SSE instructions.\n");
+       accel &= ~(MM_ACCEL_X86_SSE|MM_ACCEL_X86_SSE2);
+      } else {
+       __asm__ volatile ("xorps %xmm0, %xmm0");
+      }
+
+      signal (SIGILL, old_sigill_handler);
+    }
+#endif /* _MSC_VER */
+#endif /* ARCH_X86 || ARCH_X86_64 */
+    
+    if(getenv("XINE_NO_ACCEL")) {
+      accel = 0;
+    }
+
+    initialized = 1;
+  }
+
+  return accel;
+}
diff --git a/src/modules/xine/deinterlace.c b/src/modules/xine/deinterlace.c
new file mode 100644 (file)
index 0000000..4a99e94
--- /dev/null
@@ -0,0 +1,860 @@
+ /*
+ * Copyright (C) 2001 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ *
+ * Deinterlace routines by Miguel Freitas
+ * based of DScaler project sources (deinterlace.sourceforge.net)
+ *
+ * Currently only available for Xv driver and MMX extensions
+ *
+ * small todo list:
+ * - implement non-MMX versions for all methods
+ * - support MMX2 instructions
+ * - move some generic code from xv driver to this file
+ * - make it also work for yuy2 frames
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "deinterlace.h"
+#include "xineutils.h"
+
+#define xine_fast_memcpy memcpy
+#define xine_fast_memmove memmove
+
+/*
+   DeinterlaceFieldBob algorithm
+   Based on Virtual Dub plugin by Gunnar Thalin
+   MMX asm version from dscaler project (deinterlace.sourceforge.net)
+   Linux version for Xine player by Miguel Freitas
+*/
+static void deinterlace_bob_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height )
+{
+#ifdef USE_MMX
+  int Line;
+  uint64_t *YVal1;
+  uint64_t *YVal2;
+  uint64_t *YVal3;
+  uint64_t *Dest;
+  uint8_t* pEvenLines = psrc[0];
+  uint8_t* pOddLines = psrc[0]+width;
+  int LineLength = width;
+  int SourcePitch = width * 2;
+  int IsOdd = 1;
+  long EdgeDetect = 625;
+  long JaggieThreshold = 73;
+
+  int n;
+
+  uint64_t qwEdgeDetect;
+  uint64_t qwThreshold;
+
+  static mmx_t YMask = {ub:{0xff,0,0xff,0,0xff,0,0xff,0}};
+  static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+  qwEdgeDetect = EdgeDetect;
+  qwEdgeDetect += (qwEdgeDetect << 48) + (qwEdgeDetect << 32) + (qwEdgeDetect << 16);
+  qwThreshold = JaggieThreshold;
+  qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16);
+
+
+  // copy first even line no matter what, and the first odd line if we're
+  // processing an odd field.
+  xine_fast_memcpy(pdst, pEvenLines, LineLength);
+  if (IsOdd)
+    xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+  height = height / 2;
+  for (Line = 0; Line < height - 1; ++Line)
+  {
+    if (IsOdd)
+    {
+      YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+    }
+    else
+    {
+      YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+      YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+    }
+
+    // For ease of reading, the comments below assume that we're operating on an odd
+    // field (i.e., that bIsOdd is true).  The exact same processing is done when we
+    // operate on an even field, but the roles of the odd and even fields are reversed.
+    // It's just too cumbersome to explain the algorithm in terms of "the next odd
+    // line if we're doing an odd field, or the next even line if we're doing an
+    // even field" etc.  So wherever you see "odd" or "even" below, keep in mind that
+    // half the time this function is called, those words' meanings will invert.
+
+    // Copy the odd line to the overlay verbatim.
+    xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+    n = LineLength >> 3;
+    while( n-- )
+    {
+      movq_m2r (*YVal1++, mm0);
+      movq_m2r (*YVal2++, mm1);
+      movq_m2r (*YVal3++, mm2);
+
+      // get intensities in mm3 - 4
+      movq_r2r ( mm0, mm3 );
+      pand_m2r ( YMask, mm3 );
+      movq_r2r ( mm1, mm4 );
+      pand_m2r ( YMask, mm4 );
+      movq_r2r ( mm2, mm5 );
+      pand_m2r ( YMask, mm5 );
+
+      // get average in mm0
+      pand_m2r ( Mask, mm0 );
+      pand_m2r ( Mask, mm2 );
+      psrlw_i2r ( 01, mm0 );
+      psrlw_i2r ( 01, mm2 );
+      paddw_r2r ( mm2, mm0 );
+
+      // work out (O1 - E) * (O2 - E) / 2 - EdgeDetect * (O1 - O2) ^ 2 >> 12
+      // result will be in mm6
+
+      psrlw_i2r ( 01, mm3 );
+      psrlw_i2r ( 01, mm4 );
+      psrlw_i2r ( 01, mm5 );
+
+      movq_r2r ( mm3, mm6 );
+      psubw_r2r ( mm4, mm6 );  //mm6 = O1 - E
+
+      movq_r2r ( mm5, mm7 );
+      psubw_r2r ( mm4, mm7 );  //mm7 = O2 - E
+
+      pmullw_r2r ( mm7, mm6 );         // mm6 = (O1 - E) * (O2 - E)
+
+      movq_r2r ( mm3, mm7 );
+      psubw_r2r ( mm5, mm7 );          // mm7 = (O1 - O2)
+      pmullw_r2r ( mm7, mm7 ); // mm7 = (O1 - O2) ^ 2
+      psrlw_i2r ( 12, mm7 );           // mm7 = (O1 - O2) ^ 2 >> 12
+      pmullw_m2r ( *&qwEdgeDetect, mm7 );// mm7  = EdgeDetect * (O1 - O2) ^ 2 >> 12
+
+      psubw_r2r ( mm7, mm6 );      // mm6 is what we want
+
+      pcmpgtw_m2r ( *&qwThreshold, mm6 );
+
+      movq_r2r ( mm6, mm7 );
+
+      pand_r2r ( mm6, mm0 );
+
+      pandn_r2r ( mm1, mm7 );
+
+      por_r2r ( mm0, mm7 );
+
+      movq_r2m ( mm7, *Dest++ );
+    }
+  }
+
+  // Copy last odd line if we're processing an even field.
+  if (! IsOdd)
+  {
+    xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+                      pOddLines + (height - 1) * SourcePitch,
+                      LineLength);
+  }
+
+  // clear out the MMX registers ready for doing floating point
+  // again
+  emms();
+#endif
+}
+
+/* Deinterlace the latest field, with a tendency to weave rather than bob.
+   Good for high detail on low-movement scenes.
+   Seems to produce bad output in general case, need to check if this
+   is normal or if the code is broken.
+*/
+static int deinterlace_weave_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height )
+{
+#ifdef USE_MMX
+
+  int Line;
+  uint64_t *YVal1;
+  uint64_t *YVal2;
+  uint64_t *YVal3;
+  uint64_t *YVal4;
+  uint64_t *Dest;
+  uint8_t* pEvenLines = psrc[0];
+  uint8_t* pOddLines = psrc[0]+width;
+  uint8_t* pPrevLines;
+
+  int LineLength = width;
+  int SourcePitch = width * 2;
+  int IsOdd = 1;
+
+  long TemporalTolerance = 300;
+  long SpatialTolerance = 600;
+  long SimilarityThreshold = 25;
+
+  int n;
+
+  uint64_t qwSpatialTolerance;
+  uint64_t qwTemporalTolerance;
+  uint64_t qwThreshold;
+
+  static mmx_t YMask = {ub:{0xff,0,0xff,0,0xff,0,0xff,0}};
+  static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+
+  // Make sure we have all the data we need.
+  if ( psrc[0] == NULL || psrc[1] == NULL )
+    return 0;
+
+  if (IsOdd)
+    pPrevLines = psrc[1] + width;
+  else
+    pPrevLines = psrc[1];
+
+  // Since the code uses MMX to process 4 pixels at a time, we need our constants
+  // to be represented 4 times per quadword.
+  qwSpatialTolerance = SpatialTolerance;
+  qwSpatialTolerance += (qwSpatialTolerance << 48) + (qwSpatialTolerance << 32) + (qwSpatialTolerance << 16);
+  qwTemporalTolerance = TemporalTolerance;
+  qwTemporalTolerance += (qwTemporalTolerance << 48) + (qwTemporalTolerance << 32) + (qwTemporalTolerance << 16);
+  qwThreshold = SimilarityThreshold;
+  qwThreshold += (qwThreshold << 48) + (qwThreshold << 32) + (qwThreshold << 16);
+
+  // copy first even line no matter what, and the first odd line if we're
+  // processing an even field.
+  xine_fast_memcpy(pdst, pEvenLines, LineLength);
+  if (!IsOdd)
+    xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+  height = height / 2;
+  for (Line = 0; Line < height - 1; ++Line)
+  {
+    if (IsOdd)
+    {
+      YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+      YVal2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      YVal4 = (uint64_t *)(pPrevLines + Line * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+    }
+    else
+    {
+      YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      YVal2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+      YVal4 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+    }
+
+    // For ease of reading, the comments below assume that we're operating on an odd
+    // field (i.e., that bIsOdd is true).  The exact same processing is done when we
+    // operate on an even field, but the roles of the odd and even fields are reversed.
+    // It's just too cumbersome to explain the algorithm in terms of "the next odd
+    // line if we're doing an odd field, or the next even line if we're doing an
+    // even field" etc.  So wherever you see "odd" or "even" below, keep in mind that
+    // half the time this function is called, those words' meanings will invert.
+
+    // Copy the even scanline below this one to the overlay buffer, since we'll be
+    // adapting the current scanline to the even lines surrounding it.  The scanline
+    // above has already been copied by the previous pass through the loop.
+    xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+    n = LineLength >> 3;
+    while( n-- )
+    {
+      movq_m2r ( *YVal1++, mm0 );    // mm0 = E1
+      movq_m2r ( *YVal2++, mm1 );    // mm1 = O
+      movq_m2r ( *YVal3++, mm2 );    // mm2 = E2
+
+      movq_r2r ( mm0, mm3 );       // mm3 = intensity(E1)
+      movq_r2r ( mm1, mm4 );       // mm4 = intensity(O)
+      movq_r2r ( mm2, mm6 );       // mm6 = intensity(E2)
+
+      pand_m2r ( YMask, mm3 );
+      pand_m2r ( YMask, mm4 );
+      pand_m2r ( YMask, mm6 );
+
+      // Average E1 and E2 for interpolated bobbing.
+      // leave result in mm0
+      pand_m2r ( Mask, mm0 ); // mm0 = E1 with lower chroma bit stripped off
+      pand_m2r ( Mask, mm2 ); // mm2 = E2 with lower chroma bit stripped off
+      psrlw_i2r ( 01, mm0 );    // mm0 = E1 / 2
+      psrlw_i2r ( 01, mm2 );    // mm2 = E2 / 2
+      paddb_r2r ( mm2, mm0 );
+
+      // The meat of the work is done here.  We want to see whether this pixel is
+      // close in luminosity to ANY of: its top neighbor, its bottom neighbor,
+      // or its predecessor.  To do this without branching, we use MMX's
+      // saturation feature, which gives us Z(x) = x if x>=0, or 0 if x<0.
+      //
+      // The formula we're computing here is
+      //               Z(ST - (E1 - O) ^ 2) + Z(ST - (E2 - O) ^ 2) + Z(TT - (Oold - O) ^ 2)
+      // where ST is spatial tolerance and TT is temporal tolerance.  The idea
+      // is that if a pixel is similar to none of its neighbors, the resulting
+      // value will be pretty low, probably zero.  A high value therefore indicates
+      // that the pixel had a similar neighbor.  The pixel in the same position
+      // in the field before last (Oold) is considered a neighbor since we want
+      // to be able to display 1-pixel-high horizontal lines.
+
+      movq_m2r ( *&qwSpatialTolerance, mm7 );
+      movq_r2r ( mm3, mm5 );     // mm5 = E1
+      psubsw_r2r ( mm4, mm5 );   // mm5 = E1 - O
+      psraw_i2r ( 1, mm5 );
+      pmullw_r2r ( mm5, mm5 );   // mm5 = (E1 - O) ^ 2
+      psubusw_r2r ( mm5, mm7 );  // mm7 = ST - (E1 - O) ^ 2, or 0 if that's negative
+
+      movq_m2r ( *&qwSpatialTolerance, mm3 );
+      movq_r2r ( mm6, mm5 );    // mm5 = E2
+      psubsw_r2r ( mm4, mm5 );  // mm5 = E2 - O
+      psraw_i2r ( 1, mm5 );
+      pmullw_r2r ( mm5, mm5 );  // mm5 = (E2 - O) ^ 2
+      psubusw_r2r ( mm5, mm3 ); // mm0 = ST - (E2 - O) ^ 2, or 0 if that's negative
+      paddusw_r2r ( mm3, mm7 ); // mm7 = (ST - (E1 - O) ^ 2) + (ST - (E2 - O) ^ 2)
+
+      movq_m2r ( *&qwTemporalTolerance, mm3 );
+      movq_m2r ( *YVal4++, mm5 ); // mm5 = Oold
+      pand_m2r ( YMask, mm5 );
+      psubsw_r2r ( mm4, mm5 );  // mm5 = Oold - O
+      psraw_i2r ( 1, mm5 ); // XXX
+      pmullw_r2r ( mm5, mm5 );  // mm5 = (Oold - O) ^ 2
+      psubusw_r2r ( mm5, mm3 ); /* mm0 = TT - (Oold - O) ^ 2, or 0 if that's negative */
+      paddusw_r2r ( mm3, mm7 ); // mm7 = our magic number
+
+      /*
+       * Now compare the similarity totals against our threshold.  The pcmpgtw
+       * instruction will populate the target register with a bunch of mask bits,
+       * filling words where the comparison is true with 1s and ones where it's
+       * false with 0s.  A few ANDs and NOTs and an OR later, we have bobbed
+       * values for pixels under the similarity threshold and weaved ones for
+       * pixels over the threshold.
+       */
+
+      pcmpgtw_m2r( *&qwThreshold, mm7 ); // mm7 = 0xffff where we're greater than the threshold, 0 elsewhere
+      movq_r2r ( mm7, mm6 );  // mm6 = 0xffff where we're greater than the threshold, 0 elsewhere
+      pand_r2r ( mm1, mm7 );  // mm7 = weaved data where we're greater than the threshold, 0 elsewhere
+      pandn_r2r ( mm0, mm6 ); // mm6 = bobbed data where we're not greater than the threshold, 0 elsewhere
+      por_r2r ( mm6, mm7 );   // mm7 = bobbed and weaved data
+
+      movq_r2m ( mm7, *Dest++ );
+    }
+  }
+
+  // Copy last odd line if we're processing an odd field.
+  if (IsOdd)
+  {
+    xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+                      pOddLines + (height - 1) * SourcePitch,
+                      LineLength);
+  }
+
+  // clear out the MMX registers ready for doing floating point
+  // again
+  emms();
+
+#endif
+
+  return 1;
+}
+
+
+// This is a simple lightweight DeInterlace method that uses little CPU time
+// but gives very good results for low or intermedite motion. (MORE CPU THAN BOB)
+// It defers frames by one field, but that does not seem to produce noticeable
+// lip sync problems.
+//
+// The method used is to take either the older or newer weave pixel depending
+// upon which give the smaller comb factor, and then clip to avoid large damage
+// when wrong.
+//
+// I'd intended this to be part of a larger more elaborate method added to
+// Blended Clip but this give too good results for the CPU to ignore here.
+static int deinterlace_greedy_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height )
+{
+#ifdef USE_MMX
+  int Line;
+  int  LoopCtr;
+  uint64_t *L1;                                        // ptr to Line1, of 3
+  uint64_t *L2;                                        // ptr to Line2, the weave line
+  uint64_t *L3;                                        // ptr to Line3
+  uint64_t *LP2;                                       // ptr to prev Line2
+  uint64_t *Dest;
+  uint8_t* pEvenLines = psrc[0];
+  uint8_t* pOddLines = psrc[0]+width;
+  uint8_t* pPrevLines;
+
+  static mmx_t ShiftMask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+  int LineLength = width;
+  int SourcePitch = width * 2;
+  int IsOdd = 1;
+  long GreedyMaxComb = 15;
+  static mmx_t MaxComb;
+  int i;
+
+  if ( psrc[0] == NULL || psrc[1] == NULL )
+    return 0;
+
+  if (IsOdd)
+    pPrevLines = psrc[1] + width;
+  else
+    pPrevLines = psrc[1];
+
+
+  for( i = 0; i < 8; i++ )
+    MaxComb.ub[i] = GreedyMaxComb; // How badly do we let it weave? 0-255
+
+
+  // copy first even line no matter what, and the first odd line if we're
+  // processing an EVEN field. (note diff from other deint rtns.)
+  xine_fast_memcpy(pdst, pEvenLines, LineLength); //DL0
+  if (!IsOdd)
+    xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength); //DL1
+
+  height = height / 2;
+  for (Line = 0; Line < height - 1; ++Line)
+  {
+    LoopCtr = LineLength / 8;                          // there are LineLength / 8 qwords per line
+
+    if (IsOdd)
+    {
+      L1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+      L2 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      L3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      LP2 = (uint64_t *)(pPrevLines + Line * SourcePitch); // prev Odd lines
+      Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+    }
+    else
+    {
+      L1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      L2 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      L3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+      LP2 = (uint64_t *)(pPrevLines + (Line + 1) * SourcePitch); //prev even lines
+      Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+    }
+
+    xine_fast_memcpy((char *)Dest + LineLength, L3, LineLength);
+
+// For ease of reading, the comments below assume that we're operating on an odd
+// field (i.e., that info->IsOdd is true).  Assume the obvious for even lines..
+
+    while( LoopCtr-- )
+    {
+      movq_m2r ( *L1++, mm1 );
+      movq_m2r ( *L2++, mm2 );
+      movq_m2r ( *L3++, mm3 );
+      movq_m2r ( *LP2++, mm0 );
+
+      // average L1 and L3 leave result in mm4
+      movq_r2r ( mm1, mm4 );   // L1
+
+      pand_m2r ( ShiftMask, mm4 );
+      psrlw_i2r ( 01, mm4 );
+      movq_r2r ( mm3, mm5 );  // L3
+      pand_m2r ( ShiftMask, mm5 );
+      psrlw_i2r ( 01, mm5 );
+      paddb_r2r ( mm5, mm4 );  // the average, for computing comb
+
+      // get abs value of possible L2 comb
+      movq_r2r ( mm2, mm7 );                           // L2
+      psubusb_r2r ( mm4, mm7 );                                // L2 - avg
+      movq_r2r ( mm4, mm5 );                           // avg
+      psubusb_r2r ( mm2, mm5 );                                // avg - L2
+      por_r2r ( mm7, mm5 );                            // abs(avg-L2)
+      movq_r2r ( mm4, mm6 );     // copy of avg for later
+
+      // get abs value of possible LP2 comb
+      movq_r2r ( mm0, mm7 );                           // LP2
+      psubusb_r2r ( mm4, mm7 );                                // LP2 - avg
+      psubusb_r2r ( mm0, mm4 );                                // avg - LP2
+      por_r2r ( mm7, mm4 );                            // abs(avg-LP2)
+
+      // use L2 or LP2 depending upon which makes smaller comb
+      psubusb_r2r ( mm5, mm4 );                                // see if it goes to zero
+      psubusb_r2r ( mm5, mm5 );                                // 0
+      pcmpeqb_r2r ( mm5, mm4 );                                // if (mm4=0) then FF else 0
+      pcmpeqb_r2r ( mm4, mm5 );                                // opposite of mm4
+
+      // if Comb(LP2) <= Comb(L2) then mm4=ff, mm5=0 else mm4=0, mm5 = 55
+      pand_r2r ( mm2, mm5 );                           // use L2 if mm5 == ff, else 0
+      pand_r2r ( mm0, mm4 );                           // use LP2 if mm4 = ff, else 0
+      por_r2r ( mm5, mm4 );                            // may the best win
+
+      // Now lets clip our chosen value to be not outside of the range
+      // of the high/low range L1-L3 by more than abs(L1-L3)
+      // This allows some comb but limits the damages and also allows more
+      // detail than a boring oversmoothed clip.
+
+      movq_r2r ( mm1, mm2 );                           // copy L1
+      psubusb_r2r ( mm3, mm2 );                                // - L3, with saturation
+      paddusb_r2r ( mm3, mm2 );                // now = Max(L1,L3)
+
+      pcmpeqb_r2r ( mm7, mm7 );                                // all ffffffff
+      psubusb_r2r ( mm1, mm7 );                                // - L1
+      paddusb_r2r ( mm7, mm3 );                                // add, may sat at fff..
+      psubusb_r2r ( mm7, mm3 );                                // now = Min(L1,L3)
+
+      // allow the value to be above the high or below the low by amt of MaxComb
+      paddusb_m2r ( MaxComb, mm2 );                    // increase max by diff
+      psubusb_m2r ( MaxComb, mm3 );                    // lower min by diff
+
+      psubusb_r2r ( mm3, mm4 );                                // best - Min
+      paddusb_r2r ( mm3, mm4 );                                // now = Max(best,Min(L1,L3)
+
+      pcmpeqb_r2r ( mm7, mm7 );                                // all ffffffff
+      psubusb_r2r ( mm4, mm7 );                                // - Max(best,Min(best,L3)
+      paddusb_r2r ( mm7, mm2 );                                // add may sat at FFF..
+      psubusb_r2r ( mm7, mm2 );                                // now = Min( Max(best, Min(L1,L3), L2 )=L2 clipped
+
+      movq_r2m ( mm2, *Dest++ );        // move in our clipped best
+
+    }
+  }
+
+  /* Copy last odd line if we're processing an Odd field. */
+  if (IsOdd)
+  {
+    xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+                      pOddLines + (height - 1) * SourcePitch,
+                      LineLength);
+  }
+
+  /* clear out the MMX registers ready for doing floating point again */
+  emms();
+
+#endif
+
+  return 1;
+}
+
+/* Use one field to interpolate the other (low cpu utilization)
+   Will lose resolution but does not produce weaving effect
+   (good for fast moving scenes) also know as "linear interpolation"
+*/
+static void deinterlace_onefield_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height )
+{
+#ifdef USE_MMX
+  int Line;
+  uint64_t *YVal1;
+  uint64_t *YVal3;
+  uint64_t *Dest;
+  uint8_t* pEvenLines = psrc[0];
+  uint8_t* pOddLines = psrc[0]+width;
+  int LineLength = width;
+  int SourcePitch = width * 2;
+  int IsOdd = 1;
+
+  int n;
+
+  static mmx_t Mask = {ub:{0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};
+
+  /*
+   * copy first even line no matter what, and the first odd line if we're
+   * processing an odd field.
+   */
+
+  xine_fast_memcpy(pdst, pEvenLines, LineLength);
+  if (IsOdd)
+    xine_fast_memcpy(pdst + LineLength, pOddLines, LineLength);
+
+  height = height / 2;
+  for (Line = 0; Line < height - 1; ++Line)
+  {
+    if (IsOdd)
+    {
+      YVal1 = (uint64_t *)(pOddLines + Line * SourcePitch);
+      YVal3 = (uint64_t *)(pOddLines + (Line + 1) * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 2) * LineLength);
+    }
+    else
+    {
+      YVal1 = (uint64_t *)(pEvenLines + Line * SourcePitch);
+      YVal3 = (uint64_t *)(pEvenLines + (Line + 1) * SourcePitch);
+      Dest = (uint64_t *)(pdst + (Line * 2 + 1) * LineLength);
+    }
+
+    // Copy the odd line to the overlay verbatim.
+    xine_fast_memcpy((char *)Dest + LineLength, YVal3, LineLength);
+
+    n = LineLength >> 3;
+    while( n-- )
+    {
+      movq_m2r (*YVal1++, mm0);
+      movq_m2r (*YVal3++, mm2);
+
+      // get average in mm0
+      pand_m2r ( Mask, mm0 );
+      pand_m2r ( Mask, mm2 );
+      psrlw_i2r ( 01, mm0 );
+      psrlw_i2r ( 01, mm2 );
+      paddw_r2r ( mm2, mm0 );
+
+      movq_r2m ( mm0, *Dest++ );
+    }
+  }
+
+  /* Copy last odd line if we're processing an even field. */
+  if (! IsOdd)
+  {
+    xine_fast_memcpy(pdst + (height * 2 - 1) * LineLength,
+                      pOddLines + (height - 1) * SourcePitch,
+                      LineLength);
+  }
+
+  /* clear out the MMX registers ready for doing floating point
+   * again
+   */
+  emms();
+#endif
+}
+
+/* Linear Blend filter - does a kind of vertical blurring on the image.
+   (idea borrowed from mplayer's sources)
+*/
+static void deinterlace_linearblend_yuv_mmx( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height )
+{
+#ifdef USE_MMX
+  int Line;
+  uint64_t *YVal1;
+  uint64_t *YVal2;
+  uint64_t *YVal3;
+  uint64_t *Dest;
+  int LineLength = width;
+
+  int n;
+
+  /* Copy first line */
+  xine_fast_memmove(pdst, psrc[0], LineLength);
+
+  for (Line = 1; Line < height - 1; ++Line)
+  {
+    YVal1 = (uint64_t *)(psrc[0] + (Line - 1) * LineLength);
+    YVal2 = (uint64_t *)(psrc[0] + (Line) * LineLength);
+    YVal3 = (uint64_t *)(psrc[0] + (Line + 1) * LineLength);
+    Dest = (uint64_t *)(pdst + Line * LineLength);
+
+    n = LineLength >> 3;
+    while( n-- )
+    {
+      /* load data from 3 lines */
+      movq_m2r (*YVal1++, mm0);
+      movq_m2r (*YVal2++, mm1);
+      movq_m2r (*YVal3++, mm2);
+
+      /* expand bytes to words */
+      punpckhbw_r2r (mm0, mm3);
+      punpckhbw_r2r (mm1, mm4);
+      punpckhbw_r2r (mm2, mm5);
+      punpcklbw_r2r (mm0, mm0);
+      punpcklbw_r2r (mm1, mm1);
+      punpcklbw_r2r (mm2, mm2);
+
+      /*
+       * deinterlacing:
+       * deint_line = (line0 + 2*line1 + line2) / 4
+       */
+      psrlw_i2r (07, mm0);
+      psrlw_i2r (06, mm1);
+      psrlw_i2r (07, mm2);
+      psrlw_i2r (07, mm3);
+      psrlw_i2r (06, mm4);
+      psrlw_i2r (07, mm5);
+      paddw_r2r (mm1, mm0);
+      paddw_r2r (mm2, mm0);
+      paddw_r2r (mm4, mm3);
+      paddw_r2r (mm5, mm3);
+      psrlw_i2r (03, mm0);
+      psrlw_i2r (03, mm3);
+
+      /* pack 8 words to 8 bytes in mm0 */
+      packuswb_r2r (mm3, mm0);
+
+      movq_r2m ( mm0, *Dest++ );
+    }
+  }
+
+  /* Copy last line */
+  xine_fast_memmove(pdst + Line * LineLength,
+                   psrc[0] + Line * LineLength, LineLength);
+
+  /* clear out the MMX registers ready for doing floating point
+   * again
+   */
+  emms();
+#endif
+}
+
+/* Linear Blend filter - C version contributed by Rogerio Brito.
+   This algorithm has the same interface as the other functions.
+
+   The destination "screen" (pdst) is constructed from the source
+   screen (psrc[0]) line by line.
+
+   The i-th line of the destination screen is the average of 3 lines
+   from the source screen: the (i-1)-th, i-th and (i+1)-th lines, with
+   the i-th line having weight 2 in the computation.
+
+   Remarks:
+   * each line on pdst doesn't depend on previous lines;
+   * due to the way the algorithm is defined, the first & last lines of the
+     screen aren't deinterlaced.
+
+*/
+static void deinterlace_linearblend_yuv( uint8_t *pdst, uint8_t *psrc[],
+                                         int width, int height )
+{
+  register int x, y;
+  register uint8_t *l0, *l1, *l2, *l3;
+
+  l0 = pdst;           /* target line */
+  l1 = psrc[0];                /* 1st source line */
+  l2 = l1 + width;     /* 2nd source line = line that follows l1 */
+  l3 = l2 + width;     /* 3rd source line = line that follows l2 */
+
+  /* Copy the first line */
+  xine_fast_memcpy(l0, l1, width);
+  l0 += width;
+
+  for (y = 1; y < height-1; ++y) {
+    /* computes avg of: l1 + 2*l2 + l3 */
+
+    for (x = 0; x < width; ++x) {
+      l0[x] = (l1[x] + (l2[x]<<1) + l3[x]) >> 2;
+    }
+
+    /* updates the line pointers */
+    l1 = l2; l2 = l3; l3 += width;
+    l0 += width;
+  }
+
+  /* Copy the last line */
+  xine_fast_memcpy(l0, l1, width);
+}
+
+static int check_for_mmx(void)
+{
+#ifdef USE_MMX
+static int config_flags = -1;
+
+  if ( config_flags == -1 )
+    config_flags = xine_mm_accel();
+  if (config_flags & MM_ACCEL_X86_MMX)
+    return 1;
+  return 0;
+#else
+  return 0;
+#endif
+}
+
+/* generic YUV deinterlacer
+   pdst -> pointer to destination bitmap
+   psrc -> array of pointers to source bitmaps ([0] = most recent)
+   width,height -> dimension for bitmaps
+   method -> DEINTERLACE_xxx
+*/
+
+void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height, int method )
+{
+  switch( method ) {
+    case DEINTERLACE_NONE:
+      xine_fast_memcpy(pdst,psrc[0],width*height);
+      break;
+    case DEINTERLACE_BOB:
+      if( check_for_mmx() )
+        deinterlace_bob_yuv_mmx(pdst,psrc,width,height);
+      else /* FIXME: provide an alternative? */
+        xine_fast_memcpy(pdst,psrc[0],width*height);
+      break;
+    case DEINTERLACE_WEAVE:
+      if( check_for_mmx() )
+      {
+        if( !deinterlace_weave_yuv_mmx(pdst,psrc,width,height) )
+          xine_fast_memcpy(pdst,psrc[0],width*height);
+      }
+      else /* FIXME: provide an alternative? */
+        xine_fast_memcpy(pdst,psrc[0],width*height);
+      break;
+    case DEINTERLACE_GREEDY:
+      if( check_for_mmx() )
+      {
+        if( !deinterlace_greedy_yuv_mmx(pdst,psrc,width,height) )
+          xine_fast_memcpy(pdst,psrc[0],width*height);
+      }
+      else /* FIXME: provide an alternative? */
+        xine_fast_memcpy(pdst,psrc[0],width*height);
+      break;
+    case DEINTERLACE_ONEFIELD:
+      if( check_for_mmx() )
+        deinterlace_onefield_yuv_mmx(pdst,psrc,width,height);
+      else /* FIXME: provide an alternative? */
+        xine_fast_memcpy(pdst,psrc[0],width*height);
+      break;
+    case DEINTERLACE_ONEFIELDXV:
+      lprintf("ONEFIELDXV must be handled by the video driver.\n");
+      break;
+    case DEINTERLACE_LINEARBLEND:
+      if( check_for_mmx() )
+        deinterlace_linearblend_yuv_mmx(pdst,psrc,width,height);
+      else
+        deinterlace_linearblend_yuv(pdst,psrc,width,height);
+      break;
+    default:
+      lprintf("unknown method %d.\n",method);
+      break;
+  }
+}
+
+int deinterlace_yuv_supported ( int method )
+{
+  switch( method ) {
+    case DEINTERLACE_NONE:
+      return 1;
+    case DEINTERLACE_BOB:
+    case DEINTERLACE_WEAVE:
+    case DEINTERLACE_GREEDY:
+    case DEINTERLACE_ONEFIELD:
+      return check_for_mmx();
+    case DEINTERLACE_ONEFIELDXV:
+      lprintf ("ONEFIELDXV must be handled by the video driver.\n");
+      return 0;
+    case DEINTERLACE_LINEARBLEND:
+      return 1;
+  }
+
+  return 0;
+}
+
+const char *deinterlace_methods[] = {
+  "none",
+  "bob",
+  "weave",
+  "greedy",
+  "onefield",
+  "onefield_xv",
+  "linearblend",
+  NULL
+};
+
+
diff --git a/src/modules/xine/deinterlace.h b/src/modules/xine/deinterlace.h
new file mode 100644 (file)
index 0000000..0cad92a
--- /dev/null
@@ -0,0 +1,47 @@
+ /*
+ * Copyright (C) 2001 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ *
+ * Deinterlace routines by Miguel Freitas
+ * based of DScaler project sources (deinterlace.sourceforge.net)
+ *
+ * Currently only available for Xv driver and MMX extensions
+ *
+ */
+
+#ifndef __DEINTERLACE_H__
+#define __DEINTERLACE_H__
+
+//#include "video_out.h"
+#include <stdint.h>
+
+int deinterlace_yuv_supported ( int method );
+void deinterlace_yuv( uint8_t *pdst, uint8_t *psrc[],
+    int width, int height, int method );
+
+#define DEINTERLACE_NONE        0
+#define DEINTERLACE_BOB         1
+#define DEINTERLACE_WEAVE       2
+#define DEINTERLACE_GREEDY      3
+#define DEINTERLACE_ONEFIELD    4
+#define DEINTERLACE_ONEFIELDXV  5
+#define DEINTERLACE_LINEARBLEND 6
+
+extern const char *deinterlace_methods[];
+
+#endif
diff --git a/src/modules/xine/factory.c b/src/modules/xine/factory.c
new file mode 100644 (file)
index 0000000..11d2c15
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+       MLT_REGISTER( filter_type, "deinterlace", filter_deinterlace_init );
+}
diff --git a/src/modules/xine/filter_deinterlace.c b/src/modules/xine/filter_deinterlace.c
new file mode 100644 (file)
index 0000000..01871cf
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * filter_deinterlace.c -- deinterlace filter
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include "deinterlace.h"
+
+#include <framework/mlt_frame.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+/** Do it :-).
+*/
+
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       int error = 0;
+       int deinterlace = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "consumer_deinterlace" );
+       
+       // Pop the service off the stack
+       mlt_filter filter = mlt_frame_pop_service( this );
+
+       // Determine if we need a writable version or not
+       if ( deinterlace && !writable )
+                writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" );
+
+       // Get the input image
+       error = mlt_frame_get_image( this, image, format, width, height, writable );
+
+       // Check that we want progressive and we aren't already progressive
+       if ( deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "progressive" ) )
+       {
+               // Determine deinterlace method
+               char *method_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "method" );
+               int method = DEINTERLACE_LINEARBLEND;
+               char *frame_method_str = mlt_properties_get( MLT_FRAME_PROPERTIES( this ), "deinterlace_method" );
+               
+               if ( frame_method_str != NULL )
+                       method_str = frame_method_str;
+               
+               if ( method_str == NULL )
+                       method = DEINTERLACE_LINEARBLEND;
+               else if ( strcmp( method_str, "bob" ) == 0 )
+                       method = DEINTERLACE_BOB;
+               else if ( strcmp( method_str, "weave" ) == 0 )
+                       method = DEINTERLACE_BOB;
+               else if ( strcmp( method_str, "greedy" ) == 0 )
+                       method = DEINTERLACE_GREEDY;
+               else if ( strcmp( method_str, "onefield" ) == 0 )
+                       method = DEINTERLACE_ONEFIELD;
+                       
+               // Deinterlace the image
+               deinterlace_yuv( *image, image, *width * 2, *height, method );
+               
+               // Make sure that others know the frame is deinterlaced
+               mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "progressive", 1 );
+       }
+
+       return error;
+}
+
+/** Deinterlace filter processing - this should be lazy evaluation here...
+*/
+
+static mlt_frame deinterlace_process( mlt_filter this, mlt_frame frame )
+{
+       // Push this on to the service stack
+       mlt_frame_push_service( frame, this );
+
+       // Push the get_image method on to the stack
+       mlt_frame_push_get_image( frame, filter_get_image );
+       
+       return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter this = mlt_filter_new( );
+       if ( this != NULL )
+       {
+               this->process = deinterlace_process;
+               mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "method", arg );
+       }
+       return this;
+}
+
diff --git a/src/modules/xine/gpl b/src/modules/xine/gpl
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/modules/xine/xineutils.h b/src/modules/xine/xineutils.h
new file mode 100644 (file)
index 0000000..f0d74ce
--- /dev/null
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2000-2004 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ *
+ * $Id$
+ *
+ */
+#ifndef XINEUTILS_H
+#define XINEUTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <pthread.h>
+#if HAVE_LIBGEN_H
+#  include <libgen.h>
+#endif
+
+//#ifdef XINE_COMPILE
+#  include "attributes.h"
+//#  include "compat.h"
+//#  include "xmlparser.h"
+//#  include "xine_buffer.h"
+//#  include "configfile.h"
+//#else
+//#  include <xine/attributes.h>
+//#  include <xine/compat.h>
+//#  include <xine/xmlparser.h>
+//#  include <xine/xine_buffer.h>
+//#  include <xine/configfile.h>
+//#endif
+
+//#ifdef HAVE_CONFIG_H
+//#include "config.h"
+//#endif
+
+#include <stdio.h>
+#include <string.h>
+
+  /*
+   * debugable mutexes
+   */
+
+  typedef struct {
+    pthread_mutex_t  mutex;
+    char             id[80];
+    char            *locked_by;
+  } xine_mutex_t;
+
+  int xine_mutex_init    (xine_mutex_t *mutex, const pthread_mutexattr_t *mutexattr,
+                         char *id);
+
+  int xine_mutex_lock    (xine_mutex_t *mutex, char *who);
+  int xine_mutex_unlock  (xine_mutex_t *mutex, char *who);
+  int xine_mutex_destroy (xine_mutex_t *mutex);
+
+
+
+                       /* CPU Acceleration */
+
+/*
+ * The type of an value that fits in an MMX register (note that long
+ * long constant values MUST be suffixed by LL and unsigned long long
+ * values by ULL, lest they be truncated by the compiler)
+ */
+
+/* generic accelerations */
+#define MM_ACCEL_MLIB           0x00000001
+
+/* x86 accelerations */
+#define MM_ACCEL_X86_MMX        0x80000000
+#define MM_ACCEL_X86_3DNOW      0x40000000
+#define MM_ACCEL_X86_MMXEXT     0x20000000
+#define MM_ACCEL_X86_SSE       0x10000000
+#define MM_ACCEL_X86_SSE2      0x08000000
+/* powerpc accelerations */
+#define MM_ACCEL_PPC_ALTIVEC    0x04000000
+/* x86 compat defines */
+#define MM_MMX                  MM_ACCEL_X86_MMX
+#define MM_3DNOW                MM_ACCEL_X86_3DNOW
+#define MM_MMXEXT               MM_ACCEL_X86_MMXEXT
+#define MM_SSE                  MM_ACCEL_X86_SSE
+#define MM_SSE2                 MM_ACCEL_X86_SSE2
+
+uint32_t xine_mm_accel (void);
+
+#ifdef USE_MMX
+
+typedef        union {
+       int64_t                 q;      /* Quadword (64-bit) value */
+       uint64_t                uq;     /* Unsigned Quadword */
+       int                     d[2];   /* 2 Doubleword (32-bit) values */
+       unsigned int            ud[2];  /* 2 Unsigned Doubleword */
+       short                   w[4];   /* 4 Word (16-bit) values */
+       unsigned short          uw[4];  /* 4 Unsigned Word */
+       char                    b[8];   /* 8 Byte (8-bit) values */
+       unsigned char           ub[8];  /* 8 Unsigned Byte */
+       float                   s[2];   /* Single-precision (32-bit) value */
+} ATTR_ALIGN(8) mmx_t; /* On an 8-byte (64-bit) boundary */
+
+
+
+#define        mmx_i2r(op,imm,reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "i" (imm) )
+
+#define        mmx_m2r(op,mem,reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "m" (mem))
+
+#define        mmx_r2m(op,reg,mem) \
+       __asm__ __volatile__ (#op " %%" #reg ", %0" \
+                             : "=m" (mem) \
+                             : /* nothing */ )
+
+#define        mmx_r2r(op,regs,regd) \
+       __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+
+#define        emms() __asm__ __volatile__ ("emms")
+
+#define        movd_m2r(var,reg)       mmx_m2r (movd, var, reg)
+#define        movd_r2m(reg,var)       mmx_r2m (movd, reg, var)
+#define        movd_r2r(regs,regd)     mmx_r2r (movd, regs, regd)
+
+#define        movq_m2r(var,reg)       mmx_m2r (movq, var, reg)
+#define        movq_r2m(reg,var)       mmx_r2m (movq, reg, var)
+#define        movq_r2r(regs,regd)     mmx_r2r (movq, regs, regd)
+
+#define        packssdw_m2r(var,reg)   mmx_m2r (packssdw, var, reg)
+#define        packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
+#define        packsswb_m2r(var,reg)   mmx_m2r (packsswb, var, reg)
+#define        packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
+
+#define        packuswb_m2r(var,reg)   mmx_m2r (packuswb, var, reg)
+#define        packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
+
+#define        paddb_m2r(var,reg)      mmx_m2r (paddb, var, reg)
+#define        paddb_r2r(regs,regd)    mmx_r2r (paddb, regs, regd)
+#define        paddd_m2r(var,reg)      mmx_m2r (paddd, var, reg)
+#define        paddd_r2r(regs,regd)    mmx_r2r (paddd, regs, regd)
+#define        paddw_m2r(var,reg)      mmx_m2r (paddw, var, reg)
+#define        paddw_r2r(regs,regd)    mmx_r2r (paddw, regs, regd)
+
+#define        paddsb_m2r(var,reg)     mmx_m2r (paddsb, var, reg)
+#define        paddsb_r2r(regs,regd)   mmx_r2r (paddsb, regs, regd)
+#define        paddsw_m2r(var,reg)     mmx_m2r (paddsw, var, reg)
+#define        paddsw_r2r(regs,regd)   mmx_r2r (paddsw, regs, regd)
+
+#define        paddusb_m2r(var,reg)    mmx_m2r (paddusb, var, reg)
+#define        paddusb_r2r(regs,regd)  mmx_r2r (paddusb, regs, regd)
+#define        paddusw_m2r(var,reg)    mmx_m2r (paddusw, var, reg)
+#define        paddusw_r2r(regs,regd)  mmx_r2r (paddusw, regs, regd)
+
+#define        pand_m2r(var,reg)       mmx_m2r (pand, var, reg)
+#define        pand_r2r(regs,regd)     mmx_r2r (pand, regs, regd)
+
+#define        pandn_m2r(var,reg)      mmx_m2r (pandn, var, reg)
+#define        pandn_r2r(regs,regd)    mmx_r2r (pandn, regs, regd)
+
+#define        pcmpeqb_m2r(var,reg)    mmx_m2r (pcmpeqb, var, reg)
+#define        pcmpeqb_r2r(regs,regd)  mmx_r2r (pcmpeqb, regs, regd)
+#define        pcmpeqd_m2r(var,reg)    mmx_m2r (pcmpeqd, var, reg)
+#define        pcmpeqd_r2r(regs,regd)  mmx_r2r (pcmpeqd, regs, regd)
+#define        pcmpeqw_m2r(var,reg)    mmx_m2r (pcmpeqw, var, reg)
+#define        pcmpeqw_r2r(regs,regd)  mmx_r2r (pcmpeqw, regs, regd)
+
+#define        pcmpgtb_m2r(var,reg)    mmx_m2r (pcmpgtb, var, reg)
+#define        pcmpgtb_r2r(regs,regd)  mmx_r2r (pcmpgtb, regs, regd)
+#define        pcmpgtd_m2r(var,reg)    mmx_m2r (pcmpgtd, var, reg)
+#define        pcmpgtd_r2r(regs,regd)  mmx_r2r (pcmpgtd, regs, regd)
+#define        pcmpgtw_m2r(var,reg)    mmx_m2r (pcmpgtw, var, reg)
+#define        pcmpgtw_r2r(regs,regd)  mmx_r2r (pcmpgtw, regs, regd)
+
+#define        pmaddwd_m2r(var,reg)    mmx_m2r (pmaddwd, var, reg)
+#define        pmaddwd_r2r(regs,regd)  mmx_r2r (pmaddwd, regs, regd)
+
+#define        pmulhw_m2r(var,reg)     mmx_m2r (pmulhw, var, reg)
+#define        pmulhw_r2r(regs,regd)   mmx_r2r (pmulhw, regs, regd)
+
+#define        pmullw_m2r(var,reg)     mmx_m2r (pmullw, var, reg)
+#define        pmullw_r2r(regs,regd)   mmx_r2r (pmullw, regs, regd)
+
+#define        por_m2r(var,reg)        mmx_m2r (por, var, reg)
+#define        por_r2r(regs,regd)      mmx_r2r (por, regs, regd)
+
+#define        pslld_i2r(imm,reg)      mmx_i2r (pslld, imm, reg)
+#define        pslld_m2r(var,reg)      mmx_m2r (pslld, var, reg)
+#define        pslld_r2r(regs,regd)    mmx_r2r (pslld, regs, regd)
+#define        psllq_i2r(imm,reg)      mmx_i2r (psllq, imm, reg)
+#define        psllq_m2r(var,reg)      mmx_m2r (psllq, var, reg)
+#define        psllq_r2r(regs,regd)    mmx_r2r (psllq, regs, regd)
+#define        psllw_i2r(imm,reg)      mmx_i2r (psllw, imm, reg)
+#define        psllw_m2r(var,reg)      mmx_m2r (psllw, var, reg)
+#define        psllw_r2r(regs,regd)    mmx_r2r (psllw, regs, regd)
+
+#define        psrad_i2r(imm,reg)      mmx_i2r (psrad, imm, reg)
+#define        psrad_m2r(var,reg)      mmx_m2r (psrad, var, reg)
+#define        psrad_r2r(regs,regd)    mmx_r2r (psrad, regs, regd)
+#define        psraw_i2r(imm,reg)      mmx_i2r (psraw, imm, reg)
+#define        psraw_m2r(var,reg)      mmx_m2r (psraw, var, reg)
+#define        psraw_r2r(regs,regd)    mmx_r2r (psraw, regs, regd)
+
+#define        psrld_i2r(imm,reg)      mmx_i2r (psrld, imm, reg)
+#define        psrld_m2r(var,reg)      mmx_m2r (psrld, var, reg)
+#define        psrld_r2r(regs,regd)    mmx_r2r (psrld, regs, regd)
+#define        psrlq_i2r(imm,reg)      mmx_i2r (psrlq, imm, reg)
+#define        psrlq_m2r(var,reg)      mmx_m2r (psrlq, var, reg)
+#define        psrlq_r2r(regs,regd)    mmx_r2r (psrlq, regs, regd)
+#define        psrlw_i2r(imm,reg)      mmx_i2r (psrlw, imm, reg)
+#define        psrlw_m2r(var,reg)      mmx_m2r (psrlw, var, reg)
+#define        psrlw_r2r(regs,regd)    mmx_r2r (psrlw, regs, regd)
+
+#define        psubb_m2r(var,reg)      mmx_m2r (psubb, var, reg)
+#define        psubb_r2r(regs,regd)    mmx_r2r (psubb, regs, regd)
+#define        psubd_m2r(var,reg)      mmx_m2r (psubd, var, reg)
+#define        psubd_r2r(regs,regd)    mmx_r2r (psubd, regs, regd)
+#define        psubw_m2r(var,reg)      mmx_m2r (psubw, var, reg)
+#define        psubw_r2r(regs,regd)    mmx_r2r (psubw, regs, regd)
+
+#define        psubsb_m2r(var,reg)     mmx_m2r (psubsb, var, reg)
+#define        psubsb_r2r(regs,regd)   mmx_r2r (psubsb, regs, regd)
+#define        psubsw_m2r(var,reg)     mmx_m2r (psubsw, var, reg)
+#define        psubsw_r2r(regs,regd)   mmx_r2r (psubsw, regs, regd)
+
+#define        psubusb_m2r(var,reg)    mmx_m2r (psubusb, var, reg)
+#define        psubusb_r2r(regs,regd)  mmx_r2r (psubusb, regs, regd)
+#define        psubusw_m2r(var,reg)    mmx_m2r (psubusw, var, reg)
+#define        psubusw_r2r(regs,regd)  mmx_r2r (psubusw, regs, regd)
+
+#define        punpckhbw_m2r(var,reg)          mmx_m2r (punpckhbw, var, reg)
+#define        punpckhbw_r2r(regs,regd)        mmx_r2r (punpckhbw, regs, regd)
+#define        punpckhdq_m2r(var,reg)          mmx_m2r (punpckhdq, var, reg)
+#define        punpckhdq_r2r(regs,regd)        mmx_r2r (punpckhdq, regs, regd)
+#define        punpckhwd_m2r(var,reg)          mmx_m2r (punpckhwd, var, reg)
+#define        punpckhwd_r2r(regs,regd)        mmx_r2r (punpckhwd, regs, regd)
+
+#define        punpcklbw_m2r(var,reg)          mmx_m2r (punpcklbw, var, reg)
+#define        punpcklbw_r2r(regs,regd)        mmx_r2r (punpcklbw, regs, regd)
+#define        punpckldq_m2r(var,reg)          mmx_m2r (punpckldq, var, reg)
+#define        punpckldq_r2r(regs,regd)        mmx_r2r (punpckldq, regs, regd)
+#define        punpcklwd_m2r(var,reg)          mmx_m2r (punpcklwd, var, reg)
+#define        punpcklwd_r2r(regs,regd)        mmx_r2r (punpcklwd, regs, regd)
+
+#define        pxor_m2r(var,reg)       mmx_m2r (pxor, var, reg)
+#define        pxor_r2r(regs,regd)     mmx_r2r (pxor, regs, regd)
+
+
+/* 3DNOW extensions */
+
+#define pavgusb_m2r(var,reg)   mmx_m2r (pavgusb, var, reg)
+#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
+
+
+/* AMD MMX extensions - also available in intel SSE */
+
+
+#define mmx_m2ri(op,mem,reg,imm) \
+        __asm__ __volatile__ (#op " %1, %0, %%" #reg \
+                              : /* nothing */ \
+                              : "X" (mem), "X" (imm))
+#define mmx_r2ri(op,regs,regd,imm) \
+        __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+                              : /* nothing */ \
+                              : "X" (imm) )
+
+#define        mmx_fetch(mem,hint) \
+       __asm__ __volatile__ ("prefetch" #hint " %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+
+#define        maskmovq(regs,maskreg)          mmx_r2ri (maskmovq, regs, maskreg)
+
+#define        movntq_r2m(mmreg,var)           mmx_r2m (movntq, mmreg, var)
+
+#define        pavgb_m2r(var,reg)              mmx_m2r (pavgb, var, reg)
+#define        pavgb_r2r(regs,regd)            mmx_r2r (pavgb, regs, regd)
+#define        pavgw_m2r(var,reg)              mmx_m2r (pavgw, var, reg)
+#define        pavgw_r2r(regs,regd)            mmx_r2r (pavgw, regs, regd)
+
+#define        pextrw_r2r(mmreg,reg,imm)       mmx_r2ri (pextrw, mmreg, reg, imm)
+
+#define        pinsrw_r2r(reg,mmreg,imm)       mmx_r2ri (pinsrw, reg, mmreg, imm)
+
+#define        pmaxsw_m2r(var,reg)             mmx_m2r (pmaxsw, var, reg)
+#define        pmaxsw_r2r(regs,regd)           mmx_r2r (pmaxsw, regs, regd)
+
+#define        pmaxub_m2r(var,reg)             mmx_m2r (pmaxub, var, reg)
+#define        pmaxub_r2r(regs,regd)           mmx_r2r (pmaxub, regs, regd)
+
+#define        pminsw_m2r(var,reg)             mmx_m2r (pminsw, var, reg)
+#define        pminsw_r2r(regs,regd)           mmx_r2r (pminsw, regs, regd)
+
+#define        pminub_m2r(var,reg)             mmx_m2r (pminub, var, reg)
+#define        pminub_r2r(regs,regd)           mmx_r2r (pminub, regs, regd)
+
+#define        pmovmskb(mmreg,reg) \
+       __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
+
+#define        pmulhuw_m2r(var,reg)            mmx_m2r (pmulhuw, var, reg)
+#define        pmulhuw_r2r(regs,regd)          mmx_r2r (pmulhuw, regs, regd)
+
+#define        prefetcht0(mem)                 mmx_fetch (mem, t0)
+#define        prefetcht1(mem)                 mmx_fetch (mem, t1)
+#define        prefetcht2(mem)                 mmx_fetch (mem, t2)
+#define        prefetchnta(mem)                mmx_fetch (mem, nta)
+
+#define        psadbw_m2r(var,reg)             mmx_m2r (psadbw, var, reg)
+#define        psadbw_r2r(regs,regd)           mmx_r2r (psadbw, regs, regd)
+
+#define        pshufw_m2r(var,reg,imm)         mmx_m2ri(pshufw, var, reg, imm)
+#define        pshufw_r2r(regs,regd,imm)       mmx_r2ri(pshufw, regs, regd, imm)
+
+#define        sfence() __asm__ __volatile__ ("sfence\n\t")
+
+typedef        union {
+       float                   sf[4];  /* Single-precision (32-bit) value */
+} ATTR_ALIGN(16) sse_t;        /* On a 16 byte (128-bit) boundary */
+
+
+#define        sse_i2r(op, imm, reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "X" (imm) )
+
+#define        sse_m2r(op, mem, reg) \
+       __asm__ __volatile__ (#op " %0, %%" #reg \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+#define        sse_r2m(op, reg, mem) \
+       __asm__ __volatile__ (#op " %%" #reg ", %0" \
+                             : "=X" (mem) \
+                             : /* nothing */ )
+
+#define        sse_r2r(op, regs, regd) \
+       __asm__ __volatile__ (#op " %" #regs ", %" #regd)
+
+#define        sse_r2ri(op, regs, regd, imm) \
+       __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
+                             : /* nothing */ \
+                             : "X" (imm) )
+
+#define        sse_m2ri(op, mem, reg, subop) \
+       __asm__ __volatile__ (#op " %0, %%" #reg ", " #subop \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+
+#define        movaps_m2r(var, reg)    sse_m2r(movaps, var, reg)
+#define        movaps_r2m(reg, var)    sse_r2m(movaps, reg, var)
+#define        movaps_r2r(regs, regd)  sse_r2r(movaps, regs, regd)
+
+#define        movntps_r2m(xmmreg, var)        sse_r2m(movntps, xmmreg, var)
+
+#define        movups_m2r(var, reg)    sse_m2r(movups, var, reg)
+#define        movups_r2m(reg, var)    sse_r2m(movups, reg, var)
+#define        movups_r2r(regs, regd)  sse_r2r(movups, regs, regd)
+
+#define        movhlps_r2r(regs, regd) sse_r2r(movhlps, regs, regd)
+
+#define        movlhps_r2r(regs, regd) sse_r2r(movlhps, regs, regd)
+
+#define        movhps_m2r(var, reg)    sse_m2r(movhps, var, reg)
+#define        movhps_r2m(reg, var)    sse_r2m(movhps, reg, var)
+
+#define        movlps_m2r(var, reg)    sse_m2r(movlps, var, reg)
+#define        movlps_r2m(reg, var)    sse_r2m(movlps, reg, var)
+
+#define        movss_m2r(var, reg)     sse_m2r(movss, var, reg)
+#define        movss_r2m(reg, var)     sse_r2m(movss, reg, var)
+#define        movss_r2r(regs, regd)   sse_r2r(movss, regs, regd)
+
+#define        shufps_m2r(var, reg, index)     sse_m2ri(shufps, var, reg, index)
+#define        shufps_r2r(regs, regd, index)   sse_r2ri(shufps, regs, regd, index)
+
+#define        cvtpi2ps_m2r(var, xmmreg)       sse_m2r(cvtpi2ps, var, xmmreg)
+#define        cvtpi2ps_r2r(mmreg, xmmreg)     sse_r2r(cvtpi2ps, mmreg, xmmreg)
+
+#define        cvtps2pi_m2r(var, mmreg)        sse_m2r(cvtps2pi, var, mmreg)
+#define        cvtps2pi_r2r(xmmreg, mmreg)     sse_r2r(cvtps2pi, mmreg, xmmreg)
+
+#define        cvttps2pi_m2r(var, mmreg)       sse_m2r(cvttps2pi, var, mmreg)
+#define        cvttps2pi_r2r(xmmreg, mmreg)    sse_r2r(cvttps2pi, mmreg, xmmreg)
+
+#define        cvtsi2ss_m2r(var, xmmreg)       sse_m2r(cvtsi2ss, var, xmmreg)
+#define        cvtsi2ss_r2r(reg, xmmreg)       sse_r2r(cvtsi2ss, reg, xmmreg)
+
+#define        cvtss2si_m2r(var, reg)          sse_m2r(cvtss2si, var, reg)
+#define        cvtss2si_r2r(xmmreg, reg)       sse_r2r(cvtss2si, xmmreg, reg)
+
+#define        cvttss2si_m2r(var, reg)         sse_m2r(cvtss2si, var, reg)
+#define        cvttss2si_r2r(xmmreg, reg)      sse_r2r(cvtss2si, xmmreg, reg)
+
+#define        movmskps(xmmreg, reg) \
+       __asm__ __volatile__ ("movmskps %" #xmmreg ", %" #reg)
+
+#define        addps_m2r(var, reg)             sse_m2r(addps, var, reg)
+#define        addps_r2r(regs, regd)           sse_r2r(addps, regs, regd)
+
+#define        addss_m2r(var, reg)             sse_m2r(addss, var, reg)
+#define        addss_r2r(regs, regd)           sse_r2r(addss, regs, regd)
+
+#define        subps_m2r(var, reg)             sse_m2r(subps, var, reg)
+#define        subps_r2r(regs, regd)           sse_r2r(subps, regs, regd)
+
+#define        subss_m2r(var, reg)             sse_m2r(subss, var, reg)
+#define        subss_r2r(regs, regd)           sse_r2r(subss, regs, regd)
+
+#define        mulps_m2r(var, reg)             sse_m2r(mulps, var, reg)
+#define        mulps_r2r(regs, regd)           sse_r2r(mulps, regs, regd)
+
+#define        mulss_m2r(var, reg)             sse_m2r(mulss, var, reg)
+#define        mulss_r2r(regs, regd)           sse_r2r(mulss, regs, regd)
+
+#define        divps_m2r(var, reg)             sse_m2r(divps, var, reg)
+#define        divps_r2r(regs, regd)           sse_r2r(divps, regs, regd)
+
+#define        divss_m2r(var, reg)             sse_m2r(divss, var, reg)
+#define        divss_r2r(regs, regd)           sse_r2r(divss, regs, regd)
+
+#define        rcpps_m2r(var, reg)             sse_m2r(rcpps, var, reg)
+#define        rcpps_r2r(regs, regd)           sse_r2r(rcpps, regs, regd)
+
+#define        rcpss_m2r(var, reg)             sse_m2r(rcpss, var, reg)
+#define        rcpss_r2r(regs, regd)           sse_r2r(rcpss, regs, regd)
+
+#define        rsqrtps_m2r(var, reg)           sse_m2r(rsqrtps, var, reg)
+#define        rsqrtps_r2r(regs, regd)         sse_r2r(rsqrtps, regs, regd)
+
+#define        rsqrtss_m2r(var, reg)           sse_m2r(rsqrtss, var, reg)
+#define        rsqrtss_r2r(regs, regd)         sse_r2r(rsqrtss, regs, regd)
+
+#define        sqrtps_m2r(var, reg)            sse_m2r(sqrtps, var, reg)
+#define        sqrtps_r2r(regs, regd)          sse_r2r(sqrtps, regs, regd)
+
+#define        sqrtss_m2r(var, reg)            sse_m2r(sqrtss, var, reg)
+#define        sqrtss_r2r(regs, regd)          sse_r2r(sqrtss, regs, regd)
+
+#define        andps_m2r(var, reg)             sse_m2r(andps, var, reg)
+#define        andps_r2r(regs, regd)           sse_r2r(andps, regs, regd)
+
+#define        andnps_m2r(var, reg)            sse_m2r(andnps, var, reg)
+#define        andnps_r2r(regs, regd)          sse_r2r(andnps, regs, regd)
+
+#define        orps_m2r(var, reg)              sse_m2r(orps, var, reg)
+#define        orps_r2r(regs, regd)            sse_r2r(orps, regs, regd)
+
+#define        xorps_m2r(var, reg)             sse_m2r(xorps, var, reg)
+#define        xorps_r2r(regs, regd)           sse_r2r(xorps, regs, regd)
+
+#define        maxps_m2r(var, reg)             sse_m2r(maxps, var, reg)
+#define        maxps_r2r(regs, regd)           sse_r2r(maxps, regs, regd)
+
+#define        maxss_m2r(var, reg)             sse_m2r(maxss, var, reg)
+#define        maxss_r2r(regs, regd)           sse_r2r(maxss, regs, regd)
+
+#define        minps_m2r(var, reg)             sse_m2r(minps, var, reg)
+#define        minps_r2r(regs, regd)           sse_r2r(minps, regs, regd)
+
+#define        minss_m2r(var, reg)             sse_m2r(minss, var, reg)
+#define        minss_r2r(regs, regd)           sse_r2r(minss, regs, regd)
+
+#define        cmpps_m2r(var, reg, op)         sse_m2ri(cmpps, var, reg, op)
+#define        cmpps_r2r(regs, regd, op)       sse_r2ri(cmpps, regs, regd, op)
+
+#define        cmpeqps_m2r(var, reg)           sse_m2ri(cmpps, var, reg, 0)
+#define        cmpeqps_r2r(regs, regd)         sse_r2ri(cmpps, regs, regd, 0)
+
+#define        cmpltps_m2r(var, reg)           sse_m2ri(cmpps, var, reg, 1)
+#define        cmpltps_r2r(regs, regd)         sse_r2ri(cmpps, regs, regd, 1)
+
+#define        cmpleps_m2r(var, reg)           sse_m2ri(cmpps, var, reg, 2)
+#define        cmpleps_r2r(regs, regd)         sse_r2ri(cmpps, regs, regd, 2)
+
+#define        cmpunordps_m2r(var, reg)        sse_m2ri(cmpps, var, reg, 3)
+#define        cmpunordps_r2r(regs, regd)      sse_r2ri(cmpps, regs, regd, 3)
+
+#define        cmpneqps_m2r(var, reg)          sse_m2ri(cmpps, var, reg, 4)
+#define        cmpneqps_r2r(regs, regd)        sse_r2ri(cmpps, regs, regd, 4)
+
+#define        cmpnltps_m2r(var, reg)          sse_m2ri(cmpps, var, reg, 5)
+#define        cmpnltps_r2r(regs, regd)        sse_r2ri(cmpps, regs, regd, 5)
+
+#define        cmpnleps_m2r(var, reg)          sse_m2ri(cmpps, var, reg, 6)
+#define        cmpnleps_r2r(regs, regd)        sse_r2ri(cmpps, regs, regd, 6)
+
+#define        cmpordps_m2r(var, reg)          sse_m2ri(cmpps, var, reg, 7)
+#define        cmpordps_r2r(regs, regd)        sse_r2ri(cmpps, regs, regd, 7)
+
+#define        cmpss_m2r(var, reg, op)         sse_m2ri(cmpss, var, reg, op)
+#define        cmpss_r2r(regs, regd, op)       sse_r2ri(cmpss, regs, regd, op)
+
+#define        cmpeqss_m2r(var, reg)           sse_m2ri(cmpss, var, reg, 0)
+#define        cmpeqss_r2r(regs, regd)         sse_r2ri(cmpss, regs, regd, 0)
+
+#define        cmpltss_m2r(var, reg)           sse_m2ri(cmpss, var, reg, 1)
+#define        cmpltss_r2r(regs, regd)         sse_r2ri(cmpss, regs, regd, 1)
+
+#define        cmpless_m2r(var, reg)           sse_m2ri(cmpss, var, reg, 2)
+#define        cmpless_r2r(regs, regd)         sse_r2ri(cmpss, regs, regd, 2)
+
+#define        cmpunordss_m2r(var, reg)        sse_m2ri(cmpss, var, reg, 3)
+#define        cmpunordss_r2r(regs, regd)      sse_r2ri(cmpss, regs, regd, 3)
+
+#define        cmpneqss_m2r(var, reg)          sse_m2ri(cmpss, var, reg, 4)
+#define        cmpneqss_r2r(regs, regd)        sse_r2ri(cmpss, regs, regd, 4)
+
+#define        cmpnltss_m2r(var, reg)          sse_m2ri(cmpss, var, reg, 5)
+#define        cmpnltss_r2r(regs, regd)        sse_r2ri(cmpss, regs, regd, 5)
+
+#define        cmpnless_m2r(var, reg)          sse_m2ri(cmpss, var, reg, 6)
+#define        cmpnless_r2r(regs, regd)        sse_r2ri(cmpss, regs, regd, 6)
+
+#define        cmpordss_m2r(var, reg)          sse_m2ri(cmpss, var, reg, 7)
+#define        cmpordss_r2r(regs, regd)        sse_r2ri(cmpss, regs, regd, 7)
+
+#define        comiss_m2r(var, reg)            sse_m2r(comiss, var, reg)
+#define        comiss_r2r(regs, regd)          sse_r2r(comiss, regs, regd)
+
+#define        ucomiss_m2r(var, reg)           sse_m2r(ucomiss, var, reg)
+#define        ucomiss_r2r(regs, regd)         sse_r2r(ucomiss, regs, regd)
+
+#define        unpcklps_m2r(var, reg)          sse_m2r(unpcklps, var, reg)
+#define        unpcklps_r2r(regs, regd)        sse_r2r(unpcklps, regs, regd)
+
+#define        unpckhps_m2r(var, reg)          sse_m2r(unpckhps, var, reg)
+#define        unpckhps_r2r(regs, regd)        sse_r2r(unpckhps, regs, regd)
+
+#define        fxrstor(mem) \
+       __asm__ __volatile__ ("fxrstor %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+#define        fxsave(mem) \
+       __asm__ __volatile__ ("fxsave %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+#define        stmxcsr(mem) \
+       __asm__ __volatile__ ("stmxcsr %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+
+#define        ldmxcsr(mem) \
+       __asm__ __volatile__ ("ldmxcsr %0" \
+                             : /* nothing */ \
+                             : "X" (mem))
+#endif /* USE_MMX */
+
+
+
+                    /* Optimized/fast memcpy */
+
+/*
+   TODO : fix dll linkage problem for xine_fast_memcpy on win32
+
+   xine_fast_memcpy dll linkage is screwy here.
+   declaring as dllimport seems to fix the problem
+   but causes compiler warning with libxineutils
+*/
+#ifdef _MSC_VER
+__declspec( dllimport ) extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len);
+#else
+extern void *(* xine_fast_memcpy)(void *to, const void *from, size_t len);
+#endif
+
+#ifdef HAVE_XINE_INTERNAL_H
+/* Benchmark available memcpy methods */
+void xine_probe_fast_memcpy(xine_t *xine);
+#endif
+
+
+/*
+ * Debug stuff
+ */
+/*
+ * profiling (unworkable in non DEBUG isn't defined)
+ */
+void xine_profiler_init (void);
+int xine_profiler_allocate_slot (char *label);
+void xine_profiler_start_count (int id);
+void xine_profiler_stop_count (int id);
+void xine_profiler_print_results (void);
+
+/*
+ * Allocate and clean memory size_t 'size', then return the pointer
+ * to the allocated memory.
+ */
+#if !defined(__GNUC__) || __GNUC__ < 3
+void *xine_xmalloc(size_t size);
+#else
+void *xine_xmalloc(size_t size) __attribute__ ((__malloc__));
+#endif
+
+/*
+ * Same as above, but memory is aligned to 'alignement'.
+ * **base is used to return pointer to un-aligned memory, use
+ * this to free the mem chunk
+ */
+void *xine_xmalloc_aligned(size_t alignment, size_t size, void **base);
+
+/*
+ * Get user home directory.
+ */
+const char *xine_get_homedir(void);
+
+/*
+ * Clean a string (remove spaces and '=' at the begin,
+ * and '\n', '\r' and spaces at the end.
+ */
+char *xine_chomp (char *str);
+
+/*
+ * A thread-safe usecond sleep
+ */
+void xine_usec_sleep(unsigned usec);
+
+
+  /*
+   * Some string functions
+   */
+
+
+void xine_strdupa(char *dest, char *src);
+#define xine_strdupa(d, s) do {                                             \
+                                (d) = NULL;                                 \
+                                if((s) != NULL) {                           \
+                                  (d) = (char *) alloca(strlen((s)) + 1);   \
+                                  strcpy((d), (s));                         \
+                                }                                           \
+                              } while(0)
+
+/* Shamefully copied from glibc 2.2.3 */
+#ifdef HAVE_STRPBRK
+#define xine_strpbrk strpbrk
+#else
+static inline const char *_private_strpbrk(const char *s, const char *accept) {
+
+  while(*s != '\0') {
+    const char *a = accept;
+    while(*a != '\0')
+      if(*a++ == *s)
+       return s;
+    ++s;
+  }
+
+  return NULL;
+}
+#define xine_strpbrk _private_strpbrk
+#endif
+
+#if defined HAVE_STRSEP && !defined(_MSC_VER)
+#define xine_strsep strsep
+#else
+static inline char *_private_strsep(char **stringp, const char *delim) {
+  char *begin, *end;
+
+  begin = *stringp;
+  if(begin == NULL)
+    return NULL;
+
+  if(delim[0] == '\0' || delim[1] == '\0') {
+    char ch = delim[0];
+
+    if(ch == '\0')
+      end = NULL;
+    else {
+      if(*begin == ch)
+       end = begin;
+      else if(*begin == '\0')
+       end = NULL;
+      else
+       end = strchr(begin + 1, ch);
+    }
+  }
+  else
+    end = xine_strpbrk(begin, delim);
+
+  if(end) {
+    *end++ = '\0';
+    *stringp = end;
+  }
+  else
+    *stringp = NULL;
+
+  return begin;
+}
+#define xine_strsep _private_strsep
+#endif
+
+
+#ifdef HAVE_SETENV
+#define        xine_setenv     setenv
+#else
+static inline void _private_setenv(const char *name, const char *val, int _xx) {
+  int  len  = strlen(name) + strlen(val) + 2;
+  char env[len];
+
+  sprintf(env, "%s%c%s", name, '=', val);
+  putenv(env);
+}
+#define        xine_setenv     _private_setenv
+#endif
+
+/*
+ * Color Conversion Utility Functions
+ * The following data structures and functions facilitate the conversion
+ * of RGB images to packed YUV (YUY2) images. There are also functions to
+ * convert from YUV9 -> YV12. All of the meaty details are written in
+ * color.c.
+ */
+
+typedef struct yuv_planes_s {
+
+  unsigned char *y;
+  unsigned char *u;
+  unsigned char *v;
+  unsigned int row_width;    /* frame width */
+  unsigned int row_count;    /* frame height */
+
+} yuv_planes_t;
+
+void init_yuv_conversion(void);
+void init_yuv_planes(yuv_planes_t *yuv_planes, int width, int height);
+void free_yuv_planes(yuv_planes_t *yuv_planes);
+
+extern void (*yuv444_to_yuy2)
+  (yuv_planes_t *yuv_planes, unsigned char *yuy2_map, int pitch);
+extern void (*yuv9_to_yv12)
+  (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch,
+   unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch,
+   unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch,
+   int width, int height);
+extern void (*yuv411_to_yv12)
+  (unsigned char *y_src, int y_src_pitch, unsigned char *y_dest, int y_dest_pitch,
+   unsigned char *u_src, int u_src_pitch, unsigned char *u_dest, int u_dest_pitch,
+   unsigned char *v_src, int v_src_pitch, unsigned char *v_dest, int v_dest_pitch,
+   int width, int height);
+extern void (*yv12_to_yuy2)
+  (unsigned char *y_src, int y_src_pitch,
+   unsigned char *u_src, int u_src_pitch,
+   unsigned char *v_src, int v_src_pitch,
+   unsigned char *yuy2_map, int yuy2_pitch,
+   int width, int height, int progressive);
+extern void (*yuy2_to_yv12)
+  (unsigned char *yuy2_map, int yuy2_pitch,
+   unsigned char *y_dst, int y_dst_pitch,
+   unsigned char *u_dst, int u_dst_pitch,
+   unsigned char *v_dst, int v_dst_pitch,
+   int width, int height);
+
+#define SCALEFACTOR 65536
+#define CENTERSAMPLE 128
+
+#define COMPUTE_Y(r, g, b) \
+  (unsigned char) \
+  ((y_r_table[r] + y_g_table[g] + y_b_table[b]) / SCALEFACTOR)
+#define COMPUTE_U(r, g, b) \
+  (unsigned char) \
+  ((u_r_table[r] + u_g_table[g] + u_b_table[b]) / SCALEFACTOR + CENTERSAMPLE)
+#define COMPUTE_V(r, g, b) \
+  (unsigned char) \
+  ((v_r_table[r] + v_g_table[g] + v_b_table[b]) / SCALEFACTOR + CENTERSAMPLE)
+
+#define UNPACK_BGR15(packed_pixel, r, g, b) \
+  b = (packed_pixel & 0x7C00) >> 7; \
+  g = (packed_pixel & 0x03E0) >> 2; \
+  r = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_BGR16(packed_pixel, r, g, b) \
+  b = (packed_pixel & 0xF800) >> 8; \
+  g = (packed_pixel & 0x07E0) >> 3; \
+  r = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_RGB15(packed_pixel, r, g, b) \
+  r = (packed_pixel & 0x7C00) >> 7; \
+  g = (packed_pixel & 0x03E0) >> 2; \
+  b = (packed_pixel & 0x001F) << 3;
+
+#define UNPACK_RGB16(packed_pixel, r, g, b) \
+  r = (packed_pixel & 0xF800) >> 8; \
+  g = (packed_pixel & 0x07E0) >> 3; \
+  b = (packed_pixel & 0x001F) << 3;
+
+extern int y_r_table[256];
+extern int y_g_table[256];
+extern int y_b_table[256];
+
+extern int u_r_table[256];
+extern int u_g_table[256];
+extern int u_b_table[256];
+
+extern int v_r_table[256];
+extern int v_g_table[256];
+extern int v_b_table[256];
+
+/* frame copying functions */
+extern void yv12_to_yv12
+  (unsigned char *y_src, int y_src_pitch, unsigned char *y_dst, int y_dst_pitch,
+   unsigned char *u_src, int u_src_pitch, unsigned char *u_dst, int u_dst_pitch,
+   unsigned char *v_src, int v_src_pitch, unsigned char *v_dst, int v_dst_pitch,
+   int width, int height);
+extern void yuy2_to_yuy2
+  (unsigned char *src, int src_pitch,
+   unsigned char *dst, int dst_pitch,
+   int width, int height);
+
+/* print a hexdump of the given data */
+void xine_hexdump (const char *buf, int length);
+
+/*
+ * Optimization macros for conditions
+ * Taken from the FIASCO L4 microkernel sources
+ */
+#if !defined(__GNUC__) || __GNUC__ < 3
+#  define EXPECT_TRUE(x)  (x)
+#  define EXPECT_FALSE(x) (x)
+#else
+#  define EXPECT_TRUE(x)  __builtin_expect((x),1)
+#  define EXPECT_FALSE(x) __builtin_expect((x),0)
+#endif
+
+#ifdef NDEBUG
+#define _x_assert(exp) \
+  do {                                                                \
+    if (!(exp))                                                       \
+      fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n",  \
+              __FILE__, __LINE__, __XINE_FUNCTION__, #exp);           \
+  } while(0)
+#else
+#define _x_assert(exp) \
+  do {                                                                \
+    if (!(exp)) {                                                     \
+      fprintf(stderr, "assert: %s:%d: %s: Assertion `%s' failed.\n",  \
+              __FILE__, __LINE__, __XINE_FUNCTION__, #exp);           \
+      abort();                                                        \
+    }                                                                 \
+  } while(0)
+#endif
+
+#define _x_abort()                                                    \
+  do {                                                                \
+    fprintf(stderr, "abort: %s:%d: %s: Aborting.\n",                  \
+            __FILE__, __LINE__, __XINE_FUNCTION__);                   \
+    abort();                                                          \
+  } while(0)
+
+
+/****** logging with xine **********************************/
+
+#ifndef LOG_MODULE
+  #define LOG_MODULE __FILE__
+#endif /* LOG_MODULE */
+
+#define LOG_MODULE_STRING printf("%s: ", LOG_MODULE );
+
+#ifdef LOG_VERBOSE
+  #define LONG_LOG_MODULE_STRING                                            \
+    printf("%s: (%s:%d) ", LOG_MODULE, __XINE_FUNCTION__, __LINE__ );
+#else
+  #define LONG_LOG_MODULE_STRING  LOG_MODULE_STRING
+#endif /* LOG_VERBOSE */
+
+#ifdef LOG
+  #ifdef __GNUC__
+    #define lprintf(fmt, args...)                                           \
+      do {                                                                  \
+        LONG_LOG_MODULE_STRING                                              \
+        printf(fmt, ##args);                                                \
+      } while(0)
+  #else /* __GNUC__ */
+    #ifdef _MSC_VER
+      #define lprintf(fmtargs)                                              \
+        do {                                                                \
+          LONG_LOG_MODULE_STRING                                            \
+          printf("%s", fmtargs);                                            \
+        } while(0)
+    #else /* _MSC_VER */
+      #define lprintf(fmt, ...)                                             \
+        do {                                                                \
+          LONG_LOG_MODULE_STRING                                            \
+          printf(__VA_ARGS__);                                              \
+        } while(0)
+    #endif  /* _MSC_VER */
+  #endif /* __GNUC__ */
+#else /* LOG */
+  #ifdef __GNUC__
+    #define lprintf(fmt, args...)     do {} while(0)
+  #else
+  #ifdef _MSC_VER
+    #define lprintf
+  #else
+    #define lprintf(...)              do {} while(0)
+  #endif /* _MSC_VER */
+  #endif /* __GNUC__ */
+#endif /* LOG */
+
+#ifdef __GNUC__
+  #define llprintf(cat, fmt, args...)                                       \
+    do{                                                                     \
+      if(cat){                                                              \
+        LONG_LOG_MODULE_STRING                                              \
+        printf( fmt, ##args );                                              \
+      }                                                                     \
+    }while(0)
+#else
+#ifdef _MSC_VER
+  #define llprintf(cat, fmtargs)                                            \
+    do{                                                                     \
+      if(cat){                                                              \
+        LONG_LOG_MODULE_STRING                                              \
+        printf( "%s", fmtargs );                                            \
+      }                                                                     \
+    }while(0)
+#else
+  #define llprintf(cat, ...)                                                \
+    do{                                                                     \
+      if(cat){                                                              \
+        LONG_LOG_MODULE_STRING                                              \
+        printf( __VA_ARGS__ );                                              \
+      }                                                                     \
+    }while(0)
+#endif /* _MSC_VER */
+#endif /* __GNUC__ */
+
+#ifdef  __GNUC__
+  #define xprintf(xine, verbose, fmt, args...)                              \
+    do {                                                                    \
+      if((xine) && (xine)->verbosity >= verbose){                           \
+        xine_log(xine, XINE_LOG_TRACE, fmt, ##args);                        \
+      }                                                                     \
+    } while(0)
+#else
+#ifdef _MSC_VER
+  #define xprintf(xine, verbose, fmtargs)                                   \
+    do {                                                                    \
+      if((xine) && (xine)->verbosity >= verbose){                           \
+        xine_log(xine, XINE_LOG_TRACE, fmtargs);                            \
+      }                                                                     \
+    } while(0)
+#else
+  #define xprintf(xine, verbose, ...)                                       \
+    do {                                                                    \
+      if((xine) && (xine)->verbosity >= verbose){                           \
+        xine_log(xine, XINE_LOG_TRACE, __VA_ARGS__);                        \
+      }                                                                     \
+    } while(0)
+#endif /* _MSC_VER */
+#endif /* __GNUC__ */
+
+/* time measuring macros for profiling tasks */
+
+#ifdef DEBUG
+#  define XINE_PROFILE(function)                                            \
+     do {                                                                   \
+       struct timeval current_time;                                         \
+       double dtime;                                                        \
+       gettimeofday(&current_time, NULL);                                   \
+       dtime = -(current_time.tv_sec + (current_time.tv_usec / 1000000.0)); \
+       function;                                                            \
+       gettimeofday(&current_time, NULL);                                   \
+       dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0);   \
+       printf("%s: (%s:%d) took %lf seconds\n",                             \
+              LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime);              \
+     } while(0)
+#  define XINE_PROFILE_ACCUMULATE(function)                                 \
+     do {                                                                   \
+       struct timeval current_time;                                         \
+       static double dtime = 0;                                             \
+       gettimeofday(&current_time, NULL);                                   \
+       dtime -= current_time.tv_sec + (current_time.tv_usec / 1000000.0);   \
+       function;                                                            \
+       gettimeofday(&current_time, NULL);                                   \
+       dtime += current_time.tv_sec + (current_time.tv_usec / 1000000.0);   \
+       printf("%s: (%s:%d) took %lf seconds\n",                             \
+              LOG_MODULE, __XINE_FUNCTION__, __LINE__, dtime);              \
+     } while(0)
+#else
+#  define XINE_PROFILE(function) function
+#  define XINE_PROFILE_ACCUMULATE(function) function
+#endif /* LOG */
+
+
+/******** double chained lists with builtin iterator *******/
+
+typedef struct xine_node_s {
+
+  struct xine_node_s    *next, *prev;
+
+  void                  *content;
+
+  int                    priority;
+
+} xine_node_t;
+
+
+typedef struct {
+
+  xine_node_t    *first, *last, *cur;
+
+} xine_list_t;
+
+
+
+xine_list_t *xine_list_new (void);
+
+
+/**
+ * dispose the whole list.
+ * note: disposes _only_ the list structure, content must be free()d elsewhere
+ */
+void xine_list_free(xine_list_t *l);
+
+
+/**
+ * returns: Boolean
+ */
+int xine_list_is_empty (xine_list_t *l);
+
+/**
+ * return content of first entry in list.
+ */
+void *xine_list_first_content (xine_list_t *l);
+
+/**
+ * return next content in list.
+ */
+void *xine_list_next_content (xine_list_t *l);
+
+/**
+ * Return last content of list.
+ */
+void *xine_list_last_content (xine_list_t *l);
+
+/**
+ * Return previous content of list.
+ */
+void *xine_list_prev_content (xine_list_t *l);
+
+/**
+ * Append content to list, sorted by decreasing priority.
+ */
+void xine_list_append_priority_content (xine_list_t *l, void *content, int priority);
+
+/**
+ * Append content to list.
+ */
+void xine_list_append_content (xine_list_t *l, void *content);
+
+/**
+ * Insert content in list.
+ */
+void xine_list_insert_content (xine_list_t *l, void *content);
+
+/**
+ * Remove current content in list.
+ * note: removes only the list entry; content must be free()d elsewhere.
+ */
+void xine_list_delete_current (xine_list_t *l);
+
+#ifndef HAVE_BASENAME
+/*
+ * get base name
+ */
+char *basename (char const *name);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/tests/Makefile b/src/tests/Makefile
new file mode 100644 (file)
index 0000000..65099fb
--- /dev/null
@@ -0,0 +1,41 @@
+include ../../config.mak
+
+TARGET = dan charlie pango pixbuf dissolve luma
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../modules
+LDFLAGS += -L../framework -lmlt
+
+all: $(TARGET)
+
+hello:         hello.o
+                       $(CC) hello.o -o $@ -L../framework -L../modules -lmlt
+
+pango:         pango.o
+                       $(CC) pango.o -o $@ $(LDFLAGS)
+
+pixbuf:                pixbuf.o
+                       $(CC) pixbuf.o -o $@ $(LDFLAGS)
+
+dissolve:              dissolve.o
+                       $(CC) dissolve.o -o $@ $(LDFLAGS)
+
+luma:          luma.o
+                       $(CC) luma.o -o $@ $(LDFLAGS)
+
+dan:           dan.o 
+                       $(CC) dan.o -o $@ $(LDFLAGS)
+
+charlie:       charlie.o io.o
+                       $(CC) charlie.o io.o -o $@ $(LDFLAGS)
+
+clean:
+                       rm -f dan.o io.o charlie.o dan charlie
+
+depend:                dan.c charlie.c io.c
+                       $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/tests/charlie.c b/src/tests/charlie.c
new file mode 100644 (file)
index 0000000..7130b1d
--- /dev/null
@@ -0,0 +1,193 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <framework/mlt.h>
+
+#include "io.h"
+
+mlt_producer create_producer( char *file )
+{
+       mlt_producer result = NULL;
+
+       // 1st Line preferences
+       if ( strstr( file, ".inigo" ) )
+       {
+               char *args[ 2 ] = { file, NULL };
+               result = mlt_factory_producer( "inigo", args );
+       }
+       else if ( strstr( file, ".mpg" ) )
+               result = mlt_factory_producer( "mcmpeg", file );
+       else if ( strstr( file, ".mpeg" ) )
+               result = mlt_factory_producer( "mcmpeg", file );
+       else if ( strstr( file, ".dat" ) )
+               result = mlt_factory_producer( "mcmpeg", file );
+       else if ( strstr( file, ".dv" ) )
+               result = mlt_factory_producer( "mcdv", file );
+       else if ( strstr( file, ".dif" ) )
+               result = mlt_factory_producer( "mcdv", file );
+       else if ( strstr( file, ".jpg" ) )
+               result = mlt_factory_producer( "pixbuf", file );
+       else if ( strstr( file, ".JPG" ) )
+               result = mlt_factory_producer( "pixbuf", file );
+       else if ( strstr( file, ".jpeg" ) )
+               result = mlt_factory_producer( "pixbuf", file );
+       else if ( strstr( file, ".png" ) )
+               result = mlt_factory_producer( "pixbuf", file );
+
+       // 2nd Line fallbacks
+       if ( result == NULL && strstr( file, ".dv" ) )
+               result = mlt_factory_producer( "libdv", file );
+       else if ( result == NULL && strstr( file, ".dif" ) )
+               result = mlt_factory_producer( "libdv", file );
+
+       return result;
+}
+
+void transport_action( mlt_producer producer, char *value )
+{
+       mlt_properties properties = mlt_producer_properties( producer );
+
+       switch( value[ 0 ] )
+       {
+               case 'q':
+                       mlt_properties_set_int( properties, "done", 1 );
+                       break;
+               case '0':
+                       mlt_producer_set_speed( producer, 1 );
+                       mlt_producer_seek( producer, 0 );
+                       break;
+               case '1':
+                       mlt_producer_set_speed( producer, -5 );
+                       break;
+               case '2':
+                       mlt_producer_set_speed( producer, -2.5 );
+                       break;
+               case '3':
+                       mlt_producer_set_speed( producer, -1 );
+                       break;
+               case '4':
+                       mlt_producer_set_speed( producer, -0.5 );
+                       break;
+               case '5':
+                       mlt_producer_set_speed( producer, 0 );
+                       break;
+               case '6':
+                       mlt_producer_set_speed( producer, 0.5 );
+                       break;
+               case '7':
+                       mlt_producer_set_speed( producer, 1 );
+                       break;
+               case '8':
+                       mlt_producer_set_speed( producer, 2.5 );
+                       break;
+               case '9':
+                       mlt_producer_set_speed( producer, 5 );
+                       break;
+       }
+}
+
+mlt_consumer create_consumer( char *id, mlt_producer producer )
+{
+       char *arg = strchr( id, ':' );
+       if ( arg != NULL )
+               *arg ++ = '\0';
+       mlt_consumer consumer = mlt_factory_consumer( id, arg );
+       if ( consumer != NULL )
+       {
+               mlt_properties properties = mlt_consumer_properties( consumer );
+               mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL );
+               mlt_properties_set_data( properties, "transport_producer", producer, 0, NULL, NULL );
+       }
+       return consumer;
+}
+
+void track_service( mlt_field field, void *service, mlt_destructor destructor )
+{
+       mlt_properties properties = mlt_field_properties( field );
+       int registered = mlt_properties_get_int( properties, "registered" );
+       char *key = mlt_properties_get( properties, "registered" );
+       mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
+       mlt_properties_set_int( properties, "registered", ++ registered );
+}
+
+void set_properties( mlt_service service, char *namevalue )
+{
+       mlt_properties properties = mlt_service_properties( service );
+       mlt_properties_parse( properties, namevalue );
+}
+
+void transport( mlt_producer producer )
+{
+       mlt_properties properties = mlt_producer_properties( producer );
+
+       term_init( );
+       fprintf( stderr, "Press 'q' to continue\n" );
+       while( mlt_properties_get_int( properties, "done" ) == 0 )
+       {
+               int value = term_read( );
+               if ( value != -1 )
+                       transport_action( producer, ( char * )&value );
+       }
+}
+
+int main( int argc, char **argv )
+{
+       int i;
+       mlt_service  service = NULL;
+       mlt_consumer consumer = NULL;
+       mlt_producer producer = NULL;
+       mlt_playlist playlist = NULL;
+
+       // Construct the factory
+       mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
+
+       // Set up containers
+       playlist = mlt_playlist_init( );
+
+       // Parse the arguments
+       for ( i = 1; i < argc; i ++ )
+       {
+               if ( !strcmp( argv[ i ], "-consumer" ) )
+               {
+                       consumer = create_consumer( argv[ ++ i ], mlt_playlist_producer( playlist ) );
+                       if ( consumer != NULL )
+                               service = mlt_consumer_service( consumer );
+               }
+               else if ( !strstr( argv[ i ], "=" ) )
+               {
+                       if ( producer != NULL )
+                               mlt_playlist_append( playlist, producer );
+                       producer = create_producer( argv[ i ] );
+                       if ( producer != NULL )
+                               service = mlt_producer_service( producer );
+               }
+               else
+               {
+                       set_properties( service, argv[ i ] );
+               }
+       }
+
+       // If we have no consumer, default to sdl
+       if ( consumer == NULL )
+               consumer = create_consumer( "sdl", mlt_playlist_producer( playlist ) );
+
+       // Connect producer to playlist
+       if ( producer != NULL )
+               mlt_playlist_append( playlist, producer );
+
+       // Connect consumer to playlist
+       mlt_consumer_connect( consumer, mlt_playlist_service( playlist ) );
+
+       // Transport functionality
+       transport( mlt_playlist_producer( playlist ) );
+
+       // Close the services
+       mlt_consumer_close( consumer );
+       mlt_playlist_close( playlist );
+
+       // Close the factory
+       mlt_factory_close( );
+
+       return 0;
+}
diff --git a/src/tests/clock16ntsc.pgm b/src/tests/clock16ntsc.pgm
new file mode 100644 (file)
index 0000000..61e64ad
Binary files /dev/null and b/src/tests/clock16ntsc.pgm differ
diff --git a/src/tests/clock16pal.pgm b/src/tests/clock16pal.pgm
new file mode 100644 (file)
index 0000000..4d7b0fc
Binary files /dev/null and b/src/tests/clock16pal.pgm differ
diff --git a/src/tests/dan.c b/src/tests/dan.c
new file mode 100644 (file)
index 0000000..98f2a0d
--- /dev/null
@@ -0,0 +1,27 @@
+
+#include <framework/mlt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+
+int main( int argc, char **argv )
+{
+       mlt_properties p = mlt_properties_parse_yaml( argv[1] );
+       mlt_properties q = mlt_properties_new();
+       mlt_properties_set_data( q, "metadata", p, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml ); 
+       printf( "%s", mlt_properties_get( q, "metadata" ) );
+       mlt_properties_close( q );
+       
+       mlt_repository repo = mlt_factory_init( NULL );
+       mlt_properties metadata = mlt_repository_metadata( repo, producer_type, "avformat" );
+       if ( metadata )
+       {
+               char *s = mlt_properties_serialise_yaml( metadata );
+               printf( "%s", s );
+               free( s );
+       }
+       mlt_factory_close();
+       return 0;
+}
diff --git a/src/tests/dissolve.c b/src/tests/dissolve.c
new file mode 100644 (file)
index 0000000..d9ce2d7
--- /dev/null
@@ -0,0 +1,71 @@
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+       char temp[ 132 ];
+       char *file1 = NULL;
+       char *file2 = NULL;
+
+       mlt_factory_init( "../modules" );
+
+       if ( argc < 3 )
+       {
+               fprintf( stderr, "usage: dissolve file1.mpeg file2.mpeg\n" );
+               return 1;
+       }
+       else
+       {
+               file1 = argv[ 1 ];
+               file2 = argv[ 2 ];
+       }
+
+       // Start the consumer...
+       mlt_consumer consumer = mlt_factory_consumer( "sdl", "PAL" );
+
+       // Create the producer(s)
+       mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+       mlt_producer dv2 = mlt_factory_producer( "mcmpeg", file2 );
+
+       mlt_playlist playlist1 = mlt_playlist_init();
+       mlt_playlist_append_io( playlist1, dv1, 0.0, 5.0 );
+
+       mlt_playlist playlist2 = mlt_playlist_init();
+       mlt_playlist_blank( playlist2, 2.9 );
+       mlt_playlist_append( playlist2, dv2 );
+       
+       // Register producers(s) with a multitrack object
+       mlt_multitrack multitrack = mlt_multitrack_init( );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist1 ), 0 );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist2 ), 1 );
+
+       // Define a transition
+       mlt_transition transition = mlt_factory_transition( "luma", NULL );
+       mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+       mlt_transition_set_in_and_out( transition, 3.0, 5.0 );
+
+       // Buy a tractor and connect it to the filter
+       mlt_tractor tractor = mlt_tractor_init( );
+       mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+       // Connect the tractor to the consumer
+       mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+       // Do stuff until we're told otherwise...
+       fprintf( stderr, "Press return to continue\n" );
+       fgets( temp, 132, stdin );
+
+       // Close everything...
+       mlt_consumer_close( consumer );
+       mlt_tractor_close( tractor );
+       mlt_transition_close( transition );
+       mlt_multitrack_close( multitrack );
+       mlt_playlist_close( playlist1 );
+       mlt_playlist_close( playlist2 );
+       mlt_producer_close( dv1 );
+       mlt_producer_close( dv2 );
+
+       return 0;
+}
diff --git a/src/tests/hello.c b/src/tests/hello.c
new file mode 100644 (file)
index 0000000..4914304
--- /dev/null
@@ -0,0 +1,136 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <framework/mlt.h>
+
+mlt_producer create_playlist( int argc, char **argv )
+{
+       // We're creating a playlist here
+       mlt_playlist playlist = mlt_playlist_init( );
+
+       // We need the playlist properties to ensure clean up
+       mlt_properties properties = mlt_playlist_properties( playlist );
+
+       // Loop through each of the arguments
+       int i = 0;
+       for ( i = 1; i < argc; i ++ )
+       {
+               // Definie the unique key
+               char key[ 256 ];
+
+               // Create the producer
+               mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
+
+               // Add it to the playlist
+               mlt_playlist_append( playlist, producer );
+
+               // Create a unique key for this producer
+               sprintf( key, "producer%d", i );
+
+               // Now we need to ensure the producers are destroyed
+               mlt_properties_set_data( properties, key, producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+       }
+
+       // Return the playlist as a producer
+       return mlt_playlist_producer( playlist );
+}
+
+mlt_producer create_tracks( int argc, char **argv )
+{
+       // Create the field
+       mlt_field field = mlt_field_init( );
+
+       // Obtain the multitrack
+       mlt_multitrack multitrack = mlt_field_multitrack( field );
+
+       // Obtain the tractor
+       mlt_tractor tractor = mlt_field_tractor( field );
+
+       // Obtain a composite transition
+       mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );
+
+       // Create track 0
+       mlt_producer track0 = create_playlist( argc, argv );
+
+       // Get the length of track0
+       mlt_position length = mlt_producer_get_playtime( track0 );
+
+       // Create the watermark track
+       mlt_producer track1 = mlt_factory_producer( "fezzik", "pango:" );
+
+       // Get the properties of track1
+       mlt_properties properties = mlt_producer_properties( track1 );
+
+       // Set the properties
+       mlt_properties_set( properties, "text", "Hello\nWorld" );
+       mlt_properties_set_position( properties, "in", 0 );
+       mlt_properties_set_position( properties, "out", length - 1 );
+       mlt_properties_set_position( properties, "length", length );
+
+       // Now set the properties on the transition
+       properties = mlt_transition_properties( transition );
+       mlt_properties_set_position( properties, "in", 0 );
+       mlt_properties_set_position( properties, "out", length - 1 );
+
+       // Add our tracks to the multitrack
+       mlt_multitrack_connect( multitrack, track0, 0 );
+       mlt_multitrack_connect( multitrack, track1, 1 );
+
+       // Now plant the transition
+       mlt_field_plant_transition( field, transition, 0, 1 );
+
+       // Now set the properties on the transition
+       properties = mlt_tractor_properties( tractor );
+
+       // Ensure clean up and set properties correctly
+       mlt_properties_set_data( properties, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
+       mlt_properties_set_data( properties, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
+       mlt_properties_set_data( properties, "track0", track0, 0, ( mlt_destructor )mlt_producer_close, NULL );
+       mlt_properties_set_data( properties, "track1", track1, 0, ( mlt_destructor )mlt_producer_close, NULL );
+       mlt_properties_set_data( properties, "transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
+       mlt_properties_set_position( properties, "length", length );
+       mlt_properties_set_position( properties, "out", length - 1 );
+
+       // Return the tractor
+       return mlt_tractor_producer( tractor );
+}
+
+int main( int argc, char **argv )
+{
+       // Initialise the factory
+       if ( mlt_factory_init( NULL ) == 0 )
+       {
+               // Create the default consumer
+               mlt_consumer hello = mlt_factory_consumer( NULL, NULL );
+
+               // Create a producer using the default normalising selecter
+               mlt_producer world = create_tracks( argc, argv );
+
+               // Connect the producer to the consumer
+               mlt_consumer_connect( hello, mlt_producer_service( world ) );
+
+               // Start the consumer
+               mlt_consumer_start( hello );
+
+               // Wait for the consumer to terminate
+               while( !mlt_consumer_is_stopped( hello ) )
+                       sleep( 1 );
+
+               // Close the consumer
+               mlt_consumer_close( hello );
+
+               // Close the producer
+               mlt_producer_close( world );
+
+               // Close the factory
+               mlt_factory_close( );
+       }
+       else
+       {
+               // Report an error during initialisation
+               fprintf( stderr, "Unable to locate factory modules\n" );
+       }
+
+       // End of program
+       return 0;
+}
+
diff --git a/src/tests/io.c b/src/tests/io.c
new file mode 100644 (file)
index 0000000..431003d
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * io.c -- dv1394d client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* Application header files */
+#include "io.h"
+
+char *chomp( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               if ( length && input[ length - 1 ] == '\n' )
+                       input[ length - 1 ] = '\0';
+               if ( length > 1 && input[ length - 2 ] == '\r' )
+                       input[ length - 2 ] = '\0';
+       }
+       return input;
+}
+
+char *trim( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               int first = 0;
+               while( first < length && isspace( input[ first ] ) )
+                       first ++;
+               memmove( input, input + first, length - first + 1 );
+               length = length - first;
+               while ( length > 0 && isspace( input[ length - 1 ] ) )
+                       input[ -- length ] = '\0';
+       }
+       return input;
+}
+
+char *strip_quotes( char *input )
+{
+       if ( input != NULL )
+       {
+               char *ptr = strrchr( input, '\"' );
+               if ( ptr != NULL )
+                       *ptr = '\0';
+               if ( input[ 0 ] == '\"' )
+                       strcpy( input, input + 1 );
+       }
+       return input;
+}
+
+char *get_string( char *output, int maxlength, char *use )
+{
+       char *value = NULL;
+       strcpy( output, use );
+       if ( trim( chomp( fgets( output, maxlength, stdin ) ) ) != NULL )
+       {
+               if ( !strcmp( output, "" ) )
+                       strcpy( output, use );
+               value = output;
+       }
+       return value;
+}
+
+int *get_int( int *output, int use )
+{
+       int *value = NULL;
+       char temp[ 132 ];
+       *output = use;
+       if ( trim( chomp( fgets( temp, 132, stdin ) ) ) != NULL )
+       {
+               if ( strcmp( temp, "" ) )
+                       *output = atoi( temp );
+               value = output;
+       }
+       return value;
+}
+
+/** This stores the previous settings
+*/
+
+static struct termios oldtty;
+static int mode = 0;
+
+/** This is called automatically on application exit to restore the 
+       previous tty settings.
+*/
+
+void term_exit(void)
+{
+       if ( mode == 1 )
+       {
+               tcsetattr( 0, TCSANOW, &oldtty );
+               mode = 0;
+       }
+}
+
+/** Init terminal so that we can grab keys without blocking.
+*/
+
+void term_init( )
+{
+       struct termios tty;
+
+       tcgetattr( 0, &tty );
+       oldtty = tty;
+
+       tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+       tty.c_oflag |= OPOST;
+       tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+       tty.c_cflag &= ~(CSIZE|PARENB);
+       tty.c_cflag |= CS8;
+       tty.c_cc[ VMIN ] = 1;
+       tty.c_cc[ VTIME ] = 0;
+    
+       tcsetattr( 0, TCSANOW, &tty );
+
+       mode = 1;
+
+       atexit( term_exit );
+}
+
+/** Check for a keypress without blocking infinitely.
+       Returns: ASCII value of keypress or -1 if no keypress detected.
+*/
+
+int term_read( )
+{
+    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 = 250;
+    n = select( 1, &rfds, NULL, NULL, &tv );
+    if (n > 0) 
+       {
+        n = read( 0, &ch, 1 );
+               tcflush( 0, TCIFLUSH );
+        if (n == 1)
+            return ch;
+        return n;
+    }
+    return -1;
+}
+
+char get_keypress( )
+{
+       char value = '\0';
+       int pressed = 0;
+
+       fflush( stdout );
+
+       term_init( );
+       while ( ( pressed = term_read( ) ) == -1 ) ;
+       term_exit( );
+
+       value = (char)pressed;
+
+       return value;
+}
+
+void wait_for_any_key( char *message )
+{
+       if ( message == NULL )
+               printf( "Press any key to continue: " );
+       else
+               printf( "%s", message );
+
+       get_keypress( );
+
+       printf( "\n\n" );
+}
+
+void beep( )
+{
+       printf( "%c", 7 );
+       fflush( stdout );
+}
diff --git a/src/tests/io.h b/src/tests/io.h
new file mode 100644 (file)
index 0000000..f97e69e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * io.h -- dv1394d client demo input/output
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEMO_IO_H_
+#define _DEMO_IO_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *chomp( char * );
+extern char *trim( char * );
+extern char *strip_quotes( char * );
+extern char *get_string( char *, int, char * );
+extern int *get_int( int *, int );
+extern void term_init( );
+extern int term_read( );
+extern void term_exit( );
+extern char get_keypress( );
+extern void wait_for_any_key( char * );
+extern void beep( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/tests/luma.c b/src/tests/luma.c
new file mode 100644 (file)
index 0000000..dabb878
--- /dev/null
@@ -0,0 +1,75 @@
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+       char temp[ 132 ];
+       char *file1 = NULL;
+       char *file2 = NULL;
+       char *wipe = NULL;
+
+       mlt_factory_init( "../modules" );
+
+       if ( argc < 4 )
+       {
+               fprintf( stderr, "usage: luma file1.mpeg file2.mpeg wipe.pgm\n" );
+               return 1;
+       }
+       else
+       {
+               file1 = argv[ 1 ];
+               file2 = argv[ 2 ];
+               wipe = argv[ 3 ];
+       }
+
+       // Start the consumer...
+       mlt_consumer consumer = mlt_factory_consumer( "bluefish", "NTSC" );
+
+       // Create the producer(s)
+       mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+       mlt_producer dv2 = mlt_factory_producer( "mcmpeg", file2 );
+
+       mlt_playlist playlist1 = mlt_playlist_init();
+       mlt_playlist_append_io( playlist1, dv1, 0.0, 5.0 );
+
+       mlt_playlist playlist2 = mlt_playlist_init();
+       mlt_playlist_blank( playlist2, 2.9 );
+       mlt_playlist_append( playlist2, dv2 );
+       
+       // Register producers(s) with a multitrack object
+       mlt_multitrack multitrack = mlt_multitrack_init( );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist1 ), 0 );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist2 ), 1 );
+
+       // Define a transition
+       mlt_transition transition = mlt_factory_transition( "luma", wipe );
+       mlt_properties_set( mlt_transition_properties( transition ), "filename", wipe );
+       mlt_properties_set_double( mlt_transition_properties( transition ), "softness", 0.1 );
+       mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+       mlt_transition_set_in_and_out( transition, 3.0, 5.0 );
+
+       // Buy a tractor and connect it to the filter
+       mlt_tractor tractor = mlt_tractor_init( );
+       mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+       // Connect the tractor to the consumer
+       mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+       // Do stuff until we're told otherwise...
+       fprintf( stderr, "Press return to continue\n" );
+       fgets( temp, 132, stdin );
+
+       // Close everything...
+       mlt_consumer_close( consumer );
+       mlt_tractor_close( tractor );
+       mlt_transition_close( transition );
+       mlt_multitrack_close( multitrack );
+       mlt_playlist_close( playlist1 );
+       mlt_playlist_close( playlist2 );
+       mlt_producer_close( dv1 );
+       mlt_producer_close( dv2 );
+
+       return 0;
+}
diff --git a/src/tests/pango.c b/src/tests/pango.c
new file mode 100644 (file)
index 0000000..fb5e19a
--- /dev/null
@@ -0,0 +1,77 @@
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+       char temp[ 132 ];
+       char *file1 = NULL;
+       char *text = NULL;
+
+       mlt_factory_init( "../modules" );
+
+       if ( argc < 3 )
+       {
+               fprintf( stderr, "usage: pango file.mpeg  text_to_display\n" );
+               return 1;
+       }
+       else
+       {
+               file1 = argv[ 1 ];
+               text = argv[ 2 ];
+       }
+
+       // Start the consumer...
+       mlt_consumer consumer = mlt_factory_consumer( "bluefish", "NTSC" );
+
+       // Create the producer(s)
+       mlt_playlist pl1 = mlt_playlist_init();
+       mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+       mlt_playlist_append( pl1, dv1 );
+
+       mlt_playlist pl2 = mlt_playlist_init();
+       mlt_producer title = mlt_factory_producer( "pango", NULL ); //"<span font_desc=\"Sans Bold 36\">Mutton <span font_desc=\"Luxi Serif Bold Oblique 36\">Lettuce</span> Tomato</span>" );
+       mlt_playlist_append( pl2, title );
+       mlt_properties_set( mlt_producer_properties( title ), "font", "Sans Bold 36" );
+       mlt_properties_set( mlt_producer_properties( title ), "text", text );
+       mlt_properties_set_int( mlt_producer_properties( title ), "bgcolor", 0x0000007f );
+       mlt_properties_set_int( mlt_producer_properties( title ), "pad", 8 );
+       mlt_properties_set_int( mlt_producer_properties( title ), "align", 1 );
+       mlt_properties_set_int( mlt_producer_properties( title ), "x", 200 );
+       mlt_properties_set_int( mlt_producer_properties( title ), "y", 40 );
+       mlt_properties_set_double( mlt_producer_properties( title ), "mix", 0.8 );
+
+       // Register producers(s) with a multitrack object
+       mlt_multitrack multitrack = mlt_multitrack_init( );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl1 ), 0 );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl2 ), 1 );
+
+       // Define a transition
+       mlt_transition transition = mlt_factory_transition( "composite", NULL );
+       mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+       mlt_transition_set_in_and_out( transition, 0.0, 9999.0 );
+
+       // Buy a tractor and connect it to the filter
+       mlt_tractor tractor = mlt_tractor_init( );
+       mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+       // Connect the tractor to the consumer
+       mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+       // Do stuff until we're told otherwise...
+       fprintf( stderr, "Press return to continue\n" );
+       fgets( temp, 132, stdin );
+
+       // Close everything...
+       mlt_consumer_close( consumer );
+       mlt_tractor_close( tractor );
+       mlt_transition_close( transition );
+       mlt_multitrack_close( multitrack );
+       mlt_playlist_close( pl1 );
+       mlt_playlist_close( pl2 );
+       mlt_producer_close( dv1 );
+       mlt_producer_close( title );
+
+       return 0;
+}
diff --git a/src/tests/pixbuf.c b/src/tests/pixbuf.c
new file mode 100644 (file)
index 0000000..fd0f0b6
--- /dev/null
@@ -0,0 +1,72 @@
+
+#include <framework/mlt.h>
+
+#include <stdio.h>
+
+int main( int argc, char **argv )
+{
+       char temp[ 132 ];
+       char *file1 = NULL;
+       char *file2 = NULL;
+
+       mlt_factory_init( "../modules" );
+
+       if ( argc < 3 )
+       {
+               fprintf( stderr, "usage: pixbuf file.mpeg file.{png,jpg,etc}\n" );
+               return 1;
+       }
+       else
+       {
+               file1 = argv[ 1 ];
+               file2 = argv[ 2 ];
+       }
+
+       // Start the consumer...
+       mlt_consumer consumer = mlt_factory_consumer( "sdl", "PAL" );
+
+       // Create the producer(s)
+       mlt_playlist pl1 = mlt_playlist_init();
+       mlt_producer dv1 = mlt_factory_producer( "mcmpeg", file1 );
+       mlt_playlist_append( pl1, dv1 );
+
+       mlt_playlist pl2 = mlt_playlist_init();
+       mlt_producer overlay = mlt_factory_producer( "pixbuf", file2 );
+       mlt_playlist_append( pl2, overlay );
+       mlt_properties_set_int( mlt_producer_properties( overlay ), "x", 600 );
+       mlt_properties_set_int( mlt_producer_properties( overlay ), "y", 460 );
+       mlt_properties_set_double( mlt_producer_properties( overlay ), "mix", 0.8 );
+
+       // Register producers(s) with a multitrack object
+       mlt_multitrack multitrack = mlt_multitrack_init( );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl1 ), 0 );
+       mlt_multitrack_connect( multitrack, mlt_playlist_producer( pl2 ), 1 );
+
+       // Define a transition
+       mlt_transition transition = mlt_factory_transition( "composite", NULL );
+       mlt_transition_connect( transition, mlt_multitrack_service( multitrack ), 0, 1 );
+       mlt_transition_set_in_and_out( transition, 0.0, 9999.0 );
+
+       // Buy a tractor and connect it to the filter
+       mlt_tractor tractor = mlt_tractor_init( );
+       mlt_tractor_connect( tractor, mlt_transition_service( transition ) );
+
+       // Connect the tractor to the consumer
+       mlt_consumer_connect( consumer, mlt_tractor_service( tractor ) );
+
+       // Do stuff until we're told otherwise...
+       fprintf( stderr, "Press return to continue\n" );
+       fgets( temp, 132, stdin );
+
+       // Close everything...
+       mlt_consumer_close( consumer );
+       mlt_tractor_close( tractor );
+       mlt_transition_close( transition );
+       mlt_multitrack_close( multitrack );
+       mlt_playlist_close( pl1 );
+       mlt_playlist_close( pl2 );
+       mlt_producer_close( dv1 );
+       mlt_producer_close( overlay );
+
+       return 0;
+}
diff --git a/src/tests/setenv b/src/tests/setenv
new file mode 100644 (file)
index 0000000..44cee49
--- /dev/null
@@ -0,0 +1,7 @@
+export MLT_REPOSITORY=`pwd`/../modules
+
+export LD_LIBRARY_PATH=`pwd`/../framework:\
+`pwd`/../modules/bluefish:\
+`pwd`/../../../bluefish/lib:\
+`pwd`/../../../mpeg_sdk_demo/bin:\
+`pwd`/../../../dv_sdk
diff --git a/src/tests/test.png b/src/tests/test.png
new file mode 100644 (file)
index 0000000..b3fca64
Binary files /dev/null and b/src/tests/test.png differ
diff --git a/src/valerie/Makefile b/src/valerie/Makefile
new file mode 100644 (file)
index 0000000..52b61e1
--- /dev/null
@@ -0,0 +1,72 @@
+include ../../config.mak
+
+ifneq ($(targetos), Darwin)
+NAME = libvalerie$(LIBSUF)
+TARGET = $(NAME).$(version)
+SONAME = $(NAME).$(soversion)
+SHFLAGS += -Wl,-soname,$(SONAME)
+else
+NAME = libvalerie$(LIBSUF)
+TARGET = libvalerie.$(version)$(LIBSUF)
+SONAME = libvalerie.$(soversion)$(LIBSUF)
+SHFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion)
+endif
+
+OBJS = valerie.o \
+          valerie_notifier.o \
+          valerie_parser.o \
+          valerie_response.o \
+          valerie_status.o \
+          valerie_tokeniser.o \
+          valerie_util.o \
+          valerie_remote.o \
+          valerie_socket.o
+
+INCS = valerie.h \
+          valerie_notifier.h \
+          valerie_parser.h \
+          valerie_remote.h \
+          valerie_response.h \
+          valerie_socket.h \
+          valerie_status.h \
+          valerie_tokeniser.h \
+          valerie_util.h
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS += -I.. $(RDYNAMIC)
+
+LDFLAGS += -L../framework -lmlt -lpthread
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+               ln -sf $(TARGET) $(NAME)
+               ln -sf $(TARGET) $(SONAME)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET) $(NAME)
+
+install:       all
+       install -m 755 $(TARGET) $(DESTDIR)$(libdir)
+       ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME)
+       ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME)
+       mkdir -p "$(DESTDIR)$(prefix)/include/mlt/valerie"
+       install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/valerie"
+
+uninstall:
+       rm -f "$(DESTDIR)$(libdir)/$(TARGET)"
+       rm -f "$(DESTDIR)$(libdir)/$(SONAME)"
+       rm -f "$(DESTDIR)$(libdir)/$(NAME)"
+       rm -rf "$(DESTDIR)$(prefix)/include/mlt/valerie"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/valerie/configure b/src/valerie/configure
new file mode 100755 (executable)
index 0000000..462e2de
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo "valerie  -I$prefix/include/mlt -D_REENTRANT      -L$libdir -lvalerie" >> ../../packages.dat
diff --git a/src/valerie/valerie.c b/src/valerie/valerie.c
new file mode 100644 (file)
index 0000000..b7f1cc6
--- /dev/null
@@ -0,0 +1,969 @@
+/*
+ * valerie.c -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Application header files */
+#include "valerie.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Initialise the valerie structure.
+*/
+
+valerie valerie_init( valerie_parser parser )
+{
+       valerie this = malloc( sizeof( valerie_t ) );
+       if ( this != NULL )
+       {
+               memset( this, 0, sizeof( valerie_t ) );
+               this->parser = parser;
+       }
+       return this;
+}
+
+/** Set the response structure associated to the last command.
+*/
+
+static void valerie_set_last_response( valerie this, valerie_response response )
+{
+       if ( this != NULL )
+       {
+               if ( this->last_response != NULL )
+                       valerie_response_close( this->last_response );
+               this->last_response = response;
+       }
+}
+
+/** Connect to the parser.
+*/
+
+valerie_error_code valerie_connect( valerie this )
+{
+       valerie_error_code error = valerie_server_unavailable;
+       valerie_response response = valerie_parser_connect( this->parser );
+       if ( response != NULL )
+       {
+               valerie_set_last_response( this, response );
+               if ( valerie_response_get_error_code( response ) == 100 )
+                       error = valerie_ok;
+       }
+       return error;
+}
+
+/** Interpret a non-context sensitive error code.
+*/
+
+static valerie_error_code valerie_get_error_code( valerie this, valerie_response response )
+{
+       valerie_error_code error = valerie_server_unavailable;
+       switch( valerie_response_get_error_code( response ) )
+       {
+               case -1:
+                       error = valerie_server_unavailable;
+                       break;
+               case -2:
+                       error = valerie_no_response;
+                       break;
+               case 200:
+               case 201:
+               case 202:
+                       error = valerie_ok;
+                       break;
+               case 400:
+                       error = valerie_invalid_command;
+                       break;
+               case 401:
+                       error = valerie_server_timeout;
+                       break;
+               case 402:
+                       error = valerie_missing_argument;
+                       break;
+               case 403:
+                       error = valerie_unit_unavailable;
+                       break;
+               case 404:
+                       error = valerie_invalid_file;
+                       break;
+               default:
+               case 500:
+                       error = valerie_unknown_error;
+                       break;
+       }
+       return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_execute( valerie this, size_t size, const char *format, ... )
+{
+       valerie_error_code error = valerie_server_unavailable;
+       char *command = malloc( size );
+       if ( this != NULL && command != NULL )
+       {
+               va_list list;
+               va_start( list, format );
+               if ( vsnprintf( command, size, format, list ) != 0 )
+               {
+                       valerie_response response = valerie_parser_execute( this->parser, command );
+                       valerie_set_last_response( this, response );
+                       error = valerie_get_error_code( this, response );
+               }
+               else
+               {
+                       error = valerie_invalid_command;
+               }
+               va_end( list );
+       }
+       else
+       {
+               error = valerie_malloc_failed;
+       }
+       free( command );
+       return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_receive( valerie this, char *doc, size_t size, const char *format, ... )
+{
+       valerie_error_code error = valerie_server_unavailable;
+       char *command = malloc( size );
+       if ( this != NULL && command != NULL )
+       {
+               va_list list;
+               va_start( list, format );
+               if ( vsnprintf( command, size, format, list ) != 0 )
+               {
+                       valerie_response response = valerie_parser_received( this->parser, command, doc );
+                       valerie_set_last_response( this, response );
+                       error = valerie_get_error_code( this, response );
+               }
+               else
+               {
+                       error = valerie_invalid_command;
+               }
+               va_end( list );
+       }
+       else
+       {
+               error = valerie_malloc_failed;
+       }
+       free( command );
+       return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_push( valerie this, mlt_service service, size_t size, const char *format, ... )
+{
+       valerie_error_code error = valerie_server_unavailable;
+       char *command = malloc( size );
+       if ( this != NULL && command != NULL )
+       {
+               va_list list;
+               va_start( list, format );
+               if ( vsnprintf( command, size, format, list ) != 0 )
+               {
+                       valerie_response response = valerie_parser_push( this->parser, command, service );
+                       valerie_set_last_response( this, response );
+                       error = valerie_get_error_code( this, response );
+               }
+               else
+               {
+                       error = valerie_invalid_command;
+               }
+               va_end( list );
+       }
+       else
+       {
+               error = valerie_malloc_failed;
+       }
+       free( command );
+       return error;
+}
+
+/** Set a global property.
+*/
+
+valerie_error_code valerie_set( valerie this, char *property, char *value )
+{
+       return valerie_execute( this, 1024, "SET %s=%s", property, value );
+}
+
+/** Get a global property.
+*/
+
+valerie_error_code valerie_get( valerie this, char *property, char *value, int length )
+{
+       valerie_error_code error = valerie_execute( this, 1024, "GET %s", property );
+       if ( error == valerie_ok )
+       {
+               valerie_response response = valerie_get_last_response( this );
+               strncpy( value, valerie_response_get_line( response, 1 ), length );
+       }
+       return error;
+}
+
+/** Run a script.
+*/
+
+valerie_error_code valerie_run( valerie this, char *file )
+{
+       return valerie_execute( this, 10240, "RUN \"%s\"", file );
+}
+
+/** Add a unit.
+*/
+
+valerie_error_code valerie_unit_add( valerie this, char *guid, int *unit )
+{
+       valerie_error_code error = valerie_execute( this, 1024, "UADD %s", guid );
+       if ( error == valerie_ok )
+       {
+               int length = valerie_response_count( this->last_response );
+               char *line = valerie_response_get_line( this->last_response, length - 1 );
+               if ( line == NULL || sscanf( line, "U%d", unit ) != 1 )
+                       error = valerie_unit_creation_failed;
+       }
+       else
+       {
+               if ( error == valerie_unknown_error )
+                       error = valerie_unit_creation_failed;
+       }
+       return error;
+}
+
+/** Load a file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_load( valerie this, int unit, char *file )
+{
+       return valerie_execute( this, 10240, "LOAD U%d \"%s\"", unit, file );
+}
+
+static void valerie_interpret_clip_offset( char *output, valerie_clip_offset offset, int clip )
+{
+       switch( offset )
+       {
+               case valerie_absolute:
+                       sprintf( output, "%d", clip );
+                       break;
+               case valerie_relative:
+                       if ( clip < 0 )
+                               sprintf( output, "%d", clip );
+                       else
+                               sprintf( output, "+%d", clip );
+                       break;
+       }
+}
+
+/** Load a file on the specified unit with the specified in/out points.
+*/
+
+valerie_error_code valerie_unit_load_clipped( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+       return valerie_execute( this, 10240, "LOAD U%d \"%s\" %d %d", unit, file, in, out );
+}
+
+/** Load a file on the specified unit at the end of the current pump.
+*/
+
+valerie_error_code valerie_unit_load_back( valerie this, int unit, char *file )
+{
+       return valerie_execute( this, 10240, "LOAD U%d \"!%s\"", unit, file );
+}
+
+/** Load a file on the specified unit at the end of the pump with the specified in/out points.
+*/
+
+valerie_error_code valerie_unit_load_back_clipped( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+       return valerie_execute( this, 10240, "LOAD U%d \"!%s\" %d %d", unit, file, in, out );
+}
+
+/** Append a file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_append( valerie this, int unit, char *file, int32_t in, int32_t out )
+{
+       return valerie_execute( this, 10240, "APND U%d \"%s\" %d %d", unit, file, in, out );
+}
+
+/** Push a service on to a unit.
+*/
+
+valerie_error_code valerie_unit_receive( valerie this, int unit, char *command, char *doc )
+{
+       return valerie_receive( this, doc, 10240, "PUSH U%d %s", unit, command );
+}
+
+/** Push a service on to a unit.
+*/
+
+valerie_error_code valerie_unit_push( valerie this, int unit, char *command, mlt_service service )
+{
+       return valerie_push( this, service, 10240, "PUSH U%d %s", unit, command );
+}
+
+/** Clean the unit - this function removes all but the currently playing clip.
+*/
+
+valerie_error_code valerie_unit_clean( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "CLEAN U%d", unit );
+}
+
+/** Clear the unit - this function removes all clips.
+*/
+
+valerie_error_code valerie_unit_clear( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "CLEAR U%d", unit );
+}
+
+/** Wipe the unit - this function removes all clips before the current one.
+*/
+
+valerie_error_code valerie_unit_wipe( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "WIPE U%d", unit );
+}
+
+/** Move clips on the units playlist.
+*/
+
+valerie_error_code valerie_unit_clip_move( valerie this, int unit, valerie_clip_offset src_offset, int src, valerie_clip_offset dest_offset, int dest )
+{
+       char temp1[ 100 ];
+       char temp2[ 100 ];
+       valerie_interpret_clip_offset( temp1, src_offset, src );
+       valerie_interpret_clip_offset( temp2, dest_offset, dest );
+       return valerie_execute( this, 1024, "MOVE U%d %s %s", unit, temp1, temp2 );
+}
+
+/** Remove clip at the specified position.
+*/
+
+valerie_error_code valerie_unit_clip_remove( valerie this, int unit, valerie_clip_offset offset, int clip )
+{
+       char temp[ 100 ];
+       valerie_interpret_clip_offset( temp, offset, clip );
+       return valerie_execute( this, 1024, "REMOVE U%d %s", unit, temp );
+}
+
+/** Remove the currently playing clip.
+*/
+
+valerie_error_code valerie_unit_remove_current_clip( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "REMOVE U%d", unit );
+}
+
+/** Insert clip at the specified position.
+*/
+
+valerie_error_code valerie_unit_clip_insert( valerie this, int unit, valerie_clip_offset offset, int clip, char *file, int32_t in, int32_t out )
+{
+       char temp[ 100 ];
+       valerie_interpret_clip_offset( temp, offset, clip );
+       return valerie_execute( this, 1024, "INSERT U%d \"%s\" %s %d %d", unit, file, temp, in, out );
+}
+
+/** Play the unit at normal speed.
+*/
+
+valerie_error_code valerie_unit_play( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "PLAY U%d 1000", unit );
+}
+
+/** Play the unit at specified speed.
+*/
+
+valerie_error_code valerie_unit_play_at_speed( valerie this, int unit, int speed )
+{
+       return valerie_execute( this, 10240, "PLAY U%d %d", unit, speed );
+}
+
+/** Stop playback on the specified unit.
+*/
+
+valerie_error_code valerie_unit_stop( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "STOP U%d", unit );
+}
+
+/** Pause playback on the specified unit.
+*/
+
+valerie_error_code valerie_unit_pause( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "PAUSE U%d", unit );
+}
+
+/** Rewind the specified unit.
+*/
+
+valerie_error_code valerie_unit_rewind( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "REW U%d", unit );
+}
+
+/** Fast forward the specified unit.
+*/
+
+valerie_error_code valerie_unit_fast_forward( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "FF U%d", unit );
+}
+
+/** Step by the number of frames on the specified unit.
+*/
+
+valerie_error_code valerie_unit_step( valerie this, int unit, int32_t step )
+{
+       return valerie_execute( this, 1024, "STEP U%d %d", unit, step );
+}
+
+/** Goto the specified frame on the specified unit.
+*/
+
+valerie_error_code valerie_unit_goto( valerie this, int unit, int32_t position )
+{
+       return valerie_execute( this, 1024, "GOTO U%d %d", unit, position );
+}
+
+/** Goto the specified frame in the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_goto( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t position )
+{
+       char temp[ 100 ];
+       valerie_interpret_clip_offset( temp, offset, clip );
+       return valerie_execute( this, 1024, "GOTO U%d %d %s", unit, position, temp );
+}
+
+/** Set the in point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_set_in( valerie this, int unit, int32_t in )
+{
+       return valerie_execute( this, 1024, "SIN U%d %d", unit, in );
+}
+
+/** Set the in point of the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_set_in( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t in )
+{
+       char temp[ 100 ];
+       valerie_interpret_clip_offset( temp, offset, clip );
+       return valerie_execute( this, 1024, "SIN U%d %d %s", unit, in, temp );
+}
+
+/** Set the out point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_set_out( valerie this, int unit, int32_t out )
+{
+       return valerie_execute( this, 1024, "SOUT U%d %d", unit, out );
+}
+
+/** Set the out point of the clip on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clip_set_out( valerie this, int unit, valerie_clip_offset offset, int clip, int32_t in )
+{
+       char temp[ 100 ];
+       valerie_interpret_clip_offset( temp, offset, clip );
+       return valerie_execute( this, 1024, "SOUT U%d %d %s", unit, in, temp );
+}
+
+/** Clear the in point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_in( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "SIN U%d -1", unit );
+}
+
+/** Clear the out point of the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_out( valerie this, int unit )
+{
+       return valerie_execute( this, 1024, "SOUT U%d -1", unit );
+}
+
+/** Clear the in and out points on the loaded file on the specified unit.
+*/
+
+valerie_error_code valerie_unit_clear_in_out( valerie this, int unit )
+{
+       valerie_error_code error = valerie_unit_clear_out( this, unit );
+       if ( error == valerie_ok )
+               error = valerie_unit_clear_in( this, unit );
+       return error;
+}
+
+/** Set a unit configuration property.
+*/
+
+valerie_error_code valerie_unit_set( valerie this, int unit, const char *name, const char *value )
+{
+       return valerie_execute( this, 1024, "USET U%d %s=%s", unit, name, value );
+}
+
+/** Get a unit configuration property.
+*/
+
+valerie_error_code valerie_unit_get( valerie this, int unit, char *name )
+{
+       return valerie_execute( this, 1024, "UGET U%d %s", unit, name );
+}
+
+/** Get a units status.
+*/
+
+valerie_error_code valerie_unit_status( valerie this, int unit, valerie_status status )
+{
+       valerie_error_code error = valerie_execute( this, 1024, "USTA U%d", unit );
+       int error_code = valerie_response_get_error_code( this->last_response );
+
+       memset( status, 0, sizeof( valerie_status_t ) );
+       status->unit = unit;
+       if ( error_code == 202 && valerie_response_count( this->last_response ) == 2 )
+               valerie_status_parse( status, valerie_response_get_line( this->last_response, 1 ) );
+       else if ( error_code == 403 )
+               status->status = unit_undefined;
+
+       return error;
+}
+
+/** Transfer the current settings of unit src to unit dest.
+*/
+
+valerie_error_code valerie_unit_transfer( valerie this, int src, int dest )
+{
+       return valerie_execute( this, 1024, "XFER U%d U%d", src, dest );
+}
+
+/** Obtain the parsers notifier.
+*/
+
+valerie_notifier valerie_get_notifier( valerie this )
+{
+       if ( this != NULL )
+               return valerie_parser_get_notifier( this->parser );
+       else
+               return NULL;
+}
+
+/** List the contents of the specified directory.
+*/
+
+valerie_dir valerie_dir_init( valerie this, const char *directory )
+{
+       valerie_dir dir = malloc( sizeof( valerie_dir_t ) );
+       if ( dir != NULL )
+       {
+               memset( dir, 0, sizeof( valerie_dir_t ) );
+               dir->directory = strdup( directory );
+               dir->response = valerie_parser_executef( this->parser, "CLS \"%s\"", directory );
+       }
+       return dir;
+}
+
+/** Return the error code associated to the dir.
+*/
+
+valerie_error_code valerie_dir_get_error_code( valerie_dir dir )
+{
+       if ( dir != NULL )
+               return valerie_get_error_code( NULL, dir->response );
+       else
+               return valerie_malloc_failed;
+}
+
+/** Get a particular file entry in the directory.
+*/
+
+valerie_error_code valerie_dir_get( valerie_dir dir, int index, valerie_dir_entry entry )
+{
+       valerie_error_code error = valerie_ok;
+       memset( entry, 0, sizeof( valerie_dir_entry_t ) );
+       if ( index < valerie_dir_count( dir ) )
+       {
+               char *line = valerie_response_get_line( dir->response, index + 1 );
+               valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+               valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+               if ( valerie_tokeniser_count( tokeniser ) > 0 )
+               {
+                       valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 0 ), '\"' );
+                       strcpy( entry->full, dir->directory );
+                       if ( entry->full[ strlen( entry->full ) - 1 ] != '/' )
+                               strcat( entry->full, "/" );
+                       strcpy( entry->name, valerie_tokeniser_get_string( tokeniser, 0 ) );
+                       strcat( entry->full, entry->name );
+
+                       switch ( valerie_tokeniser_count( tokeniser ) )
+                       {
+                               case 1:
+                                       entry->dir = 1;
+                                       break;
+                               case 2:
+                                       entry->size = strtoull( valerie_tokeniser_get_string( tokeniser, 1 ), NULL, 10 );
+                                       break;
+                               default:
+                                       error = valerie_invalid_file;
+                                       break;
+                       }
+               }
+               valerie_tokeniser_close( tokeniser );
+       }
+       return error;
+}
+
+/** Get the number of entries in the directory
+*/
+
+int valerie_dir_count( valerie_dir dir )
+{
+       if ( dir != NULL && valerie_response_count( dir->response ) >= 2 )
+               return valerie_response_count( dir->response ) - 2;
+       else
+               return -1;
+}
+
+/** Close the directory structure.
+*/
+
+void valerie_dir_close( valerie_dir dir )
+{
+       if ( dir != NULL )
+       {
+               free( dir->directory );
+               valerie_response_close( dir->response );
+               free( dir );
+       }
+}
+
+/** List the playlist of the specified unit.
+*/
+
+valerie_list valerie_list_init( valerie this, int unit )
+{
+       valerie_list list = calloc( 1, sizeof( valerie_list_t ) );
+       if ( list != NULL )
+       {
+               list->response = valerie_parser_executef( this->parser, "LIST U%d", unit );
+               if ( valerie_response_count( list->response ) >= 2 )
+                       list->generation = atoi( valerie_response_get_line( list->response, 1 ) );
+       }
+       return list;
+}
+
+/** Return the error code associated to the list.
+*/
+
+valerie_error_code valerie_list_get_error_code( valerie_list list )
+{
+       if ( list != NULL )
+               return valerie_get_error_code( NULL, list->response );
+       else
+               return valerie_malloc_failed;
+}
+
+/** Get a particular file entry in the list.
+*/
+
+valerie_error_code valerie_list_get( valerie_list list, int index, valerie_list_entry entry )
+{
+       valerie_error_code error = valerie_ok;
+       memset( entry, 0, sizeof( valerie_list_entry_t ) );
+       if ( index < valerie_list_count( list ) )
+       {
+               char *line = valerie_response_get_line( list->response, index + 2 );
+               valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+               valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+               if ( valerie_tokeniser_count( tokeniser ) > 0 )
+               {
+                       entry->clip = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+                       valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 1 ), '\"' );
+                       strcpy( entry->full, valerie_tokeniser_get_string( tokeniser, 1 ) );
+                       entry->in = atol( valerie_tokeniser_get_string( tokeniser, 2 ) );
+                       entry->out = atol( valerie_tokeniser_get_string( tokeniser, 3 ) );
+                       entry->max = atol( valerie_tokeniser_get_string( tokeniser, 4 ) );
+                       entry->size = atol( valerie_tokeniser_get_string( tokeniser, 5 ) );
+                       entry->fps = atof( valerie_tokeniser_get_string( tokeniser, 6 ) );
+               }
+               valerie_tokeniser_close( tokeniser );
+       }
+       return error;
+}
+
+/** Get the number of entries in the list
+*/
+
+int valerie_list_count( valerie_list list )
+{
+       if ( list != NULL && valerie_response_count( list->response ) >= 3 )
+               return valerie_response_count( list->response ) - 3;
+       else
+               return -1;
+}
+
+/** Close the list structure.
+*/
+
+void valerie_list_close( valerie_list list )
+{
+       if ( list != NULL )
+       {
+               valerie_response_close( list->response );
+               free( list );
+       }
+}
+
+/** List the currently connected nodes.
+*/
+
+valerie_nodes valerie_nodes_init( valerie this )
+{
+       valerie_nodes nodes = malloc( sizeof( valerie_nodes_t ) );
+       if ( nodes != NULL )
+       {
+               memset( nodes, 0, sizeof( valerie_nodes_t ) );
+               nodes->response = valerie_parser_executef( this->parser, "NLS" );
+       }
+       return nodes;
+}
+
+/** Return the error code associated to the nodes list.
+*/
+
+valerie_error_code valerie_nodes_get_error_code( valerie_nodes nodes )
+{
+       if ( nodes != NULL )
+               return valerie_get_error_code( NULL, nodes->response );
+       else
+               return valerie_malloc_failed;
+}
+
+/** Get a particular node entry.
+*/
+
+valerie_error_code valerie_nodes_get( valerie_nodes nodes, int index, valerie_node_entry entry )
+{
+       valerie_error_code error = valerie_ok;
+       memset( entry, 0, sizeof( valerie_node_entry_t ) );
+       if ( index < valerie_nodes_count( nodes ) )
+       {
+               char *line = valerie_response_get_line( nodes->response, index + 1 );
+               valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+               valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+               if ( valerie_tokeniser_count( tokeniser ) == 3 )
+               {
+                       entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+                       strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 1 ), sizeof( entry->guid ) );
+                       valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' );
+                       strncpy( entry->name, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->name ) );
+               }
+
+               valerie_tokeniser_close( tokeniser );
+       }
+       return error;
+}
+
+/** Get the number of nodes
+*/
+
+int valerie_nodes_count( valerie_nodes nodes )
+{
+       if ( nodes != NULL && valerie_response_count( nodes->response ) >= 2 )
+               return valerie_response_count( nodes->response ) - 2;
+       else
+               return -1;
+}
+
+/** Close the nodes structure.
+*/
+
+void valerie_nodes_close( valerie_nodes nodes )
+{
+       if ( nodes != NULL )
+       {
+               valerie_response_close( nodes->response );
+               free( nodes );
+       }
+}
+
+/** List the currently defined units.
+*/
+
+valerie_units valerie_units_init( valerie this )
+{
+       valerie_units units = malloc( sizeof( valerie_units_t ) );
+       if ( units != NULL )
+       {
+               memset( units, 0, sizeof( valerie_units_t ) );
+               units->response = valerie_parser_executef( this->parser, "ULS" );
+       }
+       return units;
+}
+
+/** Return the error code associated to the nodes list.
+*/
+
+valerie_error_code valerie_units_get_error_code( valerie_units units )
+{
+       if ( units != NULL )
+               return valerie_get_error_code( NULL, units->response );
+       else
+               return valerie_malloc_failed;
+}
+
+/** Get a particular unit entry.
+*/
+
+valerie_error_code valerie_units_get( valerie_units units, int index, valerie_unit_entry entry )
+{
+       valerie_error_code error = valerie_ok;
+       memset( entry, 0, sizeof( valerie_unit_entry_t ) );
+       if ( index < valerie_units_count( units ) )
+       {
+               char *line = valerie_response_get_line( units->response, index + 1 );
+               valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+               valerie_tokeniser_parse_new( tokeniser, line, " " );
+
+               if ( valerie_tokeniser_count( tokeniser ) == 4 )
+               {
+                       entry->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) + 1 );
+                       entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 1 ) );
+                       strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->guid ) );
+                       entry->online = atoi( valerie_tokeniser_get_string( tokeniser, 3 ) );
+               }
+
+               valerie_tokeniser_close( tokeniser );
+       }
+       return error;
+}
+
+/** Get the number of units
+*/
+
+int valerie_units_count( valerie_units units )
+{
+       if ( units != NULL && valerie_response_count( units->response ) >= 2 )
+               return valerie_response_count( units->response ) - 2;
+       else
+               return -1;
+}
+
+/** Close the units structure.
+*/
+
+void valerie_units_close( valerie_units units )
+{
+       if ( units != NULL )
+       {
+               valerie_response_close( units->response );
+               free( units );
+       }
+}
+
+/** Get the response of the last command executed.
+*/
+
+valerie_response valerie_get_last_response( valerie this )
+{
+       return this->last_response;
+}
+
+/** Obtain a printable message associated to the error code provided.
+*/
+
+const char *valerie_error_description( valerie_error_code error )
+{
+       const char *msg = "Unrecognised error";
+       switch( error )
+       {
+               case valerie_ok:
+                       msg = "OK";
+                       break;
+               case valerie_malloc_failed:
+                       msg = "Memory allocation error";
+                       break;
+               case valerie_unknown_error:
+                       msg = "Unknown error";
+                       break;
+               case valerie_no_response:
+                       msg = "No response obtained";
+                       break;
+               case valerie_invalid_command:
+                       msg = "Invalid command";
+                       break;
+               case valerie_server_timeout:
+                       msg = "Communications with server timed out";
+                       break;
+               case valerie_missing_argument:
+                       msg = "Missing argument";
+                       break;
+               case valerie_server_unavailable:
+                       msg = "Unable to communicate with server";
+                       break;
+               case valerie_unit_creation_failed:
+                       msg = "Unit creation failed";
+                       break;
+               case valerie_unit_unavailable:
+                       msg = "Unit unavailable";
+                       break;
+               case valerie_invalid_file:
+                       msg = "Invalid file";
+                       break;
+               case valerie_invalid_position:
+                       msg = "Invalid position";
+                       break;
+       }
+       return msg;
+}
+
+/** Close the valerie structure.
+*/
+
+void valerie_close( valerie this )
+{
+       if ( this != NULL )
+       {
+               valerie_set_last_response( this, NULL );
+               free( this );
+       }
+}
diff --git a/src/valerie/valerie.h b/src/valerie/valerie.h
new file mode 100644 (file)
index 0000000..3b5ead2
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * valerie.h -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_H_
+#define _VALERIE_H_
+
+/* System header files */
+#include <limits.h>
+
+/* MLT Header files. */
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_status.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Client error conditions
+*/
+
+typedef enum
+{
+       valerie_ok = 0,
+       valerie_malloc_failed,
+       valerie_unknown_error,
+       valerie_no_response,
+       valerie_invalid_command,
+       valerie_server_timeout,
+       valerie_missing_argument,
+       valerie_server_unavailable,
+       valerie_unit_creation_failed,
+       valerie_unit_unavailable,
+       valerie_invalid_file,
+       valerie_invalid_position
+}
+valerie_error_code;
+
+/** Clip index specification.
+*/
+
+typedef enum
+{
+       valerie_absolute = 0,
+       valerie_relative
+}
+valerie_clip_offset;
+
+/** Client structure.
+*/
+
+typedef struct
+{
+       valerie_parser parser;
+       valerie_response last_response;
+}
+*valerie, valerie_t;
+
+/** Client API.
+*/
+
+extern valerie valerie_init( valerie_parser );
+
+/* Connect to the valerie parser instance */
+extern valerie_error_code valerie_connect( valerie );
+
+/* Global functions */
+extern valerie_error_code valerie_set( valerie, char *, char * );
+extern valerie_error_code valerie_get( valerie, char *, char *, int );
+extern valerie_error_code valerie_run( valerie, char * );
+
+/* Unit functions */
+extern valerie_error_code valerie_unit_add( valerie, char *, int * );
+extern valerie_error_code valerie_unit_load( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_clipped( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_load_back( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_back_clipped( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_append( valerie, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_receive( valerie, int, char *, char * );
+extern valerie_error_code valerie_unit_push( valerie, int, char *, mlt_service );
+extern valerie_error_code valerie_unit_clean( valerie, int );
+extern valerie_error_code valerie_unit_wipe( valerie, int );
+extern valerie_error_code valerie_unit_clear( valerie, int );
+extern valerie_error_code valerie_unit_clip_move( valerie, int, valerie_clip_offset, int, valerie_clip_offset, int );
+extern valerie_error_code valerie_unit_clip_remove( valerie, int, valerie_clip_offset, int );
+extern valerie_error_code valerie_unit_remove_current_clip( valerie, int );
+extern valerie_error_code valerie_unit_clip_insert( valerie, int, valerie_clip_offset, int, char *, int32_t, int32_t );
+extern valerie_error_code valerie_unit_play( valerie, int );
+extern valerie_error_code valerie_unit_play_at_speed( valerie, int, int );
+extern valerie_error_code valerie_unit_stop( valerie, int );
+extern valerie_error_code valerie_unit_pause( valerie, int );
+extern valerie_error_code valerie_unit_rewind( valerie, int );
+extern valerie_error_code valerie_unit_fast_forward( valerie, int );
+extern valerie_error_code valerie_unit_step( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_goto( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_clip_goto( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_clip_set_in( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_clip_set_out( valerie, int, valerie_clip_offset, int, int32_t );
+extern valerie_error_code valerie_unit_set_in( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_set_out( valerie, int, int32_t );
+extern valerie_error_code valerie_unit_clear_in( valerie, int );
+extern valerie_error_code valerie_unit_clear_out( valerie, int );
+extern valerie_error_code valerie_unit_clear_in_out( valerie, int );
+extern valerie_error_code valerie_unit_set( valerie, int, const char *, const char * );
+extern valerie_error_code valerie_unit_get( valerie, int, char * );
+extern valerie_error_code valerie_unit_status( valerie, int, valerie_status );
+extern valerie_error_code valerie_unit_transfer( valerie, int, int );
+
+/* Notifier functionality. */
+extern valerie_notifier valerie_get_notifier( valerie );
+
+/** Structure for the directory.
+*/
+
+typedef struct
+{
+       char *directory;
+       valerie_response response;
+}
+*valerie_dir, valerie_dir_t;
+
+/** Directory entry structure.
+*/
+
+typedef struct
+{
+       int dir;
+       char name[ NAME_MAX ];
+       char full[ PATH_MAX + NAME_MAX ];
+       unsigned long long size;
+}
+*valerie_dir_entry, valerie_dir_entry_t;
+
+/* Directory reading. */
+extern valerie_dir valerie_dir_init( valerie, const char * );
+extern valerie_error_code valerie_dir_get_error_code( valerie_dir );
+extern valerie_error_code valerie_dir_get( valerie_dir, int, valerie_dir_entry );
+extern int valerie_dir_count( valerie_dir );
+extern void valerie_dir_close( valerie_dir );
+
+/** Structure for the list.
+*/
+
+typedef struct
+{
+       int generation;
+       valerie_response response;
+}
+*valerie_list, valerie_list_t;
+
+/** List entry structure.
+*/
+
+typedef struct
+{
+       int clip;
+       char full[ PATH_MAX + NAME_MAX ];
+       int32_t in;
+       int32_t out;
+       int32_t max;
+       int32_t size;
+       int32_t fps;
+}
+*valerie_list_entry, valerie_list_entry_t;
+
+/* List reading. */
+extern valerie_list valerie_list_init( valerie, int );
+extern valerie_error_code valerie_list_get_error_code( valerie_list );
+extern valerie_error_code valerie_list_get( valerie_list, int, valerie_list_entry );
+extern int valerie_list_count( valerie_list );
+extern void valerie_list_close( valerie_list );
+
+/** Structure for nodes.
+*/
+
+typedef struct
+{
+       valerie_response response;
+}
+*valerie_nodes, valerie_nodes_t;
+
+/** Node entry structure.
+*/
+
+typedef struct
+{
+       int node;
+       char guid[ 17 ];
+       char name[ 1024 ];
+}
+*valerie_node_entry, valerie_node_entry_t;
+
+/* Node reading. */
+extern valerie_nodes valerie_nodes_init( valerie );
+extern valerie_error_code valerie_nodes_get_error_code( valerie_nodes );
+extern valerie_error_code valerie_nodes_get( valerie_nodes, int, valerie_node_entry );
+extern int valerie_nodes_count( valerie_nodes );
+extern void valerie_nodes_close( valerie_nodes );
+
+/** Structure for units.
+*/
+
+typedef struct
+{
+       valerie_response response;
+}
+*valerie_units, valerie_units_t;
+
+/** Unit entry structure.
+*/
+
+typedef struct
+{
+       int unit;
+       int node;
+       char guid[ 512 ];
+       int online;
+}
+*valerie_unit_entry, valerie_unit_entry_t;
+
+/* Unit reading. */
+extern valerie_units valerie_units_init( valerie );
+extern valerie_error_code valerie_units_get_error_code( valerie_units );
+extern valerie_error_code valerie_units_get( valerie_units, int, valerie_unit_entry );
+extern int valerie_units_count( valerie_units );
+extern void valerie_units_close( valerie_units );
+
+/* Miscellaenous functions */
+extern valerie_response valerie_get_last_response( valerie );
+extern const char *valerie_error_description( valerie_error_code );
+
+/* Courtesy functions. */
+extern valerie_error_code valerie_execute( valerie, size_t, const char *, ... );
+extern valerie_error_code valerie_push( valerie, mlt_service, size_t, const char *, ... );
+
+/* Close function. */
+extern void valerie_close( valerie );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_notifier.c b/src/valerie/valerie_notifier.c
new file mode 100644 (file)
index 0000000..5e34043
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * valerie_notifier.c -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_notifier.h"
+
+/** Notifier initialisation.
+*/
+
+valerie_notifier valerie_notifier_init( )
+{
+       valerie_notifier this = calloc( 1, sizeof( valerie_notifier_t ) );
+       if ( this != NULL )
+       {
+               int index = 0;
+               pthread_mutex_init( &this->mutex, NULL );
+               pthread_cond_init( &this->cond, NULL );
+               for ( index = 0; index < MAX_UNITS; index ++ )
+                       this->store[ index ].unit = index;
+       }
+       return this;
+}
+
+/** Get a stored status for the specified unit.
+*/
+
+void valerie_notifier_get( valerie_notifier this, valerie_status status, int unit )
+{
+       pthread_mutex_lock( &this->mutex );
+       if ( unit >= 0 && unit < MAX_UNITS )
+               valerie_status_copy( status, &this->store[ unit ] );
+       else
+               memset( status, 0, sizeof( valerie_status_t ) );
+       status->unit = unit;
+       status->dummy = time( NULL );
+       pthread_mutex_unlock( &this->mutex );
+}
+
+/** Wait on a new status.
+*/
+
+int valerie_notifier_wait( valerie_notifier this, valerie_status status )
+{
+       struct timeval now;
+       struct timespec timeout;
+       int error = 0;
+
+       memset( status, 0, sizeof( valerie_status_t ) );
+       gettimeofday( &now, NULL );
+       timeout.tv_sec = now.tv_sec + 1;
+       timeout.tv_nsec = now.tv_usec * 1000;
+       pthread_mutex_lock( &this->mutex );
+       pthread_cond_timedwait( &this->cond, &this->mutex, &timeout );
+       valerie_status_copy( status, &this->last );
+       pthread_mutex_unlock( &this->mutex );
+
+       return error;
+}
+
+/** Put a new status.
+*/
+
+void valerie_notifier_put( valerie_notifier this, valerie_status status )
+{
+       pthread_mutex_lock( &this->mutex );
+       valerie_status_copy( &this->store[ status->unit ], status );
+       valerie_status_copy( &this->last, status );
+       pthread_mutex_unlock( &this->mutex );
+       pthread_cond_broadcast( &this->cond );
+}
+
+/** Communicate a disconnected status for all units to all waiting.
+*/
+
+void valerie_notifier_disconnected( valerie_notifier notifier )
+{
+       int unit = 0;
+       valerie_status_t status;
+       for ( unit = 0; unit < MAX_UNITS; unit ++ )
+       {
+               valerie_notifier_get( notifier, &status, unit );
+               status.status = unit_disconnected;
+               valerie_notifier_put( notifier, &status );
+       }
+}
+
+/** Close the notifier - note that all access must be stopped before we call this.
+*/
+
+void valerie_notifier_close( valerie_notifier this )
+{
+       if ( this != NULL )
+       {
+               pthread_mutex_destroy( &this->mutex );
+               pthread_cond_destroy( &this->cond );
+               free( this );
+       }
+}
diff --git a/src/valerie/valerie_notifier.h b/src/valerie/valerie_notifier.h
new file mode 100644 (file)
index 0000000..9751c08
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * valerie_notifier.h -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_NOTIFIER_H_
+#define _VALERIE_NOTIFIER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include "valerie_status.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MAX_UNITS 16
+
+/** Status notifier definition.
+*/
+
+typedef struct
+{
+       pthread_mutex_t mutex;
+       pthread_cond_t cond;
+       valerie_status_t last;
+       valerie_status_t store[ MAX_UNITS ];
+}
+*valerie_notifier, valerie_notifier_t;
+
+extern valerie_notifier valerie_notifier_init( );
+extern void valerie_notifier_get( valerie_notifier, valerie_status, int );
+extern int valerie_notifier_wait( valerie_notifier, valerie_status );
+extern void valerie_notifier_put( valerie_notifier, valerie_status );
+extern void valerie_notifier_disconnected( valerie_notifier );
+extern void valerie_notifier_close( valerie_notifier );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_parser.c b/src/valerie/valerie_parser.c
new file mode 100644 (file)
index 0000000..1a74c50
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * valerie_parser.c -- Valerie Parser for Miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_util.h"
+
+/** Connect to the parser.
+*/
+
+valerie_response valerie_parser_connect( valerie_parser parser )
+{
+       return parser->connect( parser->real );
+}
+
+/** Execute a command via the parser.
+*/
+
+valerie_response valerie_parser_execute( valerie_parser parser, char *command )
+{
+       return parser->execute( parser->real, command );
+}
+
+/** Push a service via the parser.
+*/
+
+valerie_response valerie_parser_received( valerie_parser parser, char *command, char *doc )
+{
+       return parser->received != NULL ? parser->received( parser->real, command, doc ) : NULL;
+}
+
+/** Push a service via the parser.
+*/
+
+valerie_response valerie_parser_push( valerie_parser parser, char *command, mlt_service service )
+{
+       return parser->push( parser->real, command, service );
+}
+
+/** Execute a formatted command via the parser.
+*/
+
+valerie_response valerie_parser_executef( valerie_parser parser, const char *format, ... )
+{
+       char *command = malloc( 10240 );
+       valerie_response response = NULL;
+       if ( command != NULL )
+       {
+               va_list list;
+               va_start( list, format );
+               if ( vsnprintf( command, 10240, format, list ) != 0 )
+                       response = valerie_parser_execute( parser, command );
+               va_end( list );
+               free( command );
+       }
+       return response;
+}
+
+/** Execute the contents of a file. Note the special case valerie_response returned.
+*/
+
+valerie_response valerie_parser_run( valerie_parser parser, char *filename )
+{
+       valerie_response response = valerie_response_init( );
+       if ( response != NULL )
+       {
+               FILE *file = fopen( filename, "r" );
+               if ( file != NULL )
+               {
+                       char command[ 1024 ];
+                       valerie_response_set_error( response, 201, "OK" );
+                       while ( valerie_response_get_error_code( response ) == 201 && fgets( command, 1024, file ) )
+                       {
+                               valerie_util_trim( valerie_util_chomp( command ) );
+                               if ( strcmp( command, "" ) && command[ 0 ] != '#' )
+                               {
+                                       valerie_response temp = NULL;
+                                       valerie_response_printf( response, 1024, "%s\n", command );
+                                       temp = valerie_parser_execute( parser, command );
+                                       if ( temp != NULL )
+                                       {
+                                               int index = 0;
+                                               for ( index = 0; index < valerie_response_count( temp ); index ++ )
+                                                       valerie_response_printf( response, 10240, "%s\n", valerie_response_get_line( temp, index ) );
+                                               valerie_response_close( temp );
+                                       }
+                                       else
+                                       {
+                                               valerie_response_set_error( response, 500, "Batch execution failed" );
+                                       }
+                               }
+                       }
+                       fclose( file );
+               }
+               else
+               {
+                       valerie_response_set_error( response, 404, "File not found." );
+               }
+       }
+       return response;
+}
+
+/** Get the notifier associated to the parser.
+*/
+
+valerie_notifier valerie_parser_get_notifier( valerie_parser parser )
+{
+       if ( parser->notifier == NULL )
+               parser->notifier = valerie_notifier_init( );
+       return parser->notifier;
+}
+
+/** Close the parser.
+*/
+
+void valerie_parser_close( valerie_parser parser )
+{
+       if ( parser != NULL )
+       {
+               parser->close( parser->real );
+               valerie_notifier_close( parser->notifier );
+               free( parser );
+       }
+}
diff --git a/src/valerie/valerie_parser.h b/src/valerie/valerie_parser.h
new file mode 100644 (file)
index 0000000..2860dd5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * valerie_parser.h -- Valerie Parser for Miracle Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_PARSER_H_
+#define _VALERIE_PARSER_H_
+
+/* MLT Header files */
+#include <framework/mlt.h>
+
+/* Application header files */
+#include "valerie_response.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Callbacks to define the parser.
+*/
+
+typedef valerie_response (*parser_connect)( void * );
+typedef valerie_response (*parser_execute)( void *, char * );
+typedef valerie_response (*parser_received)( void *, char *, char * );
+typedef valerie_response (*parser_push)( void *, char *, mlt_service );
+typedef void (*parser_close)( void * );
+
+/** Structure for the valerie parser.
+*/
+
+typedef struct
+{
+       parser_connect connect;
+       parser_execute execute;
+       parser_push push;
+       parser_received received;
+       parser_close close;
+       void *real;
+       valerie_notifier notifier;
+}
+*valerie_parser, valerie_parser_t;
+
+/** API for the parser - note that no constructor is defined here.
+*/
+
+extern valerie_response valerie_parser_connect( valerie_parser );
+extern valerie_response valerie_parser_push( valerie_parser, char *, mlt_service );
+extern valerie_response valerie_parser_received( valerie_parser, char *, char * );
+extern valerie_response valerie_parser_execute( valerie_parser, char * );
+extern valerie_response valerie_parser_executef( valerie_parser, const char *, ... );
+extern valerie_response valerie_parser_run( valerie_parser, char * );
+extern valerie_notifier valerie_parser_get_notifier( valerie_parser );
+extern void valerie_parser_close( valerie_parser );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_remote.c b/src/valerie/valerie_remote.c
new file mode 100644 (file)
index 0000000..580b0be
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * valerie_remote.c -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Application header files */
+#include <framework/mlt.h>
+#include "valerie_remote.h"
+#include "valerie_socket.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Private valerie_remote structure.
+*/
+
+typedef struct
+{
+       int terminated;
+       char *server;
+       int port;
+       valerie_socket socket;
+       valerie_socket status;
+       pthread_t thread;
+       valerie_parser parser;
+       pthread_mutex_t mutex;
+       int connected;
+}
+*valerie_remote, valerie_remote_t;
+
+/** Forward declarations.
+*/
+
+static valerie_response valerie_remote_connect( valerie_remote );
+static valerie_response valerie_remote_execute( valerie_remote, char * );
+static valerie_response valerie_remote_receive( valerie_remote, char *, char * );
+static valerie_response valerie_remote_push( valerie_remote, char *, mlt_service );
+static void valerie_remote_close( valerie_remote );
+static int valerie_remote_read_response( valerie_socket, valerie_response );
+
+/** DV Parser constructor.
+*/
+
+valerie_parser valerie_parser_init_remote( char *server, int port )
+{
+       valerie_parser parser = calloc( 1, sizeof( valerie_parser_t ) );
+       valerie_remote remote = calloc( 1, sizeof( valerie_remote_t ) );
+
+       if ( parser != NULL )
+       {
+               parser->connect = (parser_connect)valerie_remote_connect;
+               parser->execute = (parser_execute)valerie_remote_execute;
+               parser->push = (parser_push)valerie_remote_push;
+               parser->received = (parser_received)valerie_remote_receive;
+               parser->close = (parser_close)valerie_remote_close;
+               parser->real = remote;
+
+               if ( remote != NULL )
+               {
+                       remote->parser = parser;
+                       remote->server = strdup( server );
+                       remote->port = port;
+                       pthread_mutex_init( &remote->mutex, NULL );
+               }
+       }
+       return parser;
+}
+
+/** Thread for receiving and distributing the status information.
+*/
+
+static void *valerie_remote_status_thread( void *arg )
+{
+       valerie_remote remote = arg;
+       char temp[ 10240 ];
+       int length = 0;
+       int offset = 0;
+       valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+       valerie_notifier notifier = valerie_parser_get_notifier( remote->parser );
+       valerie_status_t status;
+       int index = 0;
+
+       valerie_socket_write_data( remote->status, "STATUS\r\n", 8 );
+
+       while ( !remote->terminated && 
+                       ( length = valerie_socket_read_data( remote->status, temp + offset, sizeof( temp ) ) ) >= 0 )
+       {
+               if ( strchr( temp, '\n' ) == NULL )
+               {
+                       offset = length;
+                       continue;
+               }
+               offset = 0;
+               valerie_tokeniser_parse_new( tokeniser, temp, "\n" );
+               for ( index = 0; index < valerie_tokeniser_count( tokeniser ); index ++ )
+               {
+                       char *line = valerie_tokeniser_get_string( tokeniser, index );
+                       if ( line[ strlen( line ) - 1 ] == '\r' )
+                       {
+                               valerie_util_chomp( line );
+                               valerie_status_parse( &status, line );
+                               valerie_notifier_put( notifier, &status );
+                       }
+                       else
+                       {
+                               strcpy( temp, line );
+                               offset = strlen( temp );
+                       }
+               }
+       }
+
+       valerie_notifier_disconnected( notifier );
+       valerie_tokeniser_close( tokeniser );
+       remote->terminated = 1;
+
+       return NULL;
+}
+
+/** Forward reference.
+*/
+
+static void valerie_remote_disconnect( valerie_remote remote );
+
+/** Connect to the server.
+*/
+
+static valerie_response valerie_remote_connect( valerie_remote remote )
+{
+       valerie_response response = NULL;
+
+       valerie_remote_disconnect( remote );
+
+       if ( !remote->connected )
+       {
+               signal( SIGPIPE, SIG_IGN );
+
+               remote->socket = valerie_socket_init( remote->server, remote->port );
+               remote->status = valerie_socket_init( remote->server, remote->port );
+
+               if ( valerie_socket_connect( remote->socket ) == 0 )
+               {
+                       response = valerie_response_init( );
+                       valerie_remote_read_response( remote->socket, response );
+               }
+
+               if ( response != NULL && valerie_socket_connect( remote->status ) == 0 )
+               {
+                       valerie_response status_response = valerie_response_init( );
+                       valerie_remote_read_response( remote->status, status_response );
+                       if ( valerie_response_get_error_code( status_response ) == 100 )
+                               pthread_create( &remote->thread, NULL, valerie_remote_status_thread, remote );
+                       valerie_response_close( status_response );
+                       remote->connected = 1;
+               }
+       }
+
+       return response;
+}
+
+/** Execute the command.
+*/
+
+static valerie_response valerie_remote_execute( valerie_remote remote, char *command )
+{
+       valerie_response response = NULL;
+       pthread_mutex_lock( &remote->mutex );
+       if ( valerie_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
+       {
+               response = valerie_response_init( );
+               valerie_socket_write_data( remote->socket, "\r\n", 2 );
+               valerie_remote_read_response( remote->socket, response );
+       }
+       pthread_mutex_unlock( &remote->mutex );
+       return response;
+}
+
+/** Push a westley document to the server.
+*/
+
+static valerie_response valerie_remote_receive( valerie_remote remote, char *command, char *buffer )
+{
+       valerie_response response = NULL;
+       pthread_mutex_lock( &remote->mutex );
+       if ( valerie_socket_write_data( remote->socket, command, strlen( command ) ) == strlen( command ) )
+       {
+               char temp[ 20 ];
+               int length = strlen( buffer );
+               response = valerie_response_init( );
+               valerie_socket_write_data( remote->socket, "\r\n", 2 );
+               sprintf( temp, "%d", length );
+               valerie_socket_write_data( remote->socket, temp, strlen( temp ) );
+               valerie_socket_write_data( remote->socket, "\r\n", 2 );
+               valerie_socket_write_data( remote->socket, buffer, length );
+               valerie_socket_write_data( remote->socket, "\r\n", 2 );
+               valerie_remote_read_response( remote->socket, response );
+       }
+       pthread_mutex_unlock( &remote->mutex );
+       return response;
+}
+
+/** Push a producer to the server.
+*/
+
+static valerie_response valerie_remote_push( valerie_remote remote, char *command, mlt_service service )
+{
+       valerie_response response = NULL;
+       if ( service != NULL )
+       {
+               mlt_consumer consumer = mlt_factory_consumer( NULL, "westley", "buffer" );
+               mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+               char *buffer = NULL;
+               // Temporary hack
+               mlt_properties_set( properties, "store", "nle_" );
+               mlt_consumer_connect( consumer, service );
+               mlt_consumer_start( consumer );
+               buffer = mlt_properties_get( properties, "buffer" );
+               response = valerie_remote_receive( remote, command, buffer );
+               mlt_consumer_close( consumer );
+       }
+       return response;
+}
+
+/** Disconnect.
+*/
+
+static void valerie_remote_disconnect( valerie_remote remote )
+{
+       if ( remote != NULL && remote->terminated )
+       {
+               if ( remote->connected )
+                       pthread_join( remote->thread, NULL );
+               valerie_socket_close( remote->status );
+               valerie_socket_close( remote->socket );
+               remote->connected = 0;
+               remote->terminated = 0;
+       }
+}
+
+/** Close the parser.
+*/
+
+static void valerie_remote_close( valerie_remote remote )
+{
+       if ( remote != NULL )
+       {
+               remote->terminated = 1;
+               valerie_remote_disconnect( remote );
+               pthread_mutex_destroy( &remote->mutex );
+               free( remote->server );
+               free( remote );
+       }
+}
+
+/** Read response. 
+*/
+
+static int valerie_remote_read_response( valerie_socket socket, valerie_response response )
+{
+       char temp[ 10240 ];
+       int length;
+       int terminated = 0;
+
+       while ( !terminated && ( length = valerie_socket_read_data( socket, temp, 10240 ) ) >= 0 )
+       {
+               int position = 0;
+               temp[ length ] = '\0';
+               valerie_response_write( response, temp, length );
+               position = valerie_response_count( response ) - 1;
+               if ( position < 0 || temp[ strlen( temp ) - 1 ] != '\n' )
+                       continue;
+               switch( valerie_response_get_error_code( response ) )
+               {
+                       case 201:
+                       case 500:
+                               terminated = !strcmp( valerie_response_get_line( response, position ), "" );
+                               break;
+                       case 202:
+                               terminated = valerie_response_count( response ) >= 2;
+                               break;
+                       default:
+                               terminated = 1;
+                               break;
+               }
+       }
+
+       return 0;
+}
diff --git a/src/valerie/valerie_remote.h b/src/valerie/valerie_remote.h
new file mode 100644 (file)
index 0000000..291184a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * valerie_remote.h -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_REMOTE_H_
+#define _VALERIE_REMOTE_H_
+
+/* Application header files */
+#include "valerie_parser.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Remote parser API.
+*/
+
+extern valerie_parser valerie_parser_init_remote( char *, int );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_response.c b/src/valerie/valerie_response.c
new file mode 100644 (file)
index 0000000..6be2c87
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * valerie_response.c -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_response.h"
+
+/** Construct a new dv response.
+*/
+
+valerie_response valerie_response_init( )
+{
+       valerie_response response = malloc( sizeof( valerie_response_t ) );
+       if ( response != NULL )
+               memset( response, 0, sizeof( valerie_response_t ) );
+       return response;
+}
+
+/** Clone a dv response
+*/
+
+valerie_response valerie_response_clone( valerie_response response )
+{
+       valerie_response clone = valerie_response_init( );
+       if ( clone != NULL && response != NULL )
+       {
+               int index = 0;
+               for ( index = 0; index < valerie_response_count( response ); index ++ )
+               {
+                       char *line = valerie_response_get_line( response, index );
+                       valerie_response_printf( clone, strlen( line ) + 2, "%s\n", line );
+               }
+       }
+       return clone;
+}
+
+/** Get the error code associated to the response.
+*/
+
+int valerie_response_get_error_code( valerie_response response )
+{
+       int error_code = -1;
+       if ( response != NULL )
+       {
+               if ( response->count > 0 )
+               {
+                       if ( sscanf( response->array[ 0 ], "%d", &error_code ) != 1 )
+                               error_code = 0;
+               }
+               else
+               {
+                       error_code = -2;
+               }
+       }
+       return error_code;
+}
+
+/** Get the error description associated to the response.
+*/
+
+const char *valerie_response_get_error_string( valerie_response response )
+{
+       const char *error_string = "No message specified";
+       if ( response->count > 0 )
+       {
+               char *ptr = strchr( response->array[ 0 ], ' ' ) ;
+               if ( ptr != NULL )
+                       error_string = ptr + 1;
+       }
+       return error_string;
+}
+
+/** Get a line of text at the given index. Note that the text itself is
+       terminated only with a NUL char and it is the responsibility of the
+       the user of the returned data to use a LF or CR/LF as appropriate.
+*/
+
+char *valerie_response_get_line( valerie_response response, int index )
+{
+       if ( index < response->count )
+               return response->array[ index ];
+       else
+               return NULL;
+}
+
+/** Return the number of lines of text in the response.
+*/
+
+int valerie_response_count( valerie_response response )
+{
+       if ( response != NULL )
+               return response->count;
+       else
+               return 0;
+}
+
+/** Set the error and description associated to the response.
+*/
+
+void valerie_response_set_error( valerie_response response, int error_code, const char *error_string )
+{
+       if ( response->count == 0 )
+       {
+               valerie_response_printf( response, 10240, "%d %s\n", error_code, error_string );
+       }
+       else
+       {
+               char temp[ 10240 ];
+               int length = sprintf( temp, "%d %s", error_code, error_string );
+               response->array[ 0 ] = realloc( response->array[ 0 ], length + 1 );
+               strcpy( response->array[ 0 ], temp );
+       }
+}
+
+/** Write formatted text to the response. 
+*/
+
+int valerie_response_printf( valerie_response response, size_t size, const char *format, ... )
+{
+       int length = 0;
+       char *text = malloc( size );
+       if ( text != NULL )
+       {
+               va_list list;
+               va_start( list, format );
+               length = vsnprintf( text, size, format, list );
+               if ( length != 0 )
+                       valerie_response_write( response, text, length );
+               va_end( list );
+               free( text );
+       }
+       return length;
+}
+
+/** Write text to the reponse.
+*/
+
+int valerie_response_write( valerie_response response, const char *text, int size )
+{
+       int ret = 0;
+       const char *ptr = text;
+
+       while ( size > 0 )
+       {
+               int index = response->count - 1;
+               const char *lf = strchr( ptr, '\n' );
+               int length_of_string = 0;
+
+               /* Make sure we have space in the dynamic array. */
+               if ( !response->append && response->count >= response->size - 1 )
+               {
+                       response->size += 50;
+                       response->array = realloc( response->array, response->size * sizeof( char * ) );
+               }
+
+               /* Make sure the array is valid, or we're really in trouble */
+               if ( response->array == NULL )
+               {
+                       ret = 0;
+                       break;
+               }
+
+               /* Now, if we're appending to the previous write (ie: if it wasn't
+                  terminated by a LF), then use the index calculated above, otherwise
+                  go to the next one and ensure it's NULLed. */
+
+               if ( !response->append )
+               {
+                       response->array[ ++ index ] = NULL;
+                       response->count ++;
+               }
+               else
+               {
+                       length_of_string = strlen( response->array[ index ] );
+               }
+
+               /* Now we need to know how to handle the current ptr with respect to lf. */
+               /* TODO: tidy up and error check... sigh... tested for many, many 1000s of lines */
+
+               if ( lf == NULL )
+               {
+                       response->array[ index ] = realloc( response->array[ index ], length_of_string + size + 1 );
+                       memcpy( response->array[ index ] + length_of_string, ptr, size );
+                       response->array[ index ][ length_of_string + size ] = '\0';
+                       if ( ( length_of_string + size ) > 0 && response->array[ index ][ length_of_string + size - 1 ] == '\r' )
+                               response->array[ index ][ length_of_string + size - 1 ] = '\0';
+                       size = 0;
+                       ret += size;
+                       response->append = 1;
+               }
+               else
+               {
+                       int chars = lf - ptr;
+                       response->array[ index ] = realloc( response->array[ index ], length_of_string + chars + 1 );
+                       memcpy( response->array[ index ] + length_of_string, ptr, chars );
+                       response->array[ index ][ length_of_string + chars ] = '\0';
+                       if ( ( length_of_string + chars ) > 0 && response->array[ index ][ length_of_string + chars - 1 ] == '\r' )
+                               response->array[ index ][ length_of_string + chars - 1 ] = '\0';
+                       ptr = ptr + chars + 1;
+                       size -= ( chars + 1 );
+                       response->append = 0;
+                       ret += chars + 1;
+               }
+       }
+
+       return ret;
+}
+
+/** Close the response.
+*/
+
+void valerie_response_close( valerie_response response )
+{
+       if ( response != NULL )
+       {
+               int index = 0;
+               for ( index = 0; index < response->count; index ++ )
+                       free( response->array[ index ] );
+               free( response->array );
+               free( response );
+       }
+}
diff --git a/src/valerie/valerie_response.h b/src/valerie/valerie_response.h
new file mode 100644 (file)
index 0000000..5bef606
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * valerie_response.h -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_RESPONSE_H_
+#define _VALERIE_RESPONSE_H_
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for the response
+*/
+
+typedef struct
+{
+       char **array;
+       int size;
+       int count;
+       int append;
+}
+*valerie_response, valerie_response_t;
+
+/** API for accessing the response structure.
+*/
+
+extern valerie_response valerie_response_init( );
+extern valerie_response valerie_response_clone( valerie_response );
+extern int valerie_response_get_error_code( valerie_response );
+extern const char *valerie_response_get_error_string( valerie_response );
+extern char *valerie_response_get_line( valerie_response, int );
+extern int valerie_response_count( valerie_response );
+extern void valerie_response_set_error( valerie_response, int, const char * );
+extern int valerie_response_printf( valerie_response, size_t, const char *, ... );
+extern int valerie_response_write( valerie_response, const char *, int );
+extern void valerie_response_close( valerie_response );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_socket.c b/src/valerie/valerie_socket.c
new file mode 100644 (file)
index 0000000..8a91832
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * valerie_socket.c -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_socket.h"
+
+/** Initialise the socket.
+*/
+
+valerie_socket valerie_socket_init( char *server, int port )
+{
+       valerie_socket socket = malloc( sizeof( valerie_socket_t ) );
+       if ( socket != NULL )
+       {
+               memset( socket, 0, sizeof( valerie_socket_t ) );
+               socket->fd = -1;
+               socket->server = strdup( server );
+               socket->port = port;
+       }
+       return socket;
+}
+
+/** Connect to the server.
+*/
+
+int valerie_socket_connect( valerie_socket connection )
+{
+       int ret = 0;
+    struct hostent *host;
+    struct sockaddr_in sock;
+
+       if ( connection->server != NULL )
+       {               
+               host = gethostbyname( connection->server );
+       
+               memset( &sock, 0, sizeof( struct sockaddr_in ) );
+               memcpy( &sock.sin_addr, host->h_addr, host->h_length );
+               sock.sin_family = host->h_addrtype;
+               sock.sin_port = htons( connection->port );
+       
+               if ( ( connection->fd = socket( AF_INET, SOCK_STREAM, 0 ) ) != -1 )
+                       ret = connect( connection->fd, (const struct sockaddr *)&sock, sizeof( struct sockaddr_in ) );
+               else
+                       ret = -1;
+       }
+       
+       return ret;     
+}
+
+/** Convenience constructor for a connected file descriptor.
+*/
+
+valerie_socket valerie_socket_init_fd( int fd )
+{
+       valerie_socket socket = malloc( sizeof( valerie_socket_t ) );
+       if ( socket != NULL )
+       {
+               memset( socket, 0, sizeof( valerie_socket_t ) );
+               socket->fd = fd;
+               socket->no_close = 1;
+       }
+       return socket;
+}
+
+/** Read an arbitrarily formatted block of data from the server.
+*/
+
+int valerie_socket_read_data( valerie_socket socket, char *data, int length )
+{
+    struct timeval tv = { 1, 0 };
+    fd_set rfds;
+       int used = 0;
+
+       data[ 0 ] = '\0';
+
+    FD_ZERO( &rfds );
+    FD_SET( socket->fd, &rfds );
+
+       if ( select( socket->fd + 1, &rfds, NULL, NULL, &tv ) )
+       {
+               used = read( socket->fd, data, length - 1 );
+               if ( used > 0 )
+                       data[ used ] = '\0';
+               else
+                       used = -1;
+       }
+
+       return used;
+}      
+
+/** Write an arbitrarily formatted block of data to the server.
+*/
+
+int valerie_socket_write_data( valerie_socket socket, const char *data, int length )
+{
+       int used = 0;
+       
+       while ( used >=0 && used < length )
+       {
+               struct timeval tv = { 1, 0 };
+               fd_set rfds;
+               fd_set wfds;
+               fd_set efds;
+       
+               FD_ZERO( &rfds );
+               FD_SET( socket->fd, &rfds );
+               FD_ZERO( &wfds );
+               FD_SET( socket->fd, &wfds );
+               FD_ZERO( &efds );
+               FD_SET( socket->fd, &efds );
+       
+               errno = 0;
+
+               if ( select( socket->fd + 1, &rfds, &wfds, &efds, &tv ) )
+               {
+                       if ( errno != 0 || FD_ISSET( socket->fd, &efds ) || FD_ISSET( socket->fd, &rfds ) )
+                       {
+                               used = -1;
+                       }
+                       else if ( FD_ISSET( socket->fd, &wfds ) )
+                       {
+                               int inc = write( socket->fd, data + used, length - used );
+                               if ( inc > 0 )
+                                       used += inc;
+                               else
+                                       used = -1;
+                       }
+               }
+       }
+
+       return used;
+}
+
+/** Close the socket.
+*/
+
+void valerie_socket_close( valerie_socket socket )
+{
+       if ( socket->fd > 0 && !socket->no_close )
+               close( socket->fd );
+       free( socket->server );
+       free( socket );
+}
diff --git a/src/valerie/valerie_socket.h b/src/valerie/valerie_socket.h
new file mode 100644 (file)
index 0000000..61838de
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * valerie_socket.h -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_SOCKET_H_
+#define _VALERIE_SOCKET_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for socket.
+*/
+
+typedef struct
+{
+       char *server;
+       int port;
+       int fd;
+       int no_close;
+}
+*valerie_socket, valerie_socket_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_socket valerie_socket_init( char *, int );
+extern int valerie_socket_connect( valerie_socket );
+extern valerie_socket valerie_socket_init_fd( int );
+extern int valerie_socket_read_data( valerie_socket, char *, int );
+extern int valerie_socket_write_data( valerie_socket, const char *, int );
+extern void valerie_socket_close( valerie_socket );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_status.c b/src/valerie/valerie_status.c
new file mode 100644 (file)
index 0000000..963a3c9
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * valerie_status.c -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_status.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Parse a unit status string.
+*/
+
+void valerie_status_parse( valerie_status status, char *text )
+{
+       valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+       if ( valerie_tokeniser_parse_new( tokeniser, text, " " ) == 17 )
+       {
+               status->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+               strncpy( status->clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' ), sizeof( status->clip ) );
+               status->position = atol( valerie_tokeniser_get_string( tokeniser, 3 ) );
+               status->speed = atoi( valerie_tokeniser_get_string( tokeniser, 4 ) );
+               status->fps = atof( valerie_tokeniser_get_string( tokeniser, 5 ) );
+               status->in = atol( valerie_tokeniser_get_string( tokeniser, 6 ) );
+               status->out = atol( valerie_tokeniser_get_string( tokeniser, 7 ) );
+               status->length = atol( valerie_tokeniser_get_string( tokeniser, 8 ) );
+
+               strncpy( status->tail_clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 9 ), '\"' ), sizeof( status->tail_clip ) );
+               status->tail_position = atol( valerie_tokeniser_get_string( tokeniser, 10 ) );
+               status->tail_in = atol( valerie_tokeniser_get_string( tokeniser, 11 ) );
+               status->tail_out = atol( valerie_tokeniser_get_string( tokeniser, 12 ) );
+               status->tail_length = atol( valerie_tokeniser_get_string( tokeniser, 13 ) );
+               status->seek_flag = atoi( valerie_tokeniser_get_string( tokeniser, 14 ) );
+               status->generation = atoi( valerie_tokeniser_get_string( tokeniser, 15 ) );
+               status->clip_index = atoi( valerie_tokeniser_get_string( tokeniser, 16 ) );
+
+               if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "unknown" ) )
+                       status->status = unit_unknown;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "undefined" ) )
+                       status->status = unit_undefined;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "offline" ) )
+                       status->status = unit_offline;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "not_loaded" ) )
+                       status->status = unit_not_loaded;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "stopped" ) )
+                       status->status = unit_stopped;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "paused" ) )
+                       status->status = unit_paused;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "playing" ) )
+                       status->status = unit_playing;
+               else if ( !strcmp( valerie_tokeniser_get_string( tokeniser, 1 ), "disconnected" ) )
+                       status->status = unit_disconnected;
+       }
+       else
+       {
+               memset( status, 0, sizeof( valerie_status_t ) );
+               fprintf( stderr, "Status thread changed?\n" );
+       }
+       valerie_tokeniser_close( tokeniser );
+}
+
+/** Serialise a status into a string.
+*/
+
+char *valerie_status_serialise( valerie_status status, char *text, int length )
+{
+       const char *status_string = NULL;
+
+       switch( status->status )
+       {
+               case unit_undefined:
+                       status_string = "undefined";
+                       break;
+
+               case unit_offline:
+                       status_string = "offline";
+                       break;
+
+               case unit_not_loaded:
+                       status_string = "not_loaded";
+                       break;
+
+               case unit_stopped:
+                       status_string = "stopped";
+                       break;
+
+               case unit_playing:
+                       status_string = "playing";
+                       break;
+
+               case unit_unknown:
+                       status_string = "unknown";
+                       break;
+
+               case unit_paused:
+                       status_string = "paused";
+                       break;
+
+               case unit_disconnected:
+                       status_string = "disconnected";
+                       break;
+       }
+
+       snprintf( text, length, "%d %s \"%s\" %d %d %.2f %d %d %d \"%s\" %d %d %d %d %d %d %d\r\n",
+                                                       status->unit,
+                                                       status_string,
+                                                       status->clip,
+                                                       status->position, 
+                                                       status->speed,
+                                                       status->fps,
+                                                       status->in,
+                                                       status->out,
+                                                       status->length,
+                                                       status->tail_clip,
+                                                       status->tail_position, 
+                                                       status->tail_in,
+                                                       status->tail_out,
+                                                       status->tail_length,
+                                                       status->seek_flag,
+                                                       status->generation,
+                                                       status->clip_index );
+
+       return text;
+}
+
+/** Compare two status codes for changes.
+*/
+
+int valerie_status_compare( valerie_status status1, valerie_status status2 )
+{
+       return memcmp( status1, status2, sizeof( valerie_status_t ) );
+}
+
+/** Copy status code info from dest to src.
+*/
+
+valerie_status valerie_status_copy( valerie_status dest, valerie_status src )
+{
+       return memcpy( dest, src, sizeof( valerie_status_t ) );
+}
diff --git a/src/valerie/valerie_status.h b/src/valerie/valerie_status.h
new file mode 100644 (file)
index 0000000..769fbf3
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * valerie_status.h -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_STATUS_H_
+#define _VALERIE_STATUS_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Status codes
+*/
+
+typedef enum
+{
+       unit_unknown = 0,
+       unit_undefined,
+       unit_offline,
+       unit_not_loaded,
+       unit_stopped,
+       unit_playing,
+       unit_paused,
+       unit_disconnected
+}
+unit_status;
+
+/** Status structure.
+*/
+
+typedef struct
+{
+       int unit;
+       unit_status status;
+       char clip[ 2048 ];
+       int32_t position;
+       int speed;
+       double fps;
+       int32_t in;
+       int32_t out;
+       int32_t length;
+       char tail_clip[ 2048 ];
+       int32_t tail_position;
+       int32_t tail_in;
+       int32_t tail_out;
+       int32_t tail_length;
+       int seek_flag;
+       int generation;
+       int clip_index;
+       int dummy;
+}
+*valerie_status, valerie_status_t;
+
+/** DV1394 Status API
+*/
+
+extern void valerie_status_parse( valerie_status, char * );
+extern char *valerie_status_serialise( valerie_status, char *, int );
+extern int valerie_status_compare( valerie_status, valerie_status );
+extern valerie_status valerie_status_copy( valerie_status, valerie_status );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_tokeniser.c b/src/valerie/valerie_tokeniser.c
new file mode 100644 (file)
index 0000000..a5dd91b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * valerie_tokeniser.c -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+valerie_tokeniser valerie_tokeniser_init( )
+{
+       valerie_tokeniser tokeniser = malloc( sizeof( valerie_tokeniser_t ) );
+       if ( tokeniser != NULL )
+               memset( tokeniser, 0, sizeof( valerie_tokeniser_t ) );
+       return tokeniser;
+}
+
+/** Clear the tokeniser.
+*/
+
+static void valerie_tokeniser_clear( valerie_tokeniser tokeniser )
+{
+       int index = 0;
+       for ( index = 0; index < tokeniser->count; index ++ )
+               free( tokeniser->tokens[ index ] );
+       tokeniser->count = 0;
+       free( tokeniser->input );
+       tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int valerie_tokeniser_append( valerie_tokeniser tokeniser, char *token )
+{
+       int error = 0;
+
+       if ( tokeniser->count == tokeniser->size )
+       {
+               tokeniser->size += 20;
+               tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+       }
+
+       if ( tokeniser->tokens != NULL )
+       {
+               tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+       }
+       else
+       {
+               tokeniser->count = 0;
+               error = -1;
+       }
+       return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int valerie_tokeniser_parse_new( valerie_tokeniser tokeniser, char *string, const char *delimiter )
+{
+       int count = 0;
+       int length = strlen( string );
+       int delimiter_size = strlen( delimiter );
+       int index = 0;
+       char *token = strdup( string );
+
+       valerie_tokeniser_clear( tokeniser );
+       tokeniser->input = strdup( string );
+       strcpy( token, "" );
+
+       for ( index = 0; index < length; )
+       {
+               char *start = string + index;
+               char *end = strstr( start, delimiter );
+
+               if ( end == NULL )
+               {
+                       strcat( token, start );
+                       valerie_tokeniser_append( tokeniser, token );
+                       index = length;
+                       count ++;
+               }
+               else if ( start != end )
+               {
+                       strncat( token, start, end - start );
+                       index += end - start;
+                       if ( token[ 0 ] != '\"' || ( token[ 0 ] == '\"' && token[ strlen( token ) - 1 ] == '\"' ) )
+                       {
+                               valerie_tokeniser_append( tokeniser, token );
+                               strcpy( token, "" );
+                               count ++;
+                       }
+                       else while ( strncmp( string + index, delimiter, delimiter_size ) == 0 )
+                       {
+                               strncat( token, delimiter, delimiter_size );
+                               index += delimiter_size;
+                       }
+               }
+               else
+               {
+                       index += strlen( delimiter );
+               }
+       }
+
+       /* Special case - malformed string condition */
+       if ( !strcmp( token, "" ) )
+       {
+               count = 0 - ( count - 1 );
+               valerie_tokeniser_append( tokeniser, token );
+       }
+               
+       free( token );
+       return count;
+}
+
+/** Get the original input.
+*/
+
+char *valerie_tokeniser_get_input( valerie_tokeniser tokeniser )
+{
+       return tokeniser->input;
+}
+
+/** Get the number of tokens.
+*/
+
+int valerie_tokeniser_count( valerie_tokeniser tokeniser )
+{
+       return tokeniser->count;
+}
+
+/** Get a token as a string.
+*/
+
+char *valerie_tokeniser_get_string( valerie_tokeniser tokeniser, int index )
+{
+       if ( index < tokeniser->count )
+               return tokeniser->tokens[ index ];
+       else
+               return NULL;
+}
+
+/** Close the tokeniser.
+*/
+
+void valerie_tokeniser_close( valerie_tokeniser tokeniser )
+{
+       valerie_tokeniser_clear( tokeniser );
+       free( tokeniser->tokens );
+       free( tokeniser );
+}
diff --git a/src/valerie/valerie_tokeniser.h b/src/valerie/valerie_tokeniser.h
new file mode 100644 (file)
index 0000000..3cf8150
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * valerie_tokeniser.h -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_TOKENISER_H_
+#define _VALERIE_TOKENISER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for tokeniser.
+*/
+
+typedef struct
+{
+       char *input;
+       char **tokens;
+       int count;
+       int size;
+}
+*valerie_tokeniser, valerie_tokeniser_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_tokeniser valerie_tokeniser_init( );
+extern int valerie_tokeniser_parse_new( valerie_tokeniser, char *, const char * );
+extern char *valerie_tokeniser_get_input( valerie_tokeniser );
+extern int valerie_tokeniser_count( valerie_tokeniser );
+extern char *valerie_tokeniser_get_string( valerie_tokeniser, int );
+extern void valerie_tokeniser_close( valerie_tokeniser );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/valerie/valerie_util.c b/src/valerie/valerie_util.c
new file mode 100644 (file)
index 0000000..168f6a7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * valerie_util.c -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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
+ */
+
+/* System header files */
+#include <string.h>
+#include <ctype.h>
+
+/* Application header files */
+#include "valerie_util.h"
+
+/** Remove LF or CR/LF terminations from the input string.
+*/
+
+char *valerie_util_chomp( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               if ( length && input[ length - 1 ] == '\n' )
+                       input[ length - 1 ] = '\0';
+               if ( length > 1 && input[ length - 2 ] == '\r' )
+                       input[ length - 2 ] = '\0';
+       }
+       return input;
+}
+
+/** Remove leading and trailing spaces from the input string.
+*/
+
+char *valerie_util_trim( char *input )
+{
+       if ( input != NULL )
+       {
+               int length = strlen( input );
+               int first = 0;
+               while( first < length && isspace( input[ first ] ) )
+                       first ++;
+               memmove( input, input + first, length - first + 1 );
+               length = length - first;
+               while ( length > 0 && isspace( input[ length - 1 ] ) )
+                       input[ -- length ] = '\0';
+       }
+       return input;
+}
+
+/** Strip the specified string of leading and trailing 'value' (ie: ").
+*/
+
+char *valerie_util_strip( char *input, char value )
+{
+       if ( input != NULL )
+       {
+               char *ptr = strrchr( input, value );
+               if ( ptr != NULL )
+                       *ptr = '\0';
+               if ( input[ 0 ] == value )
+                       strcpy( input, input + 1 );
+       }
+       return input;
+}
diff --git a/src/valerie/valerie_util.h b/src/valerie/valerie_util.h
new file mode 100644 (file)
index 0000000..492eba0
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * valerie_util.h -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * 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 _VALERIE_UTIL_H_
+#define _VALERIE_UTIL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *valerie_util_chomp( char * );
+extern char *valerie_util_trim( char * );
+extern char *valerie_util_strip( char *, char );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif