From 661165812e3410fe2f6f49d7af882b36a0efcf82 Mon Sep 17 00:00:00 2001 From: lilo_booter Date: Fri, 19 Dec 2003 13:57:38 +0000 Subject: [PATCH] Initial revision git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@3 d19143bc-622f-0410-bfdd-b5b2a6649095 --- ChangeLog | 0 Makefile | 8 + README | 115 +++ configure | 54 ++ mlt/ChangeLog | 0 mlt/Makefile | 8 + mlt/README | 115 +++ mlt/configure | 54 ++ mlt/src/framework/Makefile | 38 + mlt/src/framework/config.h | 10 + mlt/src/framework/configure | 1 + mlt/src/framework/mlt_consumer.c | 63 ++ mlt/src/framework/mlt_consumer.h | 50 ++ mlt/src/framework/mlt_factory.c | 89 ++ mlt/src/framework/mlt_factory.h | 33 + mlt/src/framework/mlt_filter.c | 163 ++++ mlt/src/framework/mlt_filter.h | 62 ++ mlt/src/framework/mlt_frame.c | 415 +++++++++ mlt/src/framework/mlt_frame.h | 85 ++ mlt/src/framework/mlt_manager.h | 26 + mlt/src/framework/mlt_multitrack.c | 259 ++++++ mlt/src/framework/mlt_multitrack.h | 36 + mlt/src/framework/mlt_playlist.c | 101 +++ mlt/src/framework/mlt_playlist.h | 37 + mlt/src/framework/mlt_producer.c | 291 ++++++ mlt/src/framework/mlt_producer.h | 64 ++ mlt/src/framework/mlt_properties.c | 324 +++++++ mlt/src/framework/mlt_properties.h | 56 ++ mlt/src/framework/mlt_property.c | 220 +++++ mlt/src/framework/mlt_property.h | 82 ++ mlt/src/framework/mlt_repository.c | 42 + mlt/src/framework/mlt_repository.h | 37 + mlt/src/framework/mlt_service.c | 275 ++++++ mlt/src/framework/mlt_service.h | 79 ++ mlt/src/framework/mlt_tractor.c | 155 ++++ mlt/src/framework/mlt_tractor.h | 31 + mlt/src/framework/mlt_transition.c | 232 +++++ mlt/src/framework/mlt_transition.h | 68 ++ mlt/src/framework/mlt_types.h | 43 + mlt/src/miracle/configure | 1 + mlt/src/miracle/miracle.c | 110 +++ mlt/src/miracle/miracle_commands.c | 453 ++++++++++ mlt/src/miracle/miracle_commands.h | 52 ++ mlt/src/miracle/miracle_connection.c | 250 ++++++ mlt/src/miracle/miracle_connection.h | 91 ++ mlt/src/miracle/miracle_local.c | 460 ++++++++++ mlt/src/miracle/miracle_local.h | 41 + mlt/src/miracle/miracle_log.c | 63 ++ mlt/src/miracle/miracle_log.h | 43 + mlt/src/miracle/miracle_server.c | 275 ++++++ mlt/src/miracle/miracle_server.h | 70 ++ mlt/src/miracle/miracle_unit.c | 1100 +++++++++++++++++++++++ mlt/src/miracle/miracle_unit.h | 122 +++ mlt/src/miracle/miracle_unit_commands.c | 612 +++++++++++++ mlt/src/miracle/miracle_unit_commands.h | 57 ++ mlt/src/modules/Makefile | 15 + mlt/src/modules/configure | 26 + mlt/src/modules/dv/Makefile | 29 + mlt/src/modules/dv/configure | 11 + mlt/src/modules/dv/factory.c | 46 + mlt/src/modules/dv/producer_libdv.c | 370 ++++++++ mlt/src/modules/dv/producer_libdv.h | 28 + mlt/src/modules/gtk2/Makefile | 29 + mlt/src/modules/gtk2/configure | 11 + mlt/src/modules/gtk2/factory.c | 46 + mlt/src/modules/gtk2/producer_pixbuf.c | 231 +++++ mlt/src/modules/gtk2/producer_pixbuf.h | 41 + mlt/src/modules/sdl/Makefile | 29 + mlt/src/modules/sdl/configure | 11 + mlt/src/modules/sdl/consumer_sdl.c | 366 ++++++++ mlt/src/modules/sdl/consumer_sdl.h | 28 + mlt/src/modules/sdl/factory.c | 46 + mlt/src/tests/charlie.c | 79 ++ mlt/src/tests/dan.c | 72 ++ mlt/src/tests/test.png | Bin 0 -> 1352 bytes mlt/src/valerie/Makefile | 37 + mlt/src/valerie/configure | 1 + mlt/src/valerie/valerie.c | 875 ++++++++++++++++++ mlt/src/valerie/valerie.h | 256 ++++++ mlt/src/valerie/valerie_notifier.c | 142 +++ mlt/src/valerie/valerie_notifier.h | 61 ++ mlt/src/valerie/valerie_parser.c | 131 +++ mlt/src/valerie/valerie_parser.h | 67 ++ mlt/src/valerie/valerie_remote.c | 260 ++++++ mlt/src/valerie/valerie_remote.h | 41 + mlt/src/valerie/valerie_response.c | 244 +++++ mlt/src/valerie/valerie_response.h | 59 ++ mlt/src/valerie/valerie_socket.c | 175 ++++ mlt/src/valerie/valerie_socket.h | 56 ++ mlt/src/valerie/valerie_status.c | 160 ++++ mlt/src/valerie/valerie_status.h | 83 ++ mlt/src/valerie/valerie_tokeniser.c | 172 ++++ mlt/src/valerie/valerie_tokeniser.h | 55 ++ mlt/src/valerie/valerie_util.c | 77 ++ mlt/src/valerie/valerie_util.h | 37 + src/framework/Makefile | 38 + src/framework/config.h | 10 + src/framework/configure | 1 + src/framework/mlt_consumer.c | 63 ++ src/framework/mlt_consumer.h | 50 ++ src/framework/mlt_factory.c | 89 ++ src/framework/mlt_factory.h | 33 + src/framework/mlt_filter.c | 163 ++++ src/framework/mlt_filter.h | 62 ++ src/framework/mlt_frame.c | 415 +++++++++ src/framework/mlt_frame.h | 85 ++ src/framework/mlt_manager.h | 26 + src/framework/mlt_multitrack.c | 259 ++++++ src/framework/mlt_multitrack.h | 36 + src/framework/mlt_playlist.c | 101 +++ src/framework/mlt_playlist.h | 37 + src/framework/mlt_producer.c | 291 ++++++ src/framework/mlt_producer.h | 64 ++ src/framework/mlt_properties.c | 324 +++++++ src/framework/mlt_properties.h | 56 ++ src/framework/mlt_property.c | 220 +++++ src/framework/mlt_property.h | 82 ++ src/framework/mlt_repository.c | 42 + src/framework/mlt_repository.h | 37 + src/framework/mlt_service.c | 275 ++++++ src/framework/mlt_service.h | 79 ++ src/framework/mlt_tractor.c | 155 ++++ src/framework/mlt_tractor.h | 31 + src/framework/mlt_transition.c | 232 +++++ src/framework/mlt_transition.h | 68 ++ src/framework/mlt_types.h | 43 + src/miracle/configure | 1 + src/miracle/miracle.c | 110 +++ src/miracle/miracle_commands.c | 453 ++++++++++ src/miracle/miracle_commands.h | 52 ++ src/miracle/miracle_connection.c | 250 ++++++ src/miracle/miracle_connection.h | 91 ++ src/miracle/miracle_local.c | 460 ++++++++++ src/miracle/miracle_local.h | 41 + src/miracle/miracle_log.c | 63 ++ src/miracle/miracle_log.h | 43 + src/miracle/miracle_server.c | 275 ++++++ src/miracle/miracle_server.h | 70 ++ src/miracle/miracle_unit.c | 1100 +++++++++++++++++++++++ src/miracle/miracle_unit.h | 122 +++ src/miracle/miracle_unit_commands.c | 612 +++++++++++++ src/miracle/miracle_unit_commands.h | 57 ++ src/modules/Makefile | 15 + src/modules/configure | 26 + src/modules/dv/Makefile | 29 + src/modules/dv/configure | 11 + src/modules/dv/factory.c | 46 + src/modules/dv/producer_libdv.c | 370 ++++++++ src/modules/dv/producer_libdv.h | 28 + src/modules/gtk2/Makefile | 29 + src/modules/gtk2/configure | 11 + src/modules/gtk2/factory.c | 46 + src/modules/gtk2/producer_pixbuf.c | 231 +++++ src/modules/gtk2/producer_pixbuf.h | 41 + src/modules/sdl/Makefile | 29 + src/modules/sdl/configure | 11 + src/modules/sdl/consumer_sdl.c | 366 ++++++++ src/modules/sdl/consumer_sdl.h | 28 + src/modules/sdl/factory.c | 46 + src/tests/charlie.c | 79 ++ src/tests/dan.c | 72 ++ src/tests/test.png | Bin 0 -> 1352 bytes src/valerie/Makefile | 37 + src/valerie/configure | 1 + src/valerie/valerie.c | 875 ++++++++++++++++++ src/valerie/valerie.h | 256 ++++++ src/valerie/valerie_notifier.c | 142 +++ src/valerie/valerie_notifier.h | 61 ++ src/valerie/valerie_parser.c | 131 +++ src/valerie/valerie_parser.h | 67 ++ src/valerie/valerie_remote.c | 260 ++++++ src/valerie/valerie_remote.h | 41 + src/valerie/valerie_response.c | 244 +++++ src/valerie/valerie_response.h | 59 ++ src/valerie/valerie_socket.c | 175 ++++ src/valerie/valerie_socket.h | 56 ++ src/valerie/valerie_status.c | 160 ++++ src/valerie/valerie_status.h | 83 ++ src/valerie/valerie_tokeniser.c | 172 ++++ src/valerie/valerie_tokeniser.h | 55 ++ src/valerie/valerie_util.c | 77 ++ src/valerie/valerie_util.h | 37 + 182 files changed, 23894 insertions(+) create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 README create mode 100755 configure create mode 100644 mlt/ChangeLog create mode 100644 mlt/Makefile create mode 100644 mlt/README create mode 100755 mlt/configure create mode 100644 mlt/src/framework/Makefile create mode 100644 mlt/src/framework/config.h create mode 100755 mlt/src/framework/configure create mode 100644 mlt/src/framework/mlt_consumer.c create mode 100644 mlt/src/framework/mlt_consumer.h create mode 100644 mlt/src/framework/mlt_factory.c create mode 100644 mlt/src/framework/mlt_factory.h create mode 100644 mlt/src/framework/mlt_filter.c create mode 100644 mlt/src/framework/mlt_filter.h create mode 100644 mlt/src/framework/mlt_frame.c create mode 100644 mlt/src/framework/mlt_frame.h create mode 100644 mlt/src/framework/mlt_manager.h create mode 100644 mlt/src/framework/mlt_multitrack.c create mode 100644 mlt/src/framework/mlt_multitrack.h create mode 100644 mlt/src/framework/mlt_playlist.c create mode 100644 mlt/src/framework/mlt_playlist.h create mode 100644 mlt/src/framework/mlt_producer.c create mode 100644 mlt/src/framework/mlt_producer.h create mode 100644 mlt/src/framework/mlt_properties.c create mode 100644 mlt/src/framework/mlt_properties.h create mode 100644 mlt/src/framework/mlt_property.c create mode 100644 mlt/src/framework/mlt_property.h create mode 100644 mlt/src/framework/mlt_repository.c create mode 100644 mlt/src/framework/mlt_repository.h create mode 100644 mlt/src/framework/mlt_service.c create mode 100644 mlt/src/framework/mlt_service.h create mode 100644 mlt/src/framework/mlt_tractor.c create mode 100644 mlt/src/framework/mlt_tractor.h create mode 100644 mlt/src/framework/mlt_transition.c create mode 100644 mlt/src/framework/mlt_transition.h create mode 100644 mlt/src/framework/mlt_types.h create mode 100755 mlt/src/miracle/configure create mode 100644 mlt/src/miracle/miracle.c create mode 100644 mlt/src/miracle/miracle_commands.c create mode 100644 mlt/src/miracle/miracle_commands.h create mode 100644 mlt/src/miracle/miracle_connection.c create mode 100644 mlt/src/miracle/miracle_connection.h create mode 100644 mlt/src/miracle/miracle_local.c create mode 100644 mlt/src/miracle/miracle_local.h create mode 100644 mlt/src/miracle/miracle_log.c create mode 100644 mlt/src/miracle/miracle_log.h create mode 100644 mlt/src/miracle/miracle_server.c create mode 100644 mlt/src/miracle/miracle_server.h create mode 100644 mlt/src/miracle/miracle_unit.c create mode 100644 mlt/src/miracle/miracle_unit.h create mode 100644 mlt/src/miracle/miracle_unit_commands.c create mode 100644 mlt/src/miracle/miracle_unit_commands.h create mode 100644 mlt/src/modules/Makefile create mode 100755 mlt/src/modules/configure create mode 100644 mlt/src/modules/dv/Makefile create mode 100755 mlt/src/modules/dv/configure create mode 100644 mlt/src/modules/dv/factory.c create mode 100644 mlt/src/modules/dv/producer_libdv.c create mode 100644 mlt/src/modules/dv/producer_libdv.h create mode 100644 mlt/src/modules/gtk2/Makefile create mode 100755 mlt/src/modules/gtk2/configure create mode 100644 mlt/src/modules/gtk2/factory.c create mode 100644 mlt/src/modules/gtk2/producer_pixbuf.c create mode 100644 mlt/src/modules/gtk2/producer_pixbuf.h create mode 100644 mlt/src/modules/sdl/Makefile create mode 100755 mlt/src/modules/sdl/configure create mode 100644 mlt/src/modules/sdl/consumer_sdl.c create mode 100644 mlt/src/modules/sdl/consumer_sdl.h create mode 100644 mlt/src/modules/sdl/factory.c create mode 100644 mlt/src/tests/charlie.c create mode 100644 mlt/src/tests/dan.c create mode 100644 mlt/src/tests/test.png create mode 100644 mlt/src/valerie/Makefile create mode 100755 mlt/src/valerie/configure create mode 100644 mlt/src/valerie/valerie.c create mode 100644 mlt/src/valerie/valerie.h create mode 100644 mlt/src/valerie/valerie_notifier.c create mode 100644 mlt/src/valerie/valerie_notifier.h create mode 100644 mlt/src/valerie/valerie_parser.c create mode 100644 mlt/src/valerie/valerie_parser.h create mode 100644 mlt/src/valerie/valerie_remote.c create mode 100644 mlt/src/valerie/valerie_remote.h create mode 100644 mlt/src/valerie/valerie_response.c create mode 100644 mlt/src/valerie/valerie_response.h create mode 100644 mlt/src/valerie/valerie_socket.c create mode 100644 mlt/src/valerie/valerie_socket.h create mode 100644 mlt/src/valerie/valerie_status.c create mode 100644 mlt/src/valerie/valerie_status.h create mode 100644 mlt/src/valerie/valerie_tokeniser.c create mode 100644 mlt/src/valerie/valerie_tokeniser.h create mode 100644 mlt/src/valerie/valerie_util.c create mode 100644 mlt/src/valerie/valerie_util.h create mode 100644 src/framework/Makefile create mode 100644 src/framework/config.h create mode 100755 src/framework/configure create mode 100644 src/framework/mlt_consumer.c create mode 100644 src/framework/mlt_consumer.h create mode 100644 src/framework/mlt_factory.c create mode 100644 src/framework/mlt_factory.h create mode 100644 src/framework/mlt_filter.c create mode 100644 src/framework/mlt_filter.h create mode 100644 src/framework/mlt_frame.c create mode 100644 src/framework/mlt_frame.h create mode 100644 src/framework/mlt_manager.h create mode 100644 src/framework/mlt_multitrack.c create mode 100644 src/framework/mlt_multitrack.h create mode 100644 src/framework/mlt_playlist.c create mode 100644 src/framework/mlt_playlist.h create mode 100644 src/framework/mlt_producer.c create mode 100644 src/framework/mlt_producer.h create mode 100644 src/framework/mlt_properties.c create mode 100644 src/framework/mlt_properties.h create mode 100644 src/framework/mlt_property.c create mode 100644 src/framework/mlt_property.h create mode 100644 src/framework/mlt_repository.c create mode 100644 src/framework/mlt_repository.h create mode 100644 src/framework/mlt_service.c create mode 100644 src/framework/mlt_service.h create mode 100644 src/framework/mlt_tractor.c create mode 100644 src/framework/mlt_tractor.h create mode 100644 src/framework/mlt_transition.c create mode 100644 src/framework/mlt_transition.h create mode 100644 src/framework/mlt_types.h create mode 100755 src/miracle/configure create mode 100644 src/miracle/miracle.c create mode 100644 src/miracle/miracle_commands.c create mode 100644 src/miracle/miracle_commands.h create mode 100644 src/miracle/miracle_connection.c create mode 100644 src/miracle/miracle_connection.h create mode 100644 src/miracle/miracle_local.c create mode 100644 src/miracle/miracle_local.h create mode 100644 src/miracle/miracle_log.c create mode 100644 src/miracle/miracle_log.h create mode 100644 src/miracle/miracle_server.c create mode 100644 src/miracle/miracle_server.h create mode 100644 src/miracle/miracle_unit.c create mode 100644 src/miracle/miracle_unit.h create mode 100644 src/miracle/miracle_unit_commands.c create mode 100644 src/miracle/miracle_unit_commands.h create mode 100644 src/modules/Makefile create mode 100755 src/modules/configure create mode 100644 src/modules/dv/Makefile create mode 100755 src/modules/dv/configure create mode 100644 src/modules/dv/factory.c create mode 100644 src/modules/dv/producer_libdv.c create mode 100644 src/modules/dv/producer_libdv.h create mode 100644 src/modules/gtk2/Makefile create mode 100755 src/modules/gtk2/configure create mode 100644 src/modules/gtk2/factory.c create mode 100644 src/modules/gtk2/producer_pixbuf.c create mode 100644 src/modules/gtk2/producer_pixbuf.h create mode 100644 src/modules/sdl/Makefile create mode 100755 src/modules/sdl/configure create mode 100644 src/modules/sdl/consumer_sdl.c create mode 100644 src/modules/sdl/consumer_sdl.h create mode 100644 src/modules/sdl/factory.c create mode 100644 src/tests/charlie.c create mode 100644 src/tests/dan.c create mode 100644 src/tests/test.png create mode 100644 src/valerie/Makefile create mode 100755 src/valerie/configure create mode 100644 src/valerie/valerie.c create mode 100644 src/valerie/valerie.h create mode 100644 src/valerie/valerie_notifier.c create mode 100644 src/valerie/valerie_notifier.h create mode 100644 src/valerie/valerie_parser.c create mode 100644 src/valerie/valerie_parser.h create mode 100644 src/valerie/valerie_remote.c create mode 100644 src/valerie/valerie_remote.h create mode 100644 src/valerie/valerie_response.c create mode 100644 src/valerie/valerie_response.h create mode 100644 src/valerie/valerie_socket.c create mode 100644 src/valerie/valerie_socket.h create mode 100644 src/valerie/valerie_status.c create mode 100644 src/valerie/valerie_status.h create mode 100644 src/valerie/valerie_tokeniser.c create mode 100644 src/valerie/valerie_tokeniser.h create mode 100644 src/valerie/valerie_util.c create mode 100644 src/valerie/valerie_util.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a2754671 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +SUBDIRS = src/framework src/valerie src/modules # src/miracle src/humperdink + +all clean dist-clean depend install: + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + $(MAKE) -C $$subdir $@; \ + done + diff --git a/README b/README new file mode 100644 index 00000000..e72975b0 --- /dev/null +++ b/README @@ -0,0 +1,115 @@ +README +------ + + This document provides a description of the VPS project organisation. + + It provides *CRITICAL* architecture information, so please read carefully + if you plan to extend or use the VPS code base. + +Directories +----------- + + The directory heirarchy is defined as follows: + + + docs - Location of all text and source format + documentation + + src - All project source is provided here + + client - Client API to access the server + + framework - The media framework - this code is 100% posix + and as such contain no implementations + requiring additional libraries + + modules - All components are defined here with a + subdirectory for each dependency + + bluefish - Bluefish dependent modules and test code + + ffmpeg - ffmpeg dependent modules and test code + + mainconcept - mainconcept dependent modules and test code + + SDL - SDL dependent modules and test code + + server - The server implementation + + Additional subdirectories may be nested below those shown and should be + documented in their parent or here. + +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. + + Top level usage is: + + ./configure --help - report all configure options + ./configure --prefix=[dir] - target install directory (default: /usr/local) + ./configure --disable-[mod] - do not compile specified module(s) + ./configure --[other] - pass through to children + +Compilation +----------- + + Makefiles are generated during configuration and these are based on + a per directory template which must be provided by the developer. + +Installation +------------ + + The install is triggered by running make install or make install-strip + from the top level directory. + + The framework produces a single shared object which is installed in + $prefix/lib/ and public header files which are installed in + $prefix/include/mlt/framework. + + The client produces a single shared object which is installed in + $prefix/lib/ and public header which are installed in + $prefix/include/mlt/client. + + The server produces a single exectuable which is installed in + $prefix/bin/. This is linked against the framework shared object and + posix libs but not against any of the modules. + + The modules produce a shared object per module and a text file containing + a list of modules provided by this build. These are installed in + $prefix/share/mlt/. It is at the discretion of the module to install + additional support files. + + To allow the development of external components, mlt-client-config and + mlt-framework-config 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, 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 + + All external modules have {prefix}/include/mlt on the include path. All + headers should also be included as: + + #include + + This allows migration of source between external and internal + modules. The configuration and Makefile template requirements will require + attention though. + +Summary +------- + + 1. The server will interact with public interfaces from the framework only; + 2. The modules must expose public framework interfaces only; + 3. All modules are dynamically loaded at runtime. + diff --git a/configure b/configure new file mode 100755 index 00000000..40b31a49 --- /dev/null +++ b/configure @@ -0,0 +1,54 @@ +#!/bin/bash + +function show_help +{ + cat << EOF +Funky non-autotool config script for MLT. + + Options are: + + --help - this information + --prefix=directory - install prefix for path (default: $prefix) +EOF + + for i in src/modules/* + do + [ -d $i ] && echo " --disable-`basename $i`" + done + + echo +} + +# Debug mode +set +x + +# Define build directory for scripts called +export build_dir=`dirname $0` +export prefix=/usr/local +export help=0 + +# Iterate through arguments +for i in $* +do + case $i in + --help ) help=1 ;; + --prefix=* ) prefix="${i#--prefix=}" ;; + esac +done + +# Show help if requested +[ $help = 1 ] && show_help + +# Iterate through each of the components +for i in framework modules valerie miracle humperdink +do + if [ -x src/$i/configure ] + then + echo "Configuring `basename $i`:" + pushd src/$i > /dev/null + ./configure $@ + [ $? != 0 ] && exit 1 + popd > /dev/null + fi +done + diff --git a/mlt/ChangeLog b/mlt/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/mlt/Makefile b/mlt/Makefile new file mode 100644 index 00000000..a2754671 --- /dev/null +++ b/mlt/Makefile @@ -0,0 +1,8 @@ +SUBDIRS = src/framework src/valerie src/modules # src/miracle src/humperdink + +all clean dist-clean depend install: + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + $(MAKE) -C $$subdir $@; \ + done + diff --git a/mlt/README b/mlt/README new file mode 100644 index 00000000..e72975b0 --- /dev/null +++ b/mlt/README @@ -0,0 +1,115 @@ +README +------ + + This document provides a description of the VPS project organisation. + + It provides *CRITICAL* architecture information, so please read carefully + if you plan to extend or use the VPS code base. + +Directories +----------- + + The directory heirarchy is defined as follows: + + + docs - Location of all text and source format + documentation + + src - All project source is provided here + + client - Client API to access the server + + framework - The media framework - this code is 100% posix + and as such contain no implementations + requiring additional libraries + + modules - All components are defined here with a + subdirectory for each dependency + + bluefish - Bluefish dependent modules and test code + + ffmpeg - ffmpeg dependent modules and test code + + mainconcept - mainconcept dependent modules and test code + + SDL - SDL dependent modules and test code + + server - The server implementation + + Additional subdirectories may be nested below those shown and should be + documented in their parent or here. + +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. + + Top level usage is: + + ./configure --help - report all configure options + ./configure --prefix=[dir] - target install directory (default: /usr/local) + ./configure --disable-[mod] - do not compile specified module(s) + ./configure --[other] - pass through to children + +Compilation +----------- + + Makefiles are generated during configuration and these are based on + a per directory template which must be provided by the developer. + +Installation +------------ + + The install is triggered by running make install or make install-strip + from the top level directory. + + The framework produces a single shared object which is installed in + $prefix/lib/ and public header files which are installed in + $prefix/include/mlt/framework. + + The client produces a single shared object which is installed in + $prefix/lib/ and public header which are installed in + $prefix/include/mlt/client. + + The server produces a single exectuable which is installed in + $prefix/bin/. This is linked against the framework shared object and + posix libs but not against any of the modules. + + The modules produce a shared object per module and a text file containing + a list of modules provided by this build. These are installed in + $prefix/share/mlt/. It is at the discretion of the module to install + additional support files. + + To allow the development of external components, mlt-client-config and + mlt-framework-config 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, 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 + + All external modules have {prefix}/include/mlt on the include path. All + headers should also be included as: + + #include + + This allows migration of source between external and internal + modules. The configuration and Makefile template requirements will require + attention though. + +Summary +------- + + 1. The server will interact with public interfaces from the framework only; + 2. The modules must expose public framework interfaces only; + 3. All modules are dynamically loaded at runtime. + diff --git a/mlt/configure b/mlt/configure new file mode 100755 index 00000000..40b31a49 --- /dev/null +++ b/mlt/configure @@ -0,0 +1,54 @@ +#!/bin/bash + +function show_help +{ + cat << EOF +Funky non-autotool config script for MLT. + + Options are: + + --help - this information + --prefix=directory - install prefix for path (default: $prefix) +EOF + + for i in src/modules/* + do + [ -d $i ] && echo " --disable-`basename $i`" + done + + echo +} + +# Debug mode +set +x + +# Define build directory for scripts called +export build_dir=`dirname $0` +export prefix=/usr/local +export help=0 + +# Iterate through arguments +for i in $* +do + case $i in + --help ) help=1 ;; + --prefix=* ) prefix="${i#--prefix=}" ;; + esac +done + +# Show help if requested +[ $help = 1 ] && show_help + +# Iterate through each of the components +for i in framework modules valerie miracle humperdink +do + if [ -x src/$i/configure ] + then + echo "Configuring `basename $i`:" + pushd src/$i > /dev/null + ./configure $@ + [ $? != 0 ] && exit 1 + popd > /dev/null + fi +done + diff --git a/mlt/src/framework/Makefile b/mlt/src/framework/Makefile new file mode 100644 index 00000000..e2d60ee3 --- /dev/null +++ b/mlt/src/framework/Makefile @@ -0,0 +1,38 @@ + +FRAMEWORK_OBJS = mlt_frame.o \ + mlt_property.o \ + mlt_properties.o \ + mlt_service.o \ + mlt_producer.o \ + mlt_multitrack.o \ + mlt_consumer.o \ + mlt_filter.o \ + mlt_transition.o \ + mlt_tractor.o \ + mlt_factory.o \ + mlt_repository.o + +OBJS = $(FRAMEWORK_OBJS) + +SRCS := $(OBJS:.o=.c) + +CFLAGS=-g -Wall -D_FILE_OFFSET_BITS=64 -pthread + +all: libmlt.a + +libmlt.a: $(OBJS) + $(AR) rvu $@ $(OBJS) + ranlib $@ + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(FRAMEWORK_OBJS) libmlt.a + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/mlt/src/framework/config.h b/mlt/src/framework/config.h new file mode 100644 index 00000000..fec9861d --- /dev/null +++ b/mlt/src/framework/config.h @@ -0,0 +1,10 @@ +/** GENERATED FILE - DON'T EDIT */ + +#ifndef _MLT_CONFIG_H_ +#define _MLT_CONFIG_H_ + +#define PREFIX "/usr/local" +#define PREFIX_DATA PREFIX "/share" + +#endif + diff --git a/mlt/src/framework/configure b/mlt/src/framework/configure new file mode 100755 index 00000000..a9bf588e --- /dev/null +++ b/mlt/src/framework/configure @@ -0,0 +1 @@ +#!/bin/bash diff --git a/mlt/src/framework/mlt_consumer.c b/mlt/src/framework/mlt_consumer.c new file mode 100644 index 00000000..635ad651 --- /dev/null +++ b/mlt/src/framework/mlt_consumer.c @@ -0,0 +1,63 @@ +/* + * mlt_consumer.c -- abstraction for all consumer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_consumer.h" +#include +#include +#include + +/** Public final methods +*/ + +int mlt_consumer_init( mlt_consumer this, void *child ) +{ + memset( this, 0, sizeof( struct mlt_consumer_s ) ); + this->child = child; + return mlt_service_init( &this->parent, this ); +} + +/** Get the parent service object. +*/ + +mlt_service mlt_consumer_service( mlt_consumer this ) +{ + return &this->parent; +} + +/** Connect the consumer to the producer. +*/ + +int mlt_consumer_connect( mlt_consumer this, mlt_service producer ) +{ + return mlt_service_connect_producer( &this->parent, producer, 0 ); +} + +/** Close the consumer. +*/ + +void mlt_consumer_close( mlt_consumer this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} + diff --git a/mlt/src/framework/mlt_consumer.h b/mlt/src/framework/mlt_consumer.h new file mode 100644 index 00000000..cbe90781 --- /dev/null +++ b/mlt/src/framework/mlt_consumer.h @@ -0,0 +1,50 @@ +/* + * mlt_consumer.h -- abstraction for all consumer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_CONSUMER_H_ +#define _MLT_CONSUMER_H_ + +#include "mlt_service.h" + +/** The interface definition for all consumers. +*/ + +struct mlt_consumer_s +{ + // We're implementing service here + struct mlt_service_s parent; + + // public virtual + void ( *close )( mlt_consumer ); + + // Private data + void *private; + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_consumer_init( mlt_consumer this, void *child ); +extern mlt_service mlt_consumer_service( mlt_consumer this ); +extern int mlt_consumer_connect( mlt_consumer this, mlt_service producer ); +extern void mlt_consumer_close( mlt_consumer ); + +#endif diff --git a/mlt/src/framework/mlt_factory.c b/mlt/src/framework/mlt_factory.c new file mode 100644 index 00000000..3b40464d --- /dev/null +++ b/mlt/src/framework/mlt_factory.c @@ -0,0 +1,89 @@ +/* + * mlt_factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_factory.h" +#include "mlt_repository.h" + +#include + +/** Singleton repositories +*/ + +static mlt_repository producers = NULL; +static mlt_repository filters = NULL; +static mlt_repository transitions = NULL; +static mlt_repository consumers = NULL; + +/** Construct the factories. +*/ + +int mlt_factory_init( ) +{ + producers = mlt_repository_init( PREFIX_DATA "/producers.dat", "mlt_create_producer" ); + filters = mlt_repository_init( PREFIX_DATA "/filters.dat", "mlt_create_filter" ); + transitions = mlt_repository_init( PREFIX_DATA "/transitions.dat", "mlt_create_transition" ); + consumers = mlt_repository_init( PREFIX_DATA "/consumers.dat", "mlt_create_consumer" ); + return 0; +} + +/** Fetch a producer from the repository. +*/ + +mlt_producer mlt_factory_producer( char *service, void *input ) +{ + return ( mlt_producer )mlt_repository_fetch( producers, service, input ); +} + +/** Fetch a filter from the repository. +*/ + +mlt_filter mlt_factory_filter( char *service, void *input ) +{ + return ( mlt_filter )mlt_repository_fetch( filters, service, input ); +} + +/** Fetch a transition from the repository. +*/ + +mlt_transition mlt_transition_filter( char *service, void *input ) +{ + return ( mlt_transition )mlt_repository_fetch( transitions, service, input ); +} + +/** Fetch a consumer from the repository +*/ + +mlt_consumer mlt_factory_consumer( char *service, void *input ) +{ + return ( mlt_consumer )mlt_repository_fetch( consumers, service, input ); +} + +/** Close the factory. +*/ + +void mlt_factory_close( ) +{ + mlt_repository_close( producers ); + mlt_repository_close( filters ); + mlt_repository_close( transitions ); + mlt_repository_close( consumers ); +} + diff --git a/mlt/src/framework/mlt_factory.h b/mlt/src/framework/mlt_factory.h new file mode 100644 index 00000000..4cece1da --- /dev/null +++ b/mlt/src/framework/mlt_factory.h @@ -0,0 +1,33 @@ +/* + * mlt_factory.h -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FACTORY_H +#define _MLT_FACTORY_H + +#include "mlt_types.h" + +extern int mlt_factory_init( ); +extern mlt_producer mlt_factory_producer( char *name, void *input ); +extern mlt_filter mlt_factory_filter( char *name, void *input ); +extern mlt_transition mlt_factory_transition( char *name, void *input ); +extern mlt_consumer mlt_factory_consumer( char *name, void *input ); +extern void mlt_factory_close( ); + +#endif diff --git a/mlt/src/framework/mlt_filter.c b/mlt/src/framework/mlt_filter.c new file mode 100644 index 00000000..22fefe22 --- /dev/null +++ b/mlt/src/framework/mlt_filter.c @@ -0,0 +1,163 @@ +/* + * mlt_filter.c -- abstraction for all filter services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_filter.h" +#include "mlt_frame.h" + +#include +#include +#include + +static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor method. +*/ + +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 ) + { + service->get_frame = filter_get_frame; + return 0; + } + return 1; +} + +/** Get the service associated to this filter +*/ + +mlt_service mlt_filter_service( mlt_filter this ) +{ + return &this->parent; +} + +/** 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. +*/ + +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 ) + { + this->producer = producer; + this->track = index; + this->in = 0; + this->out = 0; + } + + return ret; +} + +/** Tune the in/out points. +*/ + +void mlt_filter_set_in_and_out( mlt_filter this, mlt_timecode in, mlt_timecode out ) +{ + this->in = in; + this->out = out; +} + +/** Return the track that this filter is operating on. +*/ + +int mlt_filter_get_track( mlt_filter this ) +{ + return this->track; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_filter_get_in( mlt_filter this ) +{ + return this->in; +} + +/** Get the out point. +*/ + +mlt_timecode mlt_filter_get_out( mlt_filter this ) +{ + return this->out; +} + +/** Process the frame. +*/ + +static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +{ + if ( this->process == NULL ) + return frame; + else + return this->process( this, frame ); +} + +/** Get a frame from this filter. +*/ + +static int filter_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + mlt_filter this = service->child; + + // If the frame request is for this filters track, we need to process it + if ( index == this->track ) + { + int ret = mlt_service_get_frame( this->producer, frame, index ); + if ( ret == 0 ) + { + if ( !mlt_frame_is_test_card( *frame ) ) + { + mlt_timecode timecode = mlt_frame_get_timecode( *frame ); + if ( timecode >= this->in && ( this->out == 0 || timecode < this->out ) ) + *frame = filter_process( this, *frame ); + } + return 0; + } + else + { + *frame = mlt_frame_init( ); + return 0; + } + } + else + { + return mlt_service_get_frame( this->producer, frame, index ); + } +} + +/** Close the filter. +*/ + +void mlt_filter_close( mlt_filter this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} + diff --git a/mlt/src/framework/mlt_filter.h b/mlt/src/framework/mlt_filter.h new file mode 100644 index 00000000..99212a92 --- /dev/null +++ b/mlt/src/framework/mlt_filter.h @@ -0,0 +1,62 @@ +/* + * mlt_filter.h -- abstraction for all filter services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FILTER_H_ +#define _MLT_FILTER_H_ + +#include "mlt_service.h" + +/** The interface definition for all filters. +*/ + +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 ); + + // track and in/out points + mlt_service producer; + int track; + mlt_timecode in; + mlt_timecode out; + + // Protected + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_filter_init( mlt_filter this, void *child ); +extern mlt_service mlt_filter_service( mlt_filter this ); +extern int mlt_filter_connect( mlt_filter this, mlt_service producer, int index ); +extern void mlt_filter_set_in_and_out( mlt_filter this, mlt_timecode in, mlt_timecode out ); +extern int mlt_filter_get_track( mlt_filter this ); +extern mlt_timecode mlt_filter_get_in( mlt_filter this ); +extern mlt_timecode mlt_filter_get_out( mlt_filter this ); +extern void mlt_filter_close( mlt_filter ); + +#endif diff --git a/mlt/src/framework/mlt_frame.c b/mlt/src/framework/mlt_frame.c new file mode 100644 index 00000000..9c9eac14 --- /dev/null +++ b/mlt/src/framework/mlt_frame.c @@ -0,0 +1,415 @@ +/* + * mlt_frame.c -- interface for all frame classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_frame.h" +#include +#include +#include + +typedef struct +{ + mlt_image_format vfmt; + int width; + int height; + uint8_t *image; + uint8_t *alpha; + mlt_audio_format afmt; + int16_t *audio; +} +frame_test; + +static frame_test test_card = { mlt_image_none, 0, 0, NULL, NULL, mlt_audio_none, NULL }; + +/** Constructor for a frame. +*/ + +mlt_frame mlt_frame_init( ) +{ + // Allocate a frame + mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 ); + + if ( this != NULL ) + { + // Initialise the properties + mlt_properties properties = &this->parent; + mlt_properties_init( properties, this ); + + // Set default properties on the frame + mlt_properties_set_timecode( properties, "timecode", 0.0 ); + mlt_properties_set_data( properties, "image", NULL, 0, NULL, NULL ); + mlt_properties_set_int( properties, "width", 720 ); + mlt_properties_set_int( properties, "height", 576 ); + mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 ); + mlt_properties_set_data( properties, "audio", NULL, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL ); + } + return this; +} + +/** Fetch the frames properties. +*/ + +mlt_properties mlt_frame_properties( mlt_frame this ) +{ + return &this->parent; +} + +/** Check if we have a way to derive something other than a test card. +*/ + +int mlt_frame_is_test_card( mlt_frame this ) +{ + return this->stack_get_image_size == 0; +} + +/** Get the aspect ratio of the frame. +*/ + +double mlt_frame_get_aspect_ratio( mlt_frame this ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_get_double( properties, "aspect_ratio" ); +} + +/** Set the aspect ratio of the frame. +*/ + +int mlt_frame_set_aspect_ratio( mlt_frame this, double value ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_set_double( properties, "aspect_ratio", value ); +} + +/** Get the timecode of this frame. +*/ + +mlt_timecode mlt_frame_get_timecode( mlt_frame this ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_get_timecode( properties, "timecode" ); +} + +/** Set the timecode of this frame. +*/ + +int mlt_frame_set_timecode( mlt_frame this, mlt_timecode value ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_set_timecode( properties, "timecode", value ); +} + +/** Stack a get_image callback. +*/ + +int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ) +{ + int ret = this->stack_get_image_size >= 10; + if ( ret == 0 ) + this->stack_get_image[ this->stack_get_image_size ++ ] = get_image; + return ret; +} + +/** Pop a get_image callback. +*/ + +mlt_get_image mlt_frame_pop_get_image( mlt_frame this ) +{ + mlt_get_image result = NULL; + if ( this->stack_get_image_size > 0 ) + result = this->stack_get_image[ -- this->stack_get_image_size ]; + return result; +} + +/** Push a frame. +*/ + +int mlt_frame_push_frame( mlt_frame this, mlt_frame that ) +{ + int ret = this->stack_frame_size >= 10; + if ( ret == 0 ) + this->stack_frame[ this->stack_frame_size ++ ] = that; + return ret; +} + +/** Pop a frame. +*/ + +mlt_frame mlt_frame_pop_frame( mlt_frame this ) +{ + mlt_frame result = NULL; + if ( this->stack_frame_size > 0 ) + result = this->stack_frame[ -- this->stack_frame_size ]; + return result; +} + +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 ); + + if ( get_image != NULL ) + { + return get_image( this, buffer, format, width, height, writable ); + } + else if ( mlt_properties_get_data( properties, "image", NULL ) != NULL ) + { + *format = mlt_image_yuv422; + *buffer = mlt_properties_get_data( properties, "image", NULL ); + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + } + else + { + if ( test_card.vfmt != *format ) + { + uint8_t *p; + uint8_t *q; + + test_card.vfmt = *format; + test_card.width = 720; + test_card.height = 576; + + switch( *format ) + { + case mlt_image_none: + break; + case mlt_image_rgb24: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 3 ); + memset( test_card.image, 255, test_card.width * test_card.height * 3 ); + break; + case mlt_image_rgb24a: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 4 ); + memset( test_card.image, 255, test_card.width * test_card.height * 4 ); + break; + case mlt_image_yuv422: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 2 ); + p = test_card.image; + q = test_card.image + test_card.width * test_card.height * 2; + while ( p != q ) + { + *p ++ = 255; + *p ++ = 128; + } + break; + case mlt_image_yuv420p: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 3 / 2 ); + memset( test_card.image, 255, test_card.width * test_card.height * 3 / 2 ); + break; + } + } + + *width = test_card.width; + *height = test_card.height; + *buffer = test_card.image; + } + + return 0; +} + +uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ) +{ + if ( this->get_alpha_mask != NULL ) + return this->get_alpha_mask( this ); + return test_card.alpha; +} + +int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + if ( this->get_audio != NULL ) + { + return this->get_audio( this, buffer, format, frequency, channels, samples ); + } + else + { + if ( test_card.afmt != *format ) + { + test_card.afmt = *format; + test_card.audio = realloc( test_card.audio, 1920 * 2 * sizeof( int16_t ) ); + memset( test_card.audio, 0, 1920 * 2 * sizeof( int16_t ) ); + } + + *buffer = test_card.audio; + *frequency = 48000; + *channels = 2; + *samples = 1920; + } + return 0; +} + +void mlt_frame_close( mlt_frame this ) +{ + mlt_frame frame = mlt_frame_pop_frame( this ); + + while ( frame != NULL ) + { + mlt_frame_close( frame); + frame = mlt_frame_pop_frame( this ); + } + + mlt_properties_close( &this->parent ); + + free( this ); +} + +/***** convenience functions *****/ +#define RGB2YUV(r, g, b, y, u, v)\ + y = (306*r + 601*g + 117*b) >> 10;\ + u = ((-172*r - 340*g + 512*b) >> 10) + 128;\ + v = ((512*r - 429*g - 83*b) >> 10) + 128;\ + y = y < 0 ? 0 : y;\ + u = u < 0 ? 0 : u;\ + v = v < 0 ? 0 : v;\ + y = y > 255 ? 255 : y;\ + u = u > 255 ? 255 : u;\ + v = v > 255 ? 255 : v + +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; + + 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; + } + } + 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; + } + } + return ret; +} + +int mlt_frame_composite_yuv( mlt_frame this, mlt_frame that, int x, int y, float weight ) +{ + int ret = 0; + int x_start = 0; + int width_src, height_src; + int width_dest, height_dest; + mlt_image_format format_src, format_dest; + uint8_t *p_src, *p_dest; + int x_end; + int i, j; + int stride_src; + int stride_dest; + + format_src = mlt_image_yuv422; + format_dest = mlt_image_yuv422; + + mlt_frame_get_image( this, &p_dest, &format_dest, &width_dest, &height_dest, 1 /* writable */ ); + mlt_frame_get_image( that, &p_src, &format_src, &width_src, &height_src, 0 /* writable */ ); + + x_end = width_src; + + stride_src = width_src * 2; + stride_dest = width_dest * 2; + uint8_t *lower = p_dest; + uint8_t *upper = p_dest + height_dest * stride_dest; + + p_dest += ( y * stride_dest ) + ( x * 2 ); + + if ( x < 0 ) + { + x_start = -x; + x_end += x_start; + } + + uint8_t *z = mlt_frame_get_alpha_mask( that ); + + for ( i = 0; i < height_src; i++ ) + { + uint8_t *p = p_src; + uint8_t *q = p_dest; + uint8_t *o = p_dest; + + for ( j = 0; j < width_src; j ++ ) + { + if ( q >= lower && q < upper && j >= x_start && j < x_end ) + { + uint8_t y = *p ++; + uint8_t uv = *p ++; + uint8_t a = ( z == NULL ) ? 255 : *z ++; + float value = ( weight * ( float ) a / 255.0 ); + *o ++ = (uint8_t)( y * value + *q++ * ( 1 - value ) ); + *o ++ = (uint8_t)( uv * value + *q++ * ( 1 - value ) ); + } + else + { + p += 2; + o += 2; + q += 2; + if ( z != NULL ) + z += 1; + } + } + + p_src += stride_src; + p_dest += stride_dest; + } + + return ret; +} + diff --git a/mlt/src/framework/mlt_frame.h b/mlt/src/framework/mlt_frame.h new file mode 100644 index 00000000..fd86f232 --- /dev/null +++ b/mlt/src/framework/mlt_frame.h @@ -0,0 +1,85 @@ +/* + * mlt_frame.h -- interface for all frame classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FRAME_H_ +#define _MLT_FRAME_H_ + +#include "mlt_properties.h" + +typedef enum +{ + mlt_image_none = 0, + mlt_image_rgb24, + mlt_image_rgb24a, + mlt_image_yuv422, + mlt_image_yuv420p +} +mlt_image_format; + +typedef enum +{ + mlt_audio_none = 0, + mlt_audio_pcm +} +mlt_audio_format; + +typedef int ( *mlt_get_image )( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); + +struct mlt_frame_s +{ + // We're extending properties here + struct mlt_properties_s parent; + + // Virtual methods + int ( *get_audio )( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); + uint8_t * ( *get_alpha_mask )( mlt_frame this ); + + // Private properties + mlt_get_image stack_get_image[ 10 ]; + int stack_get_image_size; + mlt_frame stack_frame[ 10 ]; + int stack_frame_size; +}; + +extern mlt_frame mlt_frame_init( ); +extern mlt_properties mlt_frame_properties( mlt_frame this ); +extern int mlt_frame_is_test_card( mlt_frame this ); +extern double mlt_frame_get_aspect_ratio( mlt_frame this ); +extern int mlt_frame_set_aspect_ratio( mlt_frame this, double value ); +extern mlt_timecode mlt_frame_get_timecode( mlt_frame this ); +extern int mlt_frame_set_timecode( mlt_frame this, mlt_timecode value ); + +extern int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); +extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ); +extern int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); + +extern int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ); +extern mlt_get_image mlt_frame_pop_get_image( mlt_frame this ); +extern int mlt_frame_push_frame( mlt_frame this, mlt_frame that ); +extern mlt_frame mlt_frame_pop_frame( mlt_frame this ); +extern void mlt_frame_close( mlt_frame this ); + +/* convenience functions */ +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_frame_composite_yuv( mlt_frame this, mlt_frame that, int x, int y, float weight ); + +#endif + diff --git a/mlt/src/framework/mlt_manager.h b/mlt/src/framework/mlt_manager.h new file mode 100644 index 00000000..1567e644 --- /dev/null +++ b/mlt/src/framework/mlt_manager.h @@ -0,0 +1,26 @@ +/* + * mlt_manager.h -- manager service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_MANAGER_H_ +#define _MLT_MANAGER_H_ + +mlt_producer mlt_manager_init( char **config ); + +#endif diff --git a/mlt/src/framework/mlt_multitrack.c b/mlt/src/framework/mlt_multitrack.c new file mode 100644 index 00000000..77158c76 --- /dev/null +++ b/mlt/src/framework/mlt_multitrack.c @@ -0,0 +1,259 @@ +/* + * mlt_multitrack.c -- multitrack service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_multitrack.h" +#include "mlt_frame.h" + +#include +#include + +/** Private definition. +*/ + +struct mlt_multitrack_s +{ + // We're extending producer here + struct mlt_producer_s parent; + mlt_producer *list; + int size; + int count; +}; + +/** Forward reference. +*/ + +static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); + +/** Constructor. +*/ + +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 ) + { + producer->get_frame = producer_get_frame; + } + else + { + free( this ); + this = NULL; + } + } + + return this; +} + +/** Get the producer associated to this multitrack. +*/ + +mlt_producer mlt_multitrack_producer( mlt_multitrack this ) +{ + return &this->parent; +} + +/** Get the service associated this multitrack. +*/ + +mlt_service mlt_multitrack_service( mlt_multitrack this ) +{ + return mlt_producer_service( mlt_multitrack_producer( this ) ); +} + +/** Get the properties associated this multitrack. +*/ + +mlt_properties mlt_multitrack_properties( mlt_multitrack this ) +{ + return mlt_service_properties( mlt_multitrack_service( this ) ); +} + +/** Initialise timecode related information. +*/ + +static 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_timecode length = 0; + + // We need to ensure that fps are the same on all services + double fps = 0; + + // Obtain stats on all connected services + for ( i = 0; i < this->count; i ++ ) + { + // Get the producer from this index + mlt_producer producer = this->list[ i ]; + + // If it's allocated then, update our stats + if ( producer != NULL ) + { + // Determine the longest length + length = mlt_producer_get_length( producer ) > length ? mlt_producer_get_length( producer ) : length; + + // Handle fps + if ( fps == 0 ) + { + // This is the first producer, so it controls the fps + fps = mlt_producer_get_fps( producer ); + } + else if ( fps != mlt_producer_get_fps( producer ) ) + { + // Generate a warning for now - the following attempt to fix may fail + fprintf( stderr, "Warning: fps mismatch on track %d\n", i ); + + // It should be safe to impose fps on an image producer, but not necessarily safe for video + mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps ); + } + } + } + + // Update multitrack properties now - we'll not destroy the in point here + mlt_properties_set_timecode( properties, "length", length ); + mlt_properties_set_timecode( properties, "out", length ); + mlt_properties_set_timecode( properties, "playtime", length - mlt_properties_get_timecode( properties, "in" ) ); +} + +/** Connect a producer to a given track. +*/ + +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_producer ) ); + for ( i = this->size; i < track + 10; i ++ ) + this->list[ i ] = NULL; + this->size = track + 10; + } + + // Assign the track in our list here + this->list[ track ] = producer; + + // 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 frame method. + + 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. +*/ + +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 ]; + + // Obtain the current timecode + uint64_t position = mlt_producer_frame( parent ); + + // Make sure we're at the same point + mlt_producer_seek_frame( producer, position ); + + // Get the frame from the producer + mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 ); + } + else + { + // Generate a test frame + *frame = mlt_frame_init( ); + + // Let tractor know if we've reached the end + mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", index >= this->count ); + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *frame, mlt_producer_position( parent ) ); + + // Move on to the next frame + if ( index >= this->count ) + mlt_producer_prepare_next( parent ); + } + + fprintf( stderr, "timestamp for %d = %f\n", index, ( float )mlt_frame_get_timecode( *frame ) ); + + return 0; +} + +/** Close this instance. +*/ + +void mlt_multitrack_close( mlt_multitrack this ) +{ + // Close the producer + mlt_producer_close( &this->parent ); + + // Free the list + free( this->list ); + + // Free the object + free( this ); +} diff --git a/mlt/src/framework/mlt_multitrack.h b/mlt/src/framework/mlt_multitrack.h new file mode 100644 index 00000000..9f85fc1a --- /dev/null +++ b/mlt/src/framework/mlt_multitrack.h @@ -0,0 +1,36 @@ +/* + * mlt_multitrack.h -- multitrack service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_MULITRACK_H_ +#define _MLT_MULITRACK_H_ + +#include "mlt_producer.h" + +/** Public final methods +*/ + +extern mlt_multitrack mlt_multitrack_init( ); +extern mlt_producer mlt_multitrack_producer( mlt_multitrack this ); +extern mlt_service mlt_multitrack_service( mlt_multitrack this ); +extern int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track ); +extern void mlt_multitrack_close( mlt_multitrack this ); + +#endif + diff --git a/mlt/src/framework/mlt_playlist.c b/mlt/src/framework/mlt_playlist.c new file mode 100644 index 00000000..ffcb3d56 --- /dev/null +++ b/mlt/src/framework/mlt_playlist.c @@ -0,0 +1,101 @@ +/* + * mlt_playlist.c -- playlist service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_playlist.h" + +/** Private definition. +*/ + +struct mlt_playlist_s +{ + struct mlt_producer_s parent; + int count; + int track; +}; + +/** Constructor. + + TODO: Override and implement all transport related method. + TODO: Override and implement a time code normalising service get_frame +*/ + +mlt_playlist mlt_playlist_init( ) +{ + mlt_playlist this = calloc( sizeof( struct mlt_playlist_s ), 1 ); + if ( this != NULL ) + { + mlt_producer producer = &this->parent; + if ( mlt_producer_init( producer, this ) != 0 ) + { + free( this ); + this = NULL; + } + } + + return this; +} + +/** Get the producer associated to this playlist. +*/ + +mlt_producer mlt_playlist_producer( mlt_playlist this ) +{ + return &this->parent; +} + +/** Get the service associated to this playlist. +*/ + +mlt_service mlt_playlist_service( mlt_playlist this ) +{ + return mlt_producer_service( &this->parent ); +} + +/** Append a producer to the playlist. + + TODO: Implement + TODO: Extract length information from the producer. +*/ + +int mlt_playlist_append( mlt_playlist this, mlt_producer producer ) +{ + return 0; +} + +/** Append a blank to the playlist of a given length. + + TODO: Implement +*/ + +int mlt_playlist_blank( mlt_playlist this, mlt_timecode length ) +{ + return 0; +} + +/** Close the playlist. +*/ + +mlt_playlist_close( mlt_playlist this ) +{ + mlt_producer_close( &this->parent ); + free( this ); +} diff --git a/mlt/src/framework/mlt_playlist.h b/mlt/src/framework/mlt_playlist.h new file mode 100644 index 00000000..aa916c8e --- /dev/null +++ b/mlt/src/framework/mlt_playlist.h @@ -0,0 +1,37 @@ +/* + * mlt_playlist.h -- playlist service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PLAYLIST_H_ +#define _MLT_PLAYLIST_H_ + +#include "mlt_producer.h" + +/** Public final methods +*/ + +extern mlt_playlist mlt_playlist_init( ); +extern mlt_producer mlt_playlist_producer( mlt_playlist this ); +extern mlt_service mlt_playlist_service( mlt_playlist this ); +extern int mlt_playlist_append( mlt_playlist this, mlt_producer producer ); +extern int mlt_playlist_pad( mlt_playlist this, mlt_timecode length ); +extern mlt_playlist_close( mlt_playlist this ); + +#endif + diff --git a/mlt/src/framework/mlt_producer.c b/mlt/src/framework/mlt_producer.c new file mode 100644 index 00000000..91e39896 --- /dev/null +++ b/mlt/src/framework/mlt_producer.c @@ -0,0 +1,291 @@ +/* + * mlt_producer.c -- abstraction for all producer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_producer.h" +#include "mlt_frame.h" +#include +#include +#include +#include + +/** Forward references. +*/ + +static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor +*/ + +int mlt_producer_init( mlt_producer this, void *child ) +{ + // 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; + + // Get the properties of the parent + mlt_properties properties = mlt_service_properties( parent ); + + // Set the default properties + mlt_properties_set_timecode( properties, "position", 0.0 ); + mlt_properties_set_double( properties, "frame", 1 ); + mlt_properties_set_double( properties, "fps", 25.0 ); + mlt_properties_set_double( properties, "speed", 1.0 ); + mlt_properties_set_timecode( properties, "in", 0.0 ); + mlt_properties_set_timecode( properties, "out", 36000.0 ); + mlt_properties_set_timecode( properties, "playtime", 36000.0 ); + mlt_properties_set_timecode( properties, "length", 36000.0 ); + + // Override service get_frame + parent->get_frame = producer_get_frame; + } + + return 0; +} + +/** Get the parent service object. +*/ + +mlt_service mlt_producer_service( mlt_producer this ) +{ + return &this->parent; +} + +/** Get the producer properties. +*/ + +mlt_properties mlt_producer_properties( mlt_producer this ) +{ + return mlt_service_properties( &this->parent ); +} + +/** Seek to a specified time code. +*/ + +int mlt_producer_seek( mlt_producer this, mlt_timecode timecode ) +{ + // Check bounds + if ( timecode < 0 ) + timecode = 0; + if ( timecode > mlt_producer_get_playtime( this ) ) + timecode = mlt_producer_get_playtime( this ); + + // Set the position + mlt_properties_set_timecode( mlt_producer_properties( this ), "position", timecode ); + + // Calculate the absolute frame + double frame = ( mlt_producer_get_in( this ) + timecode ) * mlt_producer_get_fps( this ); + mlt_properties_set_double( mlt_producer_properties( this ), "frame", floor( frame + 0.5 ) ); + + return 0; +} + +/** Seek to a specified absolute frame. +*/ + +int mlt_producer_seek_frame( mlt_producer this, uint64_t frame ) +{ + // Calculate the time code + double timecode = ( frame / mlt_producer_get_fps( this ) ) - mlt_producer_get_in( this ); + + // If timecode is invalid, then seek on time + if ( timecode < 0 ) + { + // Seek to the in point + mlt_producer_seek( this, 0 ); + } + else if ( timecode > mlt_producer_get_playtime( this ) ) + { + // Seek to the out point + mlt_producer_seek( this, mlt_producer_get_playtime( this ) ); + } + else + { + // Set the position + mlt_properties_set_timecode( mlt_producer_properties( this ), "position", timecode ); + + // Set the absolute frame + mlt_properties_set_double( mlt_producer_properties( this ), "frame", frame ); + } + + return 0; +} + +/** Get the current time code. +*/ + +mlt_timecode mlt_producer_position( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "position" ); +} + +/** Get the current frame. +*/ + +uint64_t mlt_producer_frame( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "frame" ); +} + +/** Set the playing speed. +*/ + +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. +*/ + +double mlt_producer_get_speed( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "speed" ); +} + +/** Get the frames per second. +*/ + +double mlt_producer_get_fps( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "fps" ); +} + +/** Set the in and out points. +*/ + +int mlt_producer_set_in_and_out( mlt_producer this, mlt_timecode in, mlt_timecode out ) +{ + // Correct ins and outs if necessary + if ( in < 0 ) + in = 0; + if ( in > mlt_producer_get_length( this ) ) + in = mlt_producer_get_length( this ); + if ( out < 0 ) + out = 0; + if ( out > mlt_producer_get_length( this ) ) + out = mlt_producer_get_length( this ); + + // Swap ins and outs if wrong + if ( out < in ) + { + mlt_timecode t = in; + in = out; + out = t; + } + + // Set the values + mlt_properties_set_timecode( mlt_producer_properties( this ), "in", in ); + mlt_properties_set_timecode( mlt_producer_properties( this ), "out", out ); + mlt_properties_set_timecode( mlt_producer_properties( this ), "playtime", out - in ); + + // Seek to the in point + mlt_producer_seek( this, 0 ); + + return 0; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_producer_get_in( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "in" ); +} + +/** Get the out point. +*/ + +mlt_timecode mlt_producer_get_out( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "out" ); +} + +/** Get the total play time. +*/ + +mlt_timecode mlt_producer_get_playtime( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "playtime" ); +} + +/** Get the total length of the producer. +*/ + +mlt_timecode mlt_producer_get_length( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "length" ); +} + +/** Prepare for next frame. +*/ + +void mlt_producer_prepare_next( mlt_producer this ) +{ + mlt_producer_seek_frame( this, mlt_producer_frame( this ) + mlt_producer_get_speed( this ) ); +} + +/** Get a frame. +*/ + +static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + int result = 1; + mlt_producer this = service->child; + + // A properly instatiated producer will have a get_frame method... + if ( this->get_frame != NULL ) + { + // Get the frame from the implementation + result = this->get_frame( this, frame, index ); + } + else + { + // Generate a test frame + *frame = mlt_frame_init( ); + + // Set the timecode + result = mlt_frame_set_timecode( *frame, mlt_producer_position( this ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( this ); + } + + return 0; +} + +/** Close the producer. +*/ + +void mlt_producer_close( mlt_producer this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} diff --git a/mlt/src/framework/mlt_producer.h b/mlt/src/framework/mlt_producer.h new file mode 100644 index 00000000..a59a82d2 --- /dev/null +++ b/mlt/src/framework/mlt_producer.h @@ -0,0 +1,64 @@ +/* + * mlt_producer.h -- abstraction for all producer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PRODUCER_H_ +#define _MLT_PRODUCER_H_ + +#include "mlt_service.h" + +/** The interface definition for all producers. +*/ + +struct mlt_producer_s +{ + // We're implementing service here + struct mlt_service_s parent; + + // Public virtual methods + int ( *get_frame )( mlt_producer, mlt_frame_ptr, int ); + void ( *close )( mlt_producer ); + + // Private data + void *private; + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_producer_init( mlt_producer this, void *child ); +extern mlt_service mlt_producer_service( mlt_producer this ); +extern mlt_properties mlt_producer_properties( mlt_producer this ); +extern int mlt_producer_seek( mlt_producer this, mlt_timecode timecode ); +extern int mlt_producer_seek_frame( mlt_producer this, uint64_t frame ); +extern mlt_timecode mlt_producer_position( mlt_producer this ); +extern uint64_t mlt_producer_frame( mlt_producer this ); +extern int mlt_producer_set_speed( mlt_producer this, double speed ); +extern double mlt_producer_get_speed( mlt_producer this ); +extern double mlt_producer_get_fps( mlt_producer this ); +extern int mlt_producer_set_in_and_out( mlt_producer this, mlt_timecode in, mlt_timecode out ); +extern mlt_timecode mlt_producer_get_in( mlt_producer this ); +extern mlt_timecode mlt_producer_get_out( mlt_producer this ); +extern mlt_timecode mlt_producer_get_playtime( mlt_producer this ); +extern mlt_timecode mlt_producer_get_length( mlt_producer this ); +extern void mlt_producer_prepare_next( mlt_producer this ); +extern void mlt_producer_close( mlt_producer this ); + +#endif diff --git a/mlt/src/framework/mlt_properties.c b/mlt/src/framework/mlt_properties.c new file mode 100644 index 00000000..233e8ace --- /dev/null +++ b/mlt/src/framework/mlt_properties.c @@ -0,0 +1,324 @@ +/* + * mlt_properties.c -- base properties class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_properties.h" +#include "mlt_property.h" + +#include +#include +#include + +/* ---------------- // Private Implementation // ---------------- */ + +/** Private implementation of the property list. +*/ + +typedef struct +{ + char **name; + mlt_property *value; + int count; + int size; +} +property_list; + +/** Basic implementation. +*/ + +int mlt_properties_init( mlt_properties this, void *child ) +{ + // NULL all methods + memset( this, 0, sizeof( struct mlt_properties_s ) ); + + // Assign the child of the object + this->child = child; + + // Allocate the private structure + this->private = calloc( sizeof( property_list ), 1 ); + + return this->private == NULL; +} + +/** Locate a property by name +*/ + +static mlt_property mlt_properties_find( mlt_properties this, char *name ) +{ + mlt_property value = NULL; + property_list *list = this->private; + int i = 0; + + // Locate the item + for ( i = 0; value == NULL && i < list->count; i ++ ) + if ( !strcmp( list->name[ i ], name ) ) + value = list->value[ i ]; + + return value; +} + +/** Add a new property. +*/ + +static mlt_property mlt_properties_add( mlt_properties this, char *name ) +{ + property_list *list = this->private; + + // Check that we have space and resize if necessary + if ( list->count == list->size ) + { + list->size += 10; + list->name = realloc( list->name, list->size * sizeof( 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( ); + + // Return and increment count accordingly + return list->value[ list->count ++ ]; +} + +/** Fetch a property by name - this includes add if not found. +*/ + +static mlt_property mlt_properties_fetch( mlt_properties this, 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; +} + +/** Set the property. +*/ + +int mlt_properties_set( mlt_properties this, char *name, 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 ) + error = mlt_property_set_string( property, value ); + + return error; +} + +/** Get a string value by name. +*/ + +char *mlt_properties_get( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? NULL : mlt_property_get_string( value ); +} + +/** Get a name by index. +*/ + +char *mlt_properties_get_name( mlt_properties this, int index ) +{ + property_list *list = this->private; + if ( index >= 0 && index < list->count ) + return list->name[ index ]; + return NULL; +} + +/** Get a string value by index. +*/ + +char *mlt_properties_get_value( mlt_properties this, int index ) +{ + property_list *list = this->private; + if ( index >= 0 && index < list->count ) + return mlt_property_get_string( list->value[ index ] ); + return NULL; +} + +/** Return the number of items in the list. +*/ + +int mlt_properties_count( mlt_properties this ) +{ + property_list *list = this->private; + return list->count; +} + +/** Set a value by parsing a name=value string +*/ + +int mlt_properties_parse( mlt_properties this, char *namevalue ) +{ + char *name = strdup( namevalue ); + char *value = strdup( namevalue ); + int error = 0; + + if ( strchr( name, '=' ) ) + { + *( strchr( name, '=' ) ) = '\0'; + strcpy( value, strchr( value, '=' ) + 1 ); + } + else + { + strcpy( value, "" ); + } + + error = mlt_properties_set( this, name, value ); + + free( name ); + free( value ); + + return error; +} + +/** Get a value associated to the name. +*/ + +int mlt_properties_get_int( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_int( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_int( mlt_properties this, 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 ); + + return error; +} + +/** Get a value associated to the name. +*/ + +double mlt_properties_get_double( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_double( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_double( mlt_properties this, 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 ); + + return error; +} + +/** Get a value associated to the name. +*/ + +mlt_timecode mlt_properties_get_timecode( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_timecode( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_timecode( mlt_properties this, char *name, mlt_timecode 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_timecode( property, value ); + + return error; +} + +/** Get a value associated to the name. +*/ + +void *mlt_properties_get_data( mlt_properties this, char *name, int *length ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? NULL : mlt_property_get_data( value, length ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_data( mlt_properties this, 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 ); + + return error; +} + +/** Close the list. +*/ + +void mlt_properties_close( mlt_properties this ) +{ + property_list *list = this->private; + int index = 0; + + // Clean up names and values + for ( index = 0; index < list->count; index ++ ) + { + free( list->name[ index ] ); + mlt_property_close( list->value[ index ] ); + } + + // Clear up the list + free( list->name ); + free( list->value ); + free( list ); +} + diff --git a/mlt/src/framework/mlt_properties.h b/mlt/src/framework/mlt_properties.h new file mode 100644 index 00000000..b64753bc --- /dev/null +++ b/mlt/src/framework/mlt_properties.h @@ -0,0 +1,56 @@ +/* + * mlt_properties.h -- base properties class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PROPERTIES_H_ +#define _MLT_PROPERTIES_H_ + +#include "mlt_types.h" + +/** The properties base class defines the basic property propagation and + handling. +*/ + +struct mlt_properties_s +{ + void *child; + void *private; +}; + +/** Public interface. +*/ + +extern int mlt_properties_init( mlt_properties, void *child ); +extern int mlt_properties_set( mlt_properties this, char *name, char *value ); +extern int mlt_properties_parse( mlt_properties this, char *namevalue ); +extern char *mlt_properties_get( mlt_properties this, char *name ); +extern char *mlt_properties_get_name( mlt_properties this, int index ); +extern char *mlt_properties_get_value( mlt_properties this, int index ); +extern int mlt_properties_get_int( mlt_properties this, char *name ); +extern int mlt_properties_set_int( mlt_properties this, char *name, int value ); +extern double mlt_properties_get_double( mlt_properties this, char *name ); +extern int mlt_properties_set_double( mlt_properties this, char *name, double value ); +extern mlt_timecode mlt_properties_get_timecode( mlt_properties this, char *name ); +extern int mlt_properties_set_timecode( mlt_properties this, char *name, mlt_timecode value ); +extern int mlt_properties_set_data( mlt_properties this, char *name, void *value, int length, mlt_destructor, mlt_serialiser ); +extern void *mlt_properties_get_data( mlt_properties this, char *name, int *length ); +extern int mlt_properties_count( mlt_properties this ); +extern void mlt_properties_close( mlt_properties this ); + +#endif diff --git a/mlt/src/framework/mlt_property.c b/mlt/src/framework/mlt_property.c new file mode 100644 index 00000000..021dbc17 --- /dev/null +++ b/mlt/src/framework/mlt_property.c @@ -0,0 +1,220 @@ +/* + * mlt_property.c -- property class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_property.h" + +#include +#include +#include + +/** Construct and uninitialised property. +*/ + +mlt_property mlt_property_init( ) +{ + return calloc( sizeof( struct mlt_property_s ), 1 ); +} + +/** Clear a property. +*/ + +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 ); + + // We can wipe it now. + memset( this, 0, sizeof( struct mlt_property_s ) ); +} + +/** Set an int on this property. +*/ + +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 a double on this property. +*/ + +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 a timecode on this property. +*/ + +int mlt_property_set_timecode( mlt_property this, mlt_timecode value ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_timecode; + this->prop_timecode = value; + return 0; +} + +/** Set a string on this property. +*/ + +int mlt_property_set_string( mlt_property this, char *value ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_string; + if ( value != NULL ) + this->prop_string = strdup( value ); + return this->prop_string != NULL; +} + +/** Set a data on this property. +*/ + +int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_data; + this->data = value; + this->length = length; + this->destructor = destructor; + this->serialiser = serialiser; + return 0; +} + +/** Get an int from this property. +*/ + +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_timecode ) + return ( int )this->prop_timecode; + else if ( this->types & mlt_prop_string ) + return atoi( this->prop_string ); + return 0; +} + +/** Get a double from this property. +*/ + +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_timecode ) + return ( double )this->prop_timecode; + else if ( this->types & mlt_prop_string ) + return atof( this->prop_string ); + return 0; +} + +/** Get a timecode from this property. +*/ + +mlt_timecode mlt_property_get_timecode( mlt_property this ) +{ + if ( this->types & mlt_prop_timecode ) + return this->prop_timecode; + else if ( this->types & mlt_prop_int ) + return ( mlt_timecode )this->prop_int; + else if ( this->types & mlt_prop_double ) + return ( mlt_timecode )this->prop_double; + else if ( this->types & mlt_prop_string ) + return ( mlt_timecode )atof( this->prop_string ); + return 0; +} + +/** Get a string from this property. +*/ + +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, "%e", this->prop_double ); + } + else if ( this->types & mlt_prop_timecode ) + { + this->types |= mlt_prop_string; + this->prop_string = malloc( 32 ); + sprintf( this->prop_string, "%e", this->prop_timecode ); + } + 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 a data and associated length. +*/ + +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; +} + +/** Close this property. +*/ + +void mlt_property_close( mlt_property this ) +{ + mlt_property_clear( this ); + free( this ); +} + + diff --git a/mlt/src/framework/mlt_property.h b/mlt/src/framework/mlt_property.h new file mode 100644 index 00000000..8fb209c4 --- /dev/null +++ b/mlt/src/framework/mlt_property.h @@ -0,0 +1,82 @@ +/* + * mlt_property.h -- property class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PROPERTY_H_ +#define _MLT_PROPERTY_H_ + +#include "mlt_types.h" + +/** Bit pattern for properties. +*/ + +typedef enum +{ + mlt_prop_none = 0, + mlt_prop_int = 1, + mlt_prop_string = 2, + mlt_prop_timecode = 4, + mlt_prop_double = 8, + mlt_prop_data = 16 +} +mlt_property_type; + +/** Property structure. +*/ + +typedef 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_timecode prop_timecode; + double prop_double; + + // String handling + char *prop_string; + + // Generic type handling + void *data; + int length; + mlt_destructor destructor; + mlt_serialiser serialiser; +} +*mlt_property; + +/** API +*/ + +extern mlt_property mlt_property_init( ); +extern void mlt_property_clear( mlt_property this ); +extern int mlt_property_set_int( mlt_property this, int value ); +extern int mlt_property_set_double( mlt_property this, double value ); +extern int mlt_property_set_timecode( mlt_property this, mlt_timecode value ); +extern int mlt_property_set_string( mlt_property this, char *value ); +extern int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser ); +extern int mlt_property_get_int( mlt_property this ); +extern double mlt_property_get_double( mlt_property this ); +extern mlt_timecode mlt_property_get_timecode( mlt_property this ); +extern char *mlt_property_get_string( mlt_property this ); +extern void *mlt_property_get_data( mlt_property this, int *length ); +extern void mlt_property_close( mlt_property this ); + +#endif + diff --git a/mlt/src/framework/mlt_repository.c b/mlt/src/framework/mlt_repository.c new file mode 100644 index 00000000..9c41bf92 --- /dev/null +++ b/mlt/src/framework/mlt_repository.c @@ -0,0 +1,42 @@ +/* + * repository.c -- provides a map between service and shared objects + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mlt_repository.h" +#include + +struct mlt_repository_s +{ +}; + +mlt_repository mlt_repository_init( char *file, char *symbol ) +{ + return NULL; +} + +void *mlt_repository_fetch( mlt_repository this, char *service, void *input ) +{ + return NULL; +} + +void mlt_repository_close( mlt_repository this ) +{ +} + + diff --git a/mlt/src/framework/mlt_repository.h b/mlt/src/framework/mlt_repository.h new file mode 100644 index 00000000..afab1cc8 --- /dev/null +++ b/mlt/src/framework/mlt_repository.h @@ -0,0 +1,37 @@ +/* + * repository.h -- provides a map between service and shared objects + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_REPOSITORY_H_ +#define _MLT_REPOSITORY_H_ + +/** Repository structure forward reference. +*/ + +typedef struct mlt_repository_s *mlt_repository; + +/** Public functions. +*/ + +extern mlt_repository mlt_repository_init( char *file, char *symbol ); +extern void *mlt_repository_fetch( mlt_repository this, char *service, void *input ); +extern void mlt_repository_close( mlt_repository this ); + +#endif + diff --git a/mlt/src/framework/mlt_service.c b/mlt/src/framework/mlt_service.c new file mode 100644 index 00000000..8e626d41 --- /dev/null +++ b/mlt/src/framework/mlt_service.c @@ -0,0 +1,275 @@ +/* + * mlt_service.c -- interface for all service classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_service.h" +#include "mlt_frame.h" +#include +#include +#include + +/** 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. +*/ + +/** Private service definition. +*/ + +typedef struct +{ + int size; + int count; + mlt_service *in; + mlt_service out; +} +mlt_service_base; + +/** Friends? +*/ + +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 ); + +/** Constructor +*/ + +int mlt_service_init( mlt_service this, void *child ) +{ + // Initialise everything to NULL + memset( this, 0, sizeof( struct mlt_service_s ) ); + + // Assign the child + this->child = child; + + // Generate private space + this->private = calloc( sizeof( mlt_service_base ), 1 ); + + // Associate the methods + this->get_frame = service_get_frame; + + // Initialise the properties + return mlt_properties_init( &this->parent, this ); +} + +/** Return the properties object. +*/ + +mlt_properties mlt_service_properties( mlt_service this ) +{ + return &this->parent; +} + +/** Connect a producer service. + Returns: > 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->private; + + // Does this service accept input? + if ( mlt_service_accepts_input( this ) == 0 ) + return 1; + + // Does the producer service accept output connections? + if ( mlt_service_accepts_output( producer ) == 0 ) + return 2; + + // 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 ) + { + // 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 ); + + // Inform caller that all went well + return 0; + } + else + { + return -1; + } +} + +/** Disconnect this service from its consumer. +*/ + +void mlt_service_disconnect( mlt_service this ) +{ + // Get the service base + mlt_service_base *base = this->private; + + // There's a bit more required here... + base->out = NULL; +} + +/** Associate this service to the its consumer. +*/ + +void mlt_service_connect( mlt_service this, mlt_service that ) +{ + // Get the service base + mlt_service_base *base = this->private; + + // There's a bit more required here... + base->out = that; +} + +/** Get the service state. +*/ + +mlt_service_state mlt_service_get_state( mlt_service this ) +{ + mlt_service_state state = mlt_state_unknown; + if ( mlt_service_has_input( this ) ) + state |= mlt_state_providing; + if ( mlt_service_has_output( this ) ) + state |= mlt_state_connected; + if ( state != ( mlt_state_providing | mlt_state_connected ) ) + state |= mlt_state_dormant; + return state; +} + +/** Get the maximum number of inputs accepted. + Returns: -1 for many, 0 for none or n for fixed. +*/ + +int mlt_service_accepts_input( mlt_service this ) +{ + if ( this->accepts_input == NULL ) + return -1; + else + return this->accepts_input( this ); +} + +/** Get the maximum number of outputs accepted. +*/ + +int mlt_service_accepts_output( mlt_service this ) +{ + if ( this->accepts_output == NULL ) + return 1; + else + return this->accepts_output( this ); +} + +/** Determines if this service has input +*/ + +int mlt_service_has_input( mlt_service this ) +{ + if ( this->has_input == NULL ) + return 1; + else + return this->has_input( this ); +} + +/** Determine if this service has output +*/ + +int mlt_service_has_output( mlt_service this ) +{ + mlt_service_base *base = this->private; + if ( this->has_output == NULL ) + return base->out != NULL; + else + return this->has_output( this ); +} + +/** Check if the service is active. +*/ + +int mlt_service_is_active( mlt_service this ) +{ + return !( mlt_service_get_state( this ) & mlt_state_dormant ); +} + +/** Obtain a frame to pass on. +*/ + +static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) +{ + mlt_service_base *base = this->private; + 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( ); + return 0; +} + +int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) +{ + return this->get_frame( this, frame, index ); +} + +/** Close the service. +*/ + +void mlt_service_close( mlt_service this ) +{ + mlt_service_base *base = this->private; + free( base->in ); + free( base ); + mlt_properties_close( &this->parent ); +} + diff --git a/mlt/src/framework/mlt_service.h b/mlt/src/framework/mlt_service.h new file mode 100644 index 00000000..88b91b50 --- /dev/null +++ b/mlt/src/framework/mlt_service.h @@ -0,0 +1,79 @@ +/* + * mlt_service.h -- interface for all service classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_SERVICE_H_ +#define _MLT_SERVICE_H_ + +#include "mlt_properties.h" + +/** State of a service. + + Note that a service may be dormant even though it's fully connected, + providing or consuming. +*/ + +typedef enum +{ + mlt_state_unknown = 0, + mlt_state_dormant = 1, + mlt_state_connected = 2, + mlt_state_providing = 4, + mlt_state_consuming = 8 +} +mlt_service_state; + +/** The interface definition for all services. +*/ + +struct mlt_service_s +{ + // We're extending properties here + struct mlt_properties_s parent; + + // Protected virtual + int ( *accepts_input )( mlt_service this ); + int ( *accepts_output )( mlt_service this ); + int ( *has_input )( mlt_service this ); + int ( *has_output )( mlt_service this ); + int ( *get_frame )( mlt_service this, mlt_frame_ptr frame, int index ); + + // Private data + void *private; + void *child; +}; + +/** The public API. +*/ + +extern int mlt_service_init( mlt_service this, void *child ); +extern mlt_properties mlt_service_properties( mlt_service this ); +extern int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index ); +extern mlt_service_state mlt_service_get_state( mlt_service this ); +extern void mlt_service_close( mlt_service this ); + +extern int mlt_service_accepts_input( mlt_service this ); +extern int mlt_service_accepts_output( mlt_service this ); +extern int mlt_service_has_input( mlt_service this ); +extern int mlt_service_has_output( mlt_service this ); +extern int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); +extern int mlt_service_is_active( mlt_service this ); + +#endif + diff --git a/mlt/src/framework/mlt_tractor.c b/mlt/src/framework/mlt_tractor.c new file mode 100644 index 00000000..f4b77c46 --- /dev/null +++ b/mlt/src/framework/mlt_tractor.c @@ -0,0 +1,155 @@ +/* + * mlt_tractor.c -- tractor service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_tractor.h" +#include "mlt_frame.h" + +#include +#include + +/** Private structure. +*/ + +struct mlt_tractor_s +{ + struct mlt_service_s parent; + mlt_service producer; +}; + +/** Forward references to static methods. +*/ + +static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int track ); + +/** Constructor for the tractor. + + TODO: thread this service... +*/ + +mlt_tractor mlt_tractor_init( ) +{ + mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 ); + if ( this != NULL ) + { + mlt_service service = &this->parent; + if ( mlt_service_init( service, this ) == 0 ) + { + service->get_frame = service_get_frame; + } + else + { + free( this ); + this = NULL; + } + } + return this; +} + +/** Get the service object associated to the tractor. +*/ + +mlt_service mlt_tractor_service( mlt_tractor this ) +{ + return &this->parent; +} + +/** Connect the tractor. +*/ + +int mlt_tractor_connect( mlt_tractor this, mlt_service producer ) +{ + int ret = mlt_service_connect_producer( &this->parent, producer, 0 ); + + if ( ret == 0 ) + { + // This is the producer we're going to connect to + this->producer = producer; + } + + return ret; +} + +/** Get the next frame. + + TODO: This should be reading a pump being populated by the thread... +*/ + +static int service_get_frame( mlt_service 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 looking = 1; + int done = 0; + mlt_frame temp; + + // 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 ); + + // Check for last track + done = mlt_properties_get_int( mlt_frame_properties( temp ), "last_track" ); + + // Handle the frame + if ( done && looking ) + { + // Use this as output if we don't have one already + *frame = temp; + } + else if ( !mlt_frame_is_test_card( temp ) && looking ) + { + // This is the one we want and we can stop looking + *frame = temp; + looking = 0; + } + else + { + // We discard all other frames + mlt_frame_close( temp ); + } + } + + // Indicate our found status + return 0; + } + else + { + // Generate a test card + *frame = mlt_frame_init( ); + return 0; + } +} + +/** Close the tractor. +*/ + +void mlt_tractor_close( mlt_tractor this ) +{ + mlt_service_close( &this->parent ); + free( this ); +} + diff --git a/mlt/src/framework/mlt_tractor.h b/mlt/src/framework/mlt_tractor.h new file mode 100644 index 00000000..28a6071b --- /dev/null +++ b/mlt/src/framework/mlt_tractor.h @@ -0,0 +1,31 @@ +/* + * mlt_tractor.h -- tractor service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TRACTOR_H_ +#define _MLT_TRACTOR_H_ + +#include "mlt_producer.h" + +extern mlt_tractor mlt_tractor_init( ); +extern mlt_service mlt_tractor_service( mlt_tractor this ); +extern int mlt_tractor_connect( mlt_tractor this, mlt_service service ); +extern void mlt_tractor_close( mlt_tractor this ); + +#endif diff --git a/mlt/src/framework/mlt_transition.c b/mlt/src/framework/mlt_transition.c new file mode 100644 index 00000000..dd4f2f64 --- /dev/null +++ b/mlt/src/framework/mlt_transition.c @@ -0,0 +1,232 @@ +/* + * mlt_transition.c -- abstraction for all transition services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_transition.h" +#include "mlt_frame.h" + +#include +#include +#include + +/** Forward references. +*/ + +static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor. +*/ + +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 ) + { + service->get_frame = transition_get_frame; + return 0; + } + return 1; +} + +/** Get the service associated to the transition. +*/ + +mlt_service mlt_transition_service( mlt_transition this ) +{ + return &this->parent; +} + +/** Connect this transition with a producers a and b tracks. +*/ + +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 ) + { + this->producer = producer; + this->a_track = a_track; + this->b_track = b_track; + this->in = 0; + this->out = 0; + } + return ret; +} + +/** Set the in and out points. +*/ + +void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out ) +{ + this->in = in; + this->out = out; +} + +/** Get the index of the a track. +*/ + +int mlt_transition_get_a_track( mlt_transition this ) +{ + return this->a_track; +} + +/** Get the index of the b track. +*/ + +int mlt_transition_get_b_track( mlt_transition this ) +{ + return this->b_track; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_transition_get_in( mlt_transition this ) +{ + return this->in; +} + +/** Get the out point. +*/ + +mlt_timecode mlt_transition_get_out( mlt_transition this ) +{ + return this->out; +} + +/** Process the frame. +*/ + +static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame ) +{ + if ( this->process == NULL ) + { + if ( !mlt_frame_is_test_card( a_frame ) ) + { + mlt_frame_close( b_frame ); + return a_frame; + } + else + { + mlt_frame_close( a_frame ); + return b_frame; + } + } + else + { + return this->process( this, a_frame, b_frame ); + } +} + +/** Get a frame from this filter. + + The logic is complex here. A transition is applied to frames on the a and b tracks + specified in the connect method above. Since all frames are obtained via this + method for all tracks, we have to take special care that we only obtain the a and + b frames once - we do this on the first call to get a frame from either a or b. + + After that, we have 3 cases to resolve: + + 1) if the track is the a_track and we're in the time zone, then we need to call the + process method to do the effect on the frame (we assign NULL to the a_frame and + b_frames here) otherwise, we pass on the a_frame unmolested; + 2) if the track is the b_track and we're the in the time zone OR the b_frame is NULL, + then we generate a test card frame, otherwise we pass on the b frame unmolested; + 3) For all other tracks, we get the frames on demand. +*/ + +static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + mlt_transition this = service->child; + + // Fetch a and b frames together... + if ( ( index == this->a_track || index == this->b_track ) && + ( this->a_frame == NULL && this->b_frame == NULL ) ) + { + mlt_service_get_frame( this->producer, &this->a_frame, this->a_track ); + mlt_service_get_frame( this->producer, &this->b_frame, this->b_track ); + } + + // Special case track processing + if ( index == this->a_track ) + { + // Determine if we're in the right time zone + mlt_timecode timecode = mlt_frame_get_timecode( this->a_frame ); + if ( timecode >= this->in && timecode < this->out ) + { + // Process the transition + *frame = transition_process( this, this->a_frame, this->b_frame ); + + // Important - NULL both frames now so that we know they're done... + this->a_frame = NULL; + this->b_frame = NULL; + } + else + { + // Pass on the 'a frame' and remember that we've done it + *frame = this->a_frame; + this->a_frame = NULL; + } + return 0; + } + if ( index == this->b_track ) + { + if ( this->b_frame == NULL ) + { + // We're *probably* in the zone and the a frame has been requested + *frame = mlt_frame_init( ); + } + else + { + mlt_timecode timecode = mlt_frame_get_timecode( this->b_frame ); + if ( timecode >= this->in && timecode < this->out ) + { + // We're in the zone, but the 'a frame' has not been requested yet + *frame = mlt_frame_init( ); + } + else + { + // We're out of the zone, pass on b and remember that we've done it + *frame = this->b_frame; + this->b_frame = NULL; + } + } + return 0; + } + else + { + // Pass through + return mlt_service_get_frame( this->producer, frame, index ); + } +} + +/** Close the transition. +*/ + +void mlt_transitition_close( mlt_transition this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} diff --git a/mlt/src/framework/mlt_transition.h b/mlt/src/framework/mlt_transition.h new file mode 100644 index 00000000..397483f1 --- /dev/null +++ b/mlt/src/framework/mlt_transition.h @@ -0,0 +1,68 @@ +/* + * mlt_transition.h -- abstraction for all transition services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TRANSITION_H_ +#define _MLT_TRANSITION_H_ + +#include "mlt_service.h" + +/** The interface definition for all transitions. +*/ + +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; + int a_track; + int b_track; + mlt_timecode in; + mlt_timecode out; + + // Private + mlt_frame a_frame; + mlt_frame b_frame; +}; + +/** Public final methods +*/ + +extern int mlt_transition_init( mlt_transition this, void *child ); +extern mlt_service mlt_transition_service( mlt_transition this ); +extern int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track ); +extern void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out ); +extern int mlt_transition_get_a_track( mlt_transition this ); +extern int mlt_transition_get_b_track( mlt_transition this ); +extern mlt_timecode mlt_transition_get_in( mlt_transition this ); +extern mlt_timecode mlt_transition_get_out( mlt_transition this ); +extern void mlt_transitition_close( mlt_transition this ); + +#endif diff --git a/mlt/src/framework/mlt_types.h b/mlt/src/framework/mlt_types.h new file mode 100644 index 00000000..aa974632 --- /dev/null +++ b/mlt/src/framework/mlt_types.h @@ -0,0 +1,43 @@ +/* + * mlt_types.h -- provides forward definitions of all public types + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TYPES_H_ +#define _MLT_TYPES_H_ + +#include + +typedef double mlt_timecode; +typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; +typedef struct mlt_properties_s *mlt_properties; +typedef struct mlt_service_s *mlt_service; +typedef struct mlt_producer_s *mlt_producer; +typedef struct mlt_manager_s *mlt_manager; +typedef struct mlt_playlist_s *mlt_playlist; +typedef struct mlt_multitrack_s *mlt_multitrack; +typedef struct mlt_filter_s *mlt_filter; +typedef struct mlt_transition_s *mlt_transition; +typedef struct mlt_consumer_s *mlt_consumer; +typedef struct mlt_tractor_s *mlt_tractor; + +typedef void ( *mlt_destructor )( void * ); +typedef char *( *mlt_serialiser )( void *, int length ); + + +#endif diff --git a/mlt/src/miracle/configure b/mlt/src/miracle/configure new file mode 100755 index 00000000..1a248525 --- /dev/null +++ b/mlt/src/miracle/configure @@ -0,0 +1 @@ +#!/bin/sh diff --git a/mlt/src/miracle/miracle.c b/mlt/src/miracle/miracle.c new file mode 100644 index 00000000..47a645ab --- /dev/null +++ b/mlt/src/miracle/miracle.c @@ -0,0 +1,110 @@ +/* + * dv1394d.c -- A DV over IEEE 1394 TCP Server + * + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Authors: + * Dan Dennedy + * Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#include "dvserver.h" +#include "log.h" + +/** Our dv server. +*/ + +static dv_server server = NULL; + +/** atexit shutdown handler for the server. +*/ + +static void main_cleanup( ) +{ + dv_server_shutdown( 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 }; + + server = dv_server_init( argv[ 0 ] ); + + for ( index = 1; index < argc; index ++ ) + { + if ( !strcmp( argv[ index ], "-port" ) ) + dv_server_set_port( server, atoi( argv[ ++ index ] ) ); + else if ( !strcmp( argv[ index ], "-proxy" ) ) + dv_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(); + dv1394d_log_init( log_syslog, LOG_INFO ); + } + else + { + dv1394d_log_init( log_stderr, LOG_INFO ); + } + + atexit( main_cleanup ); + + /* Execute the server */ + error = dv_server_execute( server ); + + /* We need to wait until we're exited.. */ + while ( !server->shutdown ) + nanosleep( &tm, NULL ); + + return error; +} diff --git a/mlt/src/miracle/miracle_commands.c b/mlt/src/miracle/miracle_commands.c new file mode 100644 index 00000000..8a492d1a --- /dev/null +++ b/mlt/src/miracle/miracle_commands.c @@ -0,0 +1,453 @@ +/* + * global_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvunit.h" +#include "global_commands.h" +#include "raw1394util.h" +#include +#include "log.h" + +static dv_unit g_units[MAX_UNITS]; + + +/** Return the dv_unit given a numeric index. +*/ + +dv_unit dv1394d_get_unit( int n ) +{ + if (n < MAX_UNITS) + return g_units[n]; + else + return NULL; +} + +/** Destroy the dv_unit given its numeric index. +*/ + +void dv1394d_delete_unit( int n ) +{ + if (n < MAX_UNITS) + { + dv_unit unit = dv1394d_get_unit(n); + if (unit != NULL) + { + dv_unit_close( unit ); + g_units[ n ] = NULL; + dv1394d_log( LOG_NOTICE, "Deleted unit U%d.", n ); + } + } +} + +/** Destroy all allocated units on the server. +*/ + +void dv1394d_delete_all_units( void ) +{ + int i; + for (i = 0; i < MAX_UNITS; i++) + if ( dv1394d_get_unit(i) != NULL ) + { + dv_unit_close( dv1394d_get_unit(i) ); + dv1394d_log( LOG_NOTICE, "Deleted unit U%d.", i ); + } +} + +/** Add a DV virtual vtr to the server. +*/ +response_codes dv1394d_add_unit( command_argument cmd_arg ) +{ + int i; + int channel = -1; + char *guid_str = (char*) cmd_arg->argument; + octlet_t guid; + uint32_t guid_hi; + uint32_t guid_lo; + + sscanf( guid_str, "%08x%08x", &guid_hi, &guid_lo ); + guid = (octlet_t)guid_hi << 32 | (octlet_t) guid_lo; + + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 3 ) + channel = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 2 ) ); + + /* make sure unit does not already exit */ + for (i = 0; i < MAX_UNITS; i++) + { + if (g_units[i] != NULL) + if ( dv_unit_get_guid( g_units[i] ) == guid ) + { + dv_response_printf( cmd_arg->response, 1024, "a unit already exists for that node\n\n" ); + return RESPONSE_ERROR; + } + } + + for (i = 0; i < MAX_UNITS; i++) + { + if (g_units[i] == NULL) + { + + g_units[ i ] = dv_unit_init( guid, channel ); + if ( g_units[ i ] == NULL ) + { + dv_response_printf( cmd_arg->response, 1024, "failed to allocate unit\n" ); + return RESPONSE_ERROR; + } + g_units[ i ]->unit = i; + dv_unit_set_notifier( g_units[ i ], dv_parser_get_notifier( cmd_arg->parser ), cmd_arg->root_dir ); + + dv1394d_log( LOG_NOTICE, "added unit %d to send to node %d over channel %d", + i, dv_unit_get_nodeid( g_units[i] ), dv_unit_get_channel( g_units[i] ) ); + dv_response_printf( cmd_arg->response, 10, "U%1d\n\n", i ); + return RESPONSE_SUCCESS_N; + } + } + + dv_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 dv1394d_list_nodes( command_argument cmd_arg ) +{ + response_codes error = RESPONSE_SUCCESS_N; + raw1394handle_t handle; + int i, j; + char line[1024]; + octlet_t guid; + rom1394_directory dir; + + for ( j = 0; j < raw1394_get_num_ports(); j++ ) + { + handle = raw1394_open(j); + for ( i = 0; i < raw1394_get_nodecount(handle); ++i ) + { + rom1394_get_directory( handle, i, &dir); + if ( (rom1394_get_node_type(&dir) == ROM1394_NODE_TYPE_AVC) ) + { + guid = rom1394_get_guid(handle, i); + if (dir.label != NULL) + { + snprintf( line, 1023, "%02d %08x%08x \"%s\"\n", i, + (quadlet_t) (guid>>32), (quadlet_t) (guid & 0xffffffff), dir.label ); + } else { + snprintf( line, 1023, "%02d %08x%08x \"Unlabeled Node %d\"\n", i, + (quadlet_t) (guid>>32), (quadlet_t) (guid & 0xffffffff), i ); + } + dv_response_write( cmd_arg->response, line, strlen(line) ); + rom1394_free_directory( &dir); + } + } + raw1394_close( handle ); + } + dv_response_write( cmd_arg->response, "\n", 1 ); + return error; +} + + +/** List units already added to server. +*/ +response_codes dv1394d_list_units( command_argument cmd_arg ) +{ + response_codes error = RESPONSE_SUCCESS_N; + char line[1024]; + int i; + + for (i = 0; i < MAX_UNITS; i++) + { + if (dv1394d_get_unit(i) != NULL) + { + snprintf( line, 1023, "U%d %02d %08x%08x %d\n", i, dv_unit_get_nodeid(g_units[i]), + (quadlet_t) (dv_unit_get_guid(g_units[i]) >> 32), + (quadlet_t) (dv_unit_get_guid(g_units[i]) & 0xffffffff), + !dv_unit_is_offline( g_units[i] ) ); + dv_response_write( cmd_arg->response, line, strlen(line) ); + } + } + dv_response_write( cmd_arg->response, "\n", 1 ); + + return error; +} + +static int +filter_files( const struct dirent *de ) +{ + if ( de->d_name[ 0 ] != '.' ) + return 1; + else + return 0; +} + +/** List clips in a directory. +*/ +response_codes dv1394d_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 ) ) + dv_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 ) || ( strstr( fullname, ".clip" ) && info.st_mode | S_IXUSR ) ) ) + dv_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 ); + dv_response_write( cmd_arg->response, "\n", 1 ); + } + + return error; +} + +/** Set a server configuration property. +*/ + +response_codes dv1394d_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++; + dv1394d_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) + dv_unit_terminate( g_units[i] ); + } + + /* set the property */ + strncpy( cmd_arg->root_dir, value, 1023 ); + + /* add a trailing slash if needed */ + if ( 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 dv1394d_get_global_property( command_argument cmd_arg ) +{ + char *key = (char*) cmd_arg->argument; + + if ( strncasecmp( key, "root", 1024) == 0 ) + { + dv_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; +} + +/** IEEE 1394 Bus Reset handler + + This is included here for now due to all the unit management involved. +*/ + +static int reset_handler( raw1394handle_t h, unsigned int generation ) +{ + int i, j, count, retry = 3; + int port = (int) raw1394_get_userdata( h ); + + raw1394_update_generation( h, generation ); + dv1394d_log( LOG_NOTICE, "bus reset on port %d", port ); + + while ( retry-- > 0 ) + { + raw1394handle_t handle = raw1394_open( port ); + count = raw1394_get_nodecount( handle ); + + if ( count > 0 ) + { + dv1394d_log( LOG_DEBUG, "bus reset, checking units" ); + + /* suspend all units on this port */ + for ( j = MAX_UNITS; j > 0; j-- ) + { + if ( g_units[ j-1 ] != NULL && dv_unit_get_port( g_units[ j-1 ] ) == port ) + dv_unit_suspend( g_units[ j-1 ] ); + } + dv1394d_log( LOG_DEBUG, "All units are now stopped" ); + + /* restore units with known guid, take others offline */ + for ( j = 0; j < MAX_UNITS; j++ ) + { + if ( g_units[j] != NULL && + ( dv_unit_get_port( g_units[ j ] ) == port || dv_unit_get_port( g_units[ j ] ) == -1 ) ) + { + int found = 0; + for ( i = 0; i < count; i++ ) + { + octlet_t guid; + dv1394d_log( LOG_DEBUG, "attempting to get guid for node %d", i ); + guid = rom1394_get_guid( handle, i ); + if ( guid == g_units[ j ]->guid ) + { + dv1394d_log( LOG_NOTICE, "unit with GUID %08x%08x found", + (quadlet_t) (g_units[j]->guid>>32), (quadlet_t) (g_units[j]->guid & 0xffffffff)); + if ( dv_unit_is_offline( g_units[ j ] ) ) + dv_unit_online( g_units[ j ] ); + else + dv_unit_restore( g_units[ j ] ); + found = 1; + break; + } + } + if ( found == 0 ) + dv_unit_offline( g_units[ j ] ); + } + } + dv1394d_log( LOG_DEBUG, "completed bus reset handler"); + raw1394_close( handle ); + return 0; + } + raw1394_close( handle ); + } + dv1394d_log( LOG_CRIT, "raw1394 reported zero nodes on the bus!" ); + return 0; +} + + +/** One pthread per IEEE 1394 port +*/ + +static pthread_t raw1394service_thread[4]; + +/** One raw1394 handle for each pthread/port +*/ + +static raw1394handle_t raw1394service_handle[4]; + +/** The service thread that polls raw1394 for new events. +*/ + +static void* raw1394_service( void *arg ) +{ + raw1394handle_t handle = (raw1394handle_t) arg; + struct pollfd raw1394_poll; + raw1394_poll.fd = raw1394_get_fd( handle ); + raw1394_poll.events = POLLIN; + raw1394_poll.revents = 0; + while ( 1 ) + { + if ( poll( &raw1394_poll, 1, 200) > 0 ) + { + if ( (raw1394_poll.revents & POLLIN) + || (raw1394_poll.revents & POLLPRI) ) + raw1394_loop_iterate( handle ); + } + pthread_testcancel(); + } + +} + + +/** Start the raw1394 service threads for handling bus reset. + + One thread is launched per port on the system. +*/ + +void raw1394_start_service_threads( void ) +{ + int port; + for ( port = 0; port < raw1394_get_num_ports(); port++ ) + { + raw1394service_handle[port] = raw1394_open( port ); + raw1394_set_bus_reset_handler( raw1394service_handle[port], reset_handler ); + pthread_create( &(raw1394service_thread[port]), NULL, raw1394_service, raw1394service_handle[port] ); + } + for ( ; port < 4; port++ ) + raw1394service_handle[port] = NULL; +} + +/** Shutdown all the raw1394 service threads. +*/ + +void raw1394_stop_service_threads( void ) +{ + int i; + for ( i = 0; i < 4; i++ ) + { + if ( raw1394service_handle[i] != NULL ) + { + pthread_cancel( raw1394service_thread[i] ); + pthread_join( raw1394service_thread[i], NULL ); + raw1394_close( raw1394service_handle[i] ); + } + } +} + + diff --git a/mlt/src/miracle/miracle_commands.h b/mlt/src/miracle/miracle_commands.h new file mode 100644 index 00000000..6c60d7da --- /dev/null +++ b/mlt/src/miracle/miracle_commands.h @@ -0,0 +1,52 @@ +/* + * global_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _GLOBAL_COMMANDS_H_ +#define _GLOBAL_COMMANDS_H_ + +#include +#include "dvunit.h" +#include "dvconnection.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +dv_unit dv1394d_get_unit( int ); +void dv1394d_delete_unit( int ); +void dv1394d_delete_all_units( void ); +int dv1394d_unit_status( int n, dv1394_status status, int root_offset ); +void raw1394_start_service_threads( void ); +void raw1394_stop_service_threads( void ); + +extern response_codes dv1394d_add_unit( command_argument ); +extern response_codes dv1394d_list_nodes( command_argument ); +extern response_codes dv1394d_list_units( command_argument ); +extern response_codes dv1394d_list_clips( command_argument ); +extern response_codes dv1394d_set_global_property( command_argument ); +extern response_codes dv1394d_get_global_property( command_argument ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/miracle/miracle_connection.c b/mlt/src/miracle/miracle_connection.c new file mode 100644 index 00000000..6c65e351 --- /dev/null +++ b/mlt/src/miracle/miracle_connection.c @@ -0,0 +1,250 @@ +/* + * dvconnection.c -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#include "global_commands.h" +#include "dvconnection.h" +#include "dvsocket.h" +#include "dvserver.h" +#include "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, dv_response ); +static int connection_read( int, char *, int ); +static void connection_close( int ); + +static int connection_initiate( int fd ) +{ + int error = 0; + dv_response response = dv_response_init( ); + dv_response_set_error( response, 100, "VTR Ready" ); + error = connection_send( fd, response ); + dv_response_close( response ); + return error; +} + +static int connection_send( int fd, dv_response response ) +{ + int error = 0; + int index = 0; + int code = dv_response_get_error_code( response ); + + if ( code != -1 ) + { + int items = dv_response_count( response ); + + if ( items == 0 ) + dv_response_set_error( response, 500, "Unknown error" ); + + if ( code == 200 && items > 2 ) + dv_response_set_error( response, 201, "OK" ); + else if ( code == 200 && items > 1 ) + dv_response_set_error( response, 202, "OK" ); + + code = dv_response_get_error_code( response ); + items = dv_response_count( response ); + + for ( index = 0; !error && index < items; index ++ ) + { + char *line = dv_response_get_line( response, index ); + int length = strlen( line ); + if ( length == 0 && index != dv_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( dv_response_get_line( response, items - 1 ), "" ) ) + write( fd, "\r\n", 2 ); + } + else + { + char *message = "500 Empty Response\r\n\r\n"; + write( fd, message, strlen( 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 || strncmp( command, "BYE", 3 ) == 0 ) + nchars = 0; + return nchars; +} + +int connection_status( int fd, dv1394_notifier notifier ) +{ + int error = 0; + int index = 0; + dv1394_status_t status; + char text[ 10240 ]; + dv_socket socket = dv_socket_init_fd( fd ); + + for ( index = 0; !error && index < MAX_UNITS; index ++ ) + { + dv1394_notifier_get( notifier, &status, index ); + dv1394_status_serialise( &status, text, sizeof( text ) ); + error = dv_socket_write_data( socket, text, strlen( text ) ) != strlen( text ); + } + + while ( !error ) + { + if ( dv1394_notifier_wait( notifier, &status ) == 0 ) + { + dv1394_status_serialise( &status, text, sizeof( text ) ); + error = dv_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; + } + } + + dv_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; + char address[ 512 ]; + char command[ 1024 ]; + int fd = connection->fd; + dv_parser parser = connection->parser; + dv_response response = NULL; + + /* We definitely want to ignore broken pipes. */ + signal( SIGPIPE, SIG_IGN ); + + /* 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 ); + + dv1394d_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 ) ) + { + if ( strncmp( command, "STATUS", 6 ) ) + { + response = dv_parser_execute( parser, command ); + dv1394d_log( LOG_INFO, "%s \"%s\" %d", address, command, dv_response_get_error_code( response ) ); + error = connection_send( fd, response ); + dv_response_close( response ); + } + else + { + error = connection_status( fd, dv_parser_get_notifier( parser ) ); + } + } + } + + /* Free the resources associated with this connection. */ + connection_close( fd ); + + dv1394d_log( LOG_NOTICE, "Connection with %s (%d) closed", address, fd ); + + free( connection ); + + return NULL; +} diff --git a/mlt/src/miracle/miracle_connection.h b/mlt/src/miracle/miracle_connection.h new file mode 100644 index 00000000..1c5d00b0 --- /dev/null +++ b/mlt/src/miracle/miracle_connection.h @@ -0,0 +1,91 @@ +/* + * dvconnection.h -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_CONNECTION_H_ +#define _DV_CONNECTION_H_ + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Connection structure +*/ + +typedef struct +{ + int fd; + struct sockaddr_in sin; + dv_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 +{ + dv_parser parser; + dv_response response; + dv_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/mlt/src/miracle/miracle_local.c b/mlt/src/miracle/miracle_local.c new file mode 100644 index 00000000..fce4ab22 --- /dev/null +++ b/mlt/src/miracle/miracle_local.c @@ -0,0 +1,460 @@ +/* + * dvlocal.c -- Local dv1394d Parser + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include + +/* Library header files */ +#include + +/* Application header files */ +#include +#include +#include "dvlocal.h" +#include "dvconnection.h" +#include "global_commands.h" +#include "unit_commands.h" +#include "log.h" +#include "raw1394util.h" + +/** Private dv_local structure. +*/ + +typedef struct +{ + dv_parser parser; + char root_dir[1024]; +} +*dv_local, dv_local_t; + +/** Forward declarations. +*/ + +static dv_response dv_local_connect( dv_local ); +static dv_response dv_local_execute( dv_local, char * ); +static void dv_local_close( dv_local ); +response_codes print_help( command_argument arg ); +response_codes dv1394d_run( command_argument arg ); +response_codes dv1394d_shutdown( command_argument arg ); + +/** DV Parser constructor. +*/ + +dv_parser dv_parser_init_local( ) +{ + dv_parser parser = malloc( sizeof( dv_parser_t ) ); + dv_local local = malloc( sizeof( dv_local_t ) ); + + if ( parser != NULL ) + { + memset( parser, 0, sizeof( dv_parser_t ) ); + + parser->connect = (parser_connect)dv_local_connect; + parser->execute = (parser_execute)dv_local_execute; + parser->close = (parser_close)dv_local_close; + parser->real = local; + + if ( local != NULL ) + { + memset( local, 0, sizeof( dv_local_t ) ); + local->parser = parser; + local->root_dir[0] = '/'; + } + } + return parser; +} + +/** response status code/message pair +*/ + +typedef struct +{ + int code; + 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 +} +arguments_types; + +/** A command definition. +*/ + +typedef struct +{ +/* The command string corresponding to this operation (e.g. "play") */ + 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 */ + 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", print_help, 0, ATYPE_NONE, "Display this information!"}, + {"NLS", dv1394d_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."}, + {"UADD", dv1394d_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."}, + {"ULS", dv1394d_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."}, + {"CLS", dv1394d_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."}, + {"SET", dv1394d_set_global_property, 0, ATYPE_STRING, "Set a server configuration property."}, + {"GET", dv1394d_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."}, + {"RUN", dv1394d_run, 0, ATYPE_STRING, "Run a batch file." }, + {"LIST", dv1394d_list, 1, ATYPE_NONE, "List the playlist associated to a unit."}, + {"LOAD", dv1394d_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."}, + {"INSERT", dv1394d_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."}, + {"REMOVE", dv1394d_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."}, + {"CLEAN", dv1394d_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."}, + {"MOVE", dv1394d_move, 1, ATYPE_INT, "Move a clip to another clip index."}, + {"APND", dv1394d_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."}, + {"PLAY", dv1394d_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."}, + {"STOP", dv1394d_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."}, + {"PAUSE", dv1394d_pause, 1, ATYPE_NONE, "Pause a playing clip."}, + {"REW", dv1394d_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."}, + {"FF", dv1394d_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."}, + {"STEP", dv1394d_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."}, + {"GOTO", dv1394d_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."}, + {"SIN", dv1394d_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", dv1394d_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", dv1394d_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."}, + {"USET", dv1394d_set_unit_property, 1, ATYPE_STRING, "Set a unit configuration property."}, + {"UGET", dv1394d_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."}, + {"XFER", dv1394d_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."}, + {"SHUTDOWN", dv1394d_shutdown, 0, ATYPE_NONE, "Shutdown the server."}, + {NULL, NULL, 0, ATYPE_NONE, NULL} +}; + +/** Usage message +*/ + +static char helpstr [] = + "dv1394d -- A DV over IEEE 1394 TCP Server\n" + " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n" + " Authors:\n" + " Dan Dennedy \n" + " Charles Yates \n" + "Available commands:\n"; + +/** Lookup the response message for a status code. +*/ + +inline 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 dv1394d command set +*/ + +response_codes print_help( command_argument cmd_arg ) +{ + int i = 0; + + dv_response_printf( cmd_arg->response, 10240, "%s", helpstr ); + + for ( i = 0; vocabulary[ i ].command != NULL; i ++ ) + dv_response_printf( cmd_arg->response, 1024, + "%-10.10s%s\n", + vocabulary[ i ].command, + vocabulary[ i ].help ); + + dv_response_printf( cmd_arg->response, 2, "\n" ); + + return RESPONSE_SUCCESS_N; +} + +/** Execute a batch file. +*/ + +response_codes dv1394d_run( command_argument cmd_arg ) +{ + dv_response temp = dv_parser_run( cmd_arg->parser, (char *)cmd_arg->argument ); + + if ( temp != NULL ) + { + int index = 0; + + dv_response_set_error( cmd_arg->response, + dv_response_get_error_code( temp ), + dv_response_get_error_string( temp ) ); + + for ( index = 1; index < dv_response_count( temp ); index ++ ) + dv_response_printf( cmd_arg->response, 10240, "%s\n", dv_response_get_line( temp, index ) ); + + dv_response_close( temp ); + } + + return dv_response_get_error_code( cmd_arg->response ); +} + +response_codes dv1394d_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 + dv1394d_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) ); +#else + dv1394d_log( LOG_DEBUG, "Received signal %i - shutting down.", sig ); +#endif + + exit(EXIT_SUCCESS); + } +} + +/** Local 'connect' function. +*/ + +static dv_response dv_local_connect( dv_local local ) +{ + dv_response response = dv_response_init( ); + + self = pthread_self( ); + + dv_response_set_error( response, 100, "VTR Ready" ); + + signal( SIGHUP, signal_handler ); + signal( SIGINT, signal_handler ); + signal( SIGTERM, signal_handler ); + signal( SIGSTOP, signal_handler ); + signal( SIGCHLD, SIG_IGN ); + + raw1394_reconcile_bus(); + /* Start the raw1394 service threads for handling bus resets */ + raw1394_start_service_threads(); + + return response; +} + +/** Set the error and determine the message associated to this command. +*/ + +void dv_command_set_error( command_argument cmd, response_codes code ) +{ + dv_response_set_error( cmd->response, code, get_response_msg( code ) ); +} + +/** Parse the unit argument. +*/ + +int dv_command_parse_unit( command_argument cmd, int argument ) +{ + int unit = -1; + char *string = dv_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 *dv_command_parse_argument( command_argument cmd, int argument, arguments_types type ) +{ + void *ret = NULL; + char *value = dv_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_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 dv_command_get_error( command_argument cmd ) +{ + response_codes ret = dv_response_get_error_code( cmd->response ); + if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 ) + ret = RESPONSE_SUCCESS; + return ret; +} + +/** Execute the command. +*/ + +static dv_response dv_local_execute( dv_local local, char *command ) +{ + command_argument_t cmd; + cmd.parser = local->parser; + cmd.response = dv_response_init( ); + cmd.tokeniser = dv_tokeniser_init( ); + cmd.command = command; + cmd.unit = -1; + cmd.argument = NULL; + cmd.root_dir = local->root_dir; + + /* Set the default error */ + dv_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND ); + + /* Parse the command */ + if ( dv_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 ) + { + int index = 0; + char *value = dv_tokeniser_get_string( cmd.tokeniser, 0 ); + int found = 0; + + /* Strip quotes from all tokens */ + for ( index = 0; index < dv_tokeniser_count( cmd.tokeniser ); index ++ ) + dv_util_strip( dv_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; + + dv_command_set_error( &cmd, RESPONSE_SUCCESS ); + + if ( vocabulary[ index ].is_unit ) + { + cmd.unit = dv_command_parse_unit( &cmd, position ); + if ( cmd.unit == -1 ) + dv_command_set_error( &cmd, RESPONSE_MISSING_ARG ); + position ++; + } + + if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS ) + { + cmd.argument = dv_command_parse_argument( &cmd, position, vocabulary[ index ].type ); + if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE ) + dv_command_set_error( &cmd, RESPONSE_MISSING_ARG ); + position ++; + } + + if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS ) + { + response_codes error = vocabulary[ index ].operation( &cmd ); + dv_command_set_error( &cmd, error ); + } + + free( cmd.argument ); + } + } + + dv_tokeniser_close( cmd.tokeniser ); + + return cmd.response; +} + +/** Close the parser. +*/ + +static void dv_local_close( dv_local local ) +{ + raw1394_stop_service_threads(); + dv1394d_delete_all_units(); + pthread_kill_other_threads_np(); + dv1394d_log( LOG_DEBUG, "Clean shutdown." ); + free( local ); + dv_clip_factory_close( ); + dv_frame_pool_close( ); +} diff --git a/mlt/src/miracle/miracle_local.h b/mlt/src/miracle/miracle_local.h new file mode 100644 index 00000000..fbbe4447 --- /dev/null +++ b/mlt/src/miracle/miracle_local.h @@ -0,0 +1,41 @@ +/* + * dvlocal.h -- Local dv1394d Parser + * Copyright (C) 2002-2003 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 + */ + +#ifndef _DV_LOCAL_H_ +#define _DV_LOCAL_H_ + +/* Application header files */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Local parser API. +*/ + +extern dv_parser dv_parser_init_local( ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/miracle/miracle_log.c b/mlt/src/miracle/miracle_log.c new file mode 100644 index 00000000..cdb5d82f --- /dev/null +++ b/mlt/src/miracle/miracle_log.c @@ -0,0 +1,63 @@ +/* + * log.h -- logging facility implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "log.h" + +static int log_output = log_stderr; +static int threshold = LOG_DEBUG; + +void +dv1394d_log_init( enum log_output method, int new_threshold ) +{ + log_output = method; + threshold = new_threshold; + if (method == log_syslog) + openlog( "dv1394d", LOG_CONS, LOG_DAEMON ); + +} + +void +dv1394d_log( int priority, 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/mlt/src/miracle/miracle_log.h b/mlt/src/miracle/miracle_log.h new file mode 100644 index 00000000..04505e97 --- /dev/null +++ b/mlt/src/miracle/miracle_log.h @@ -0,0 +1,43 @@ +/* + * log.h -- logging facility header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum log_output { + log_stderr, + log_syslog +}; + +void dv1394d_log_init( enum log_output method, int threshold ); +void dv1394d_log( int priority, char *format, ... ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/miracle/miracle_server.c b/mlt/src/miracle/miracle_server.c new file mode 100644 index 00000000..d4f886b9 --- /dev/null +++ b/mlt/src/miracle/miracle_server.c @@ -0,0 +1,275 @@ +/* + * dvserver.c -- DV Server + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "log.h" +#include +#include +#include + +/* Application header files */ +#include "dvserver.h" +#include "dvconnection.h" +#include "dvlocal.h" +#include "log.h" +#include +#include + +/** Initialise a server structure. +*/ + +dv_server dv_server_init( char *id ) +{ + dv_server server = malloc( sizeof( dv_server_t ) ); + if ( server != NULL ) + { + memset( server, 0, sizeof( dv_server_t ) ); + server->id = id; + server->port = DEFAULT_TCP_PORT; + server->socket = -1; + } + return server; +} + +/** Set the port of the server. +*/ + +void dv_server_set_port( dv_server server, int port ) +{ + server->port = port; +} + +void dv_server_set_proxy( dv_server server, char *proxy ) +{ + dv_tokeniser tokeniser = dv_tokeniser_init( ); + server->proxy = 1; + server->remote_port = DEFAULT_TCP_PORT; + dv_tokeniser_parse_new( tokeniser, proxy, ":" ); + strcpy( server->remote_server, dv_tokeniser_get_string( tokeniser, 0 ) ); + if ( dv_tokeniser_count( tokeniser ) == 2 ) + server->remote_port = atoi( dv_tokeniser_get_string( tokeniser, 1 ) ); + dv_tokeniser_close( tokeniser ); +} + +/** Wait for a connection. +*/ + +static int dv_server_wait_for_connect( dv_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 *dv_server_run( void *arg ) +{ + dv_server server = arg; + pthread_t cmd_parse_info; + connection_t *tmp = NULL; + pthread_attr_t thread_attributes; + int socksize; + + socksize = sizeof( struct sockaddr ); + + dv1394d_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 ); + pthread_attr_init( &thread_attributes ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_INHERIT_SCHED ); + /* pthread_attr_setschedpolicy( &thread_attributes, SCHED_RR ); */ + + while ( !server->shutdown ) + { + /* Wait for a new connection. */ + if ( dv_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->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 ); + } + } + + dv1394d_log( LOG_NOTICE, "%s version %s server terminated.", server->id, VERSION ); + + return NULL; +} + +/** Execute the server thread. +*/ + +int dv_server_execute( dv_server server ) +{ + int error = 0; + dv_response response = NULL; + int index = 0; + struct sockaddr_in ServerAddr; + int flag = 1; + + 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" ); + dv1394d_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" ); + dv1394d_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" ); + dv1394d_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 ) + { + dv1394d_log( LOG_NOTICE, "Starting server on %d.", server->port ); + server->parser = dv_parser_init_local( ); + } + else + { + dv1394d_log( LOG_NOTICE, "Starting proxy for %s:%d on %d.", server->remote_server, server->remote_port, server->port ); + server->parser = dv_parser_init_remote( server->remote_server, server->remote_port ); + } + + response = dv_parser_connect( server->parser ); + + if ( response != NULL && dv_response_get_error_code( response ) == 100 ) + { + /* read configuration file */ + if ( response != NULL && !server->proxy ) + { + dv_response_close( response ); + response = dv_parser_run( server->parser, "/etc/dv1394d.conf" ); + + if ( dv_response_count( response ) > 1 ) + { + if ( dv_response_get_error_code( response ) > 299 ) + dv1394d_log( LOG_ERR, "Error evaluating server configuration. Processing stopped." ); + for ( index = 0; index < dv_response_count( response ); index ++ ) + dv1394d_log( LOG_DEBUG, "%4d: %s", index, dv_response_get_line( response, index ) ); + } + } + + if ( response != NULL ) + { + pthread_attr_t attr; + int result; + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setschedpolicy( &attr, SCHED_FIFO ); + pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); + dv_response_close( response ); + result = pthread_create( &server->thread, &attr, dv_server_run, server ); + if ( result ) + { + dv1394d_log( LOG_WARNING, "Failed to schedule realtime (%s)", strerror(errno) ); + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); + result = pthread_create( &server->thread, &attr, dv_server_run, server ); + if ( result ) + { + dv1394d_log( LOG_CRIT, "Failed to launch TCP listener thread" ); + error = -1; + } + } + } + } + else + { + dv1394d_log( LOG_ERR, "Error connecting to parser. Processing stopped." ); + server->shutdown = 1; + error = -1; + } + + return error; +} + +/** Shutdown the server. +*/ + +void dv_server_shutdown( dv_server server ) +{ + if ( server != NULL && !server->shutdown ) + { + server->shutdown = 1; + pthread_join( server->thread, NULL ); + dv_parser_close( server->parser ); + close( server->socket ); + } +} diff --git a/mlt/src/miracle/miracle_server.h b/mlt/src/miracle/miracle_server.h new file mode 100644 index 00000000..96b22cee --- /dev/null +++ b/mlt/src/miracle/miracle_server.h @@ -0,0 +1,70 @@ +/* + * dvserver.h -- DV Server + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_SERVER_H_ +#define _DV_SERVER_H_ + +/* System header files */ +#include + +/* Application header files */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Servers default port +*/ + +#define DEFAULT_TCP_PORT 5250 + +/** Structure for the server +*/ + +typedef struct +{ + char *id; + int port; + int socket; + dv_parser parser; + pthread_t thread; + int shutdown; + int proxy; + char remote_server[ 50 ]; + int remote_port; +} +*dv_server, dv_server_t; + +/** API for the server +*/ + +extern dv_server dv_server_init( char * ); +extern void dv_server_set_port( dv_server, int ); +extern void dv_server_set_proxy( dv_server, char * ); +extern int dv_server_execute( dv_server ); +extern void dv_server_shutdown( dv_server ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/miracle/miracle_unit.c b/mlt/src/miracle/miracle_unit.c new file mode 100644 index 00000000..bc8adcfb --- /dev/null +++ b/mlt/src/miracle/miracle_unit.c @@ -0,0 +1,1100 @@ +/* + * dvunit.c -- DV Transmission Unit Implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dvunit.h" +#include "dvframe.h" +#include "dvframepool.h" +#include "dvqueue.h" +#include "dvpump.h" +#include "dverror.h" +#include "dvplayer.h" +#include "raw1394util.h" +#include "log.h" +#include "dvlocal.h" + +/* Forward references */ +static void dv_unit_status_communicate( dv_unit ); + +/** dv1394 device file names based upon devfs default names. */ + +static char *devices[4][4] = { + { + "/dev/ieee1394/dv/host0/NTSC/in", + "/dev/ieee1394/dv/host0/NTSC/out", + "/dev/ieee1394/dv/host0/PAL/in", + "/dev/ieee1394/dv/host0/PAL/out", + },{ + "/dev/ieee1394/dv/host1/NTSC/in", + "/dev/ieee1394/dv/host1/NTSC/out", + "/dev/ieee1394/dv/host1/PAL/in", + "/dev/ieee1394/dv/host1/PAL/out" + },{ + "/dev/ieee1394/dv/host2/NTSC/in", + "/dev/ieee1394/dv/host2/NTSC/out", + "/dev/ieee1394/dv/host2/PAL/in", + "/dev/ieee1394/dv/host2/PAL/out" + },{ + "/dev/ieee1394/dv/host3/NTSC/in", + "/dev/ieee1394/dv/host3/NTSC/out", + "/dev/ieee1394/dv/host3/PAL/in", + "/dev/ieee1394/dv/host3/PAL/out" + } +}; + +static int device_count[4] = {0,0,0,0}; + +/** Allocate a new DV transmission unit. + + \param dv1394d_fd The file descriptor of a dv1394 device file to + use for transmission. + \param guid The node GUID of the receiving device. + \param channel The channel to use for transmission. + \return A new dv_unit handle. +*/ + +dv_unit dv_unit_init( octlet_t guid, int channel ) +{ + dv_unit unit = malloc( sizeof( dv_unit_t ) ); + if ( unit != NULL ) + { + int node_id; + + memset( unit, 0, sizeof( dv_unit_t ) ); + unit->guid = guid; + unit->buffer_size = 25; + unit->is_terminated = 1; + unit->channel = channel; + unit->dv1394_fd = -1; + unit->n_frames = DV1394_MAX_FRAMES / 2; + unit->n_fill = 1; + + /* get a raw1394 handle for plug control */ + if ( ( node_id = raw1394_find_node( &(unit->raw1394), guid ) ) != -1 ) + { + if ( dv_unit_online( unit ) == 1 ) + dv1394d_log( LOG_DEBUG, "Added online unit with GUID %08x%08x", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + else + { + dv_unit_close( unit ); + unit = NULL; + } + } + else + { + dv1394d_log( LOG_DEBUG, "Added offline unit with GUID %08x%08x", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + } + } + return unit; +} + +/** Allow stdin to feed the unit (redundant now that senddv has been dropped). +*/ + +void dv_unit_allow_stdin( dv_unit unit, int flag ) +{ + unit->allow_stdin = flag; +} + +/** Override the default buffer/pump size - this must be done prior to the pumps + creation. +*/ + +void dv_unit_set_buffer_size( dv_unit unit, int size ) +{ + if ( size > 0 ) + { + if ( unit->pump == NULL ) + unit->buffer_size = size; + else + unit->buffer_size = dv_pump_resize( unit->pump, size ); + } +} + +int dv_unit_get_buffer_size( dv_unit unit ) +{ + return unit->buffer_size; +} + +void dv_unit_set_n_frames( dv_unit unit, int size ) +{ + if ( size > 0 && size <= DV1394_MAX_FRAMES / 2 ) + unit->n_frames = size; +} + +int dv_unit_get_n_frames( dv_unit unit ) +{ + return unit->n_frames; +} + +void dv_unit_set_n_fill( dv_unit unit, int size ) +{ + unit->n_fill = size; +} + +int dv_unit_get_n_fill( dv_unit unit ) +{ + return unit->n_fill; +} + +/** Set the notifier info +*/ + +void dv_unit_set_notifier( dv_unit this, dv1394_notifier notifier, char *root_dir ) +{ + this->notifier = notifier; + this->root_dir = root_dir; + dv_unit_status_communicate( this ); +} + +/** Communicate the current status to all threads waiting on the notifier. +*/ + +static void dv_unit_status_communicate( dv_unit unit ) +{ + if ( unit != NULL && unit->notifier != NULL && unit->root_dir != NULL ) + { + dv1394_status_t status; + if ( dv_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 ) ) + dv1394_notifier_put( unit->notifier, &status ); + } +} + +/** Load a clip into the unit clearing existing play list. + + \todo error handling + \param unit A dv_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) +*/ + +dv_error_code dv_unit_load( dv_unit unit, const char *clip, long in, long out, int flush ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_replace_file( player, (char*) clip, in, out, flush ); + dv1394d_log( LOG_DEBUG, "loaded clip %s", clip ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_insert( dv_unit unit, const char *clip, int index, long in, long out ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_insert_file( player, (char*) clip, index, in, out ); + dv1394d_log( LOG_DEBUG, "inserted clip %s", clip ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_remove( dv_unit unit, int index ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_remove_clip( player, index ); + dv1394d_log( LOG_DEBUG, "removed clip %d", index ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_clean( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_clean( player ); + dv1394d_log( LOG_DEBUG, "Cleaned playlist" ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_move( dv_unit unit, int src, int dest ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_move_clip( player, src, dest ); + dv1394d_log( LOG_DEBUG, "moved clip %d to %d", src, dest ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +/** Add a clip to the unit play list. + + \todo error handling + \param unit A dv_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) +*/ + +dv_error_code dv_unit_append( dv_unit unit, const char *clip, long in, long out ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_add_file( player, (char*) clip, in, out ); + dv_unit_status_communicate( unit ); + return error; +} + +void *output_cleanup( void *arg ) +{ + dv_unit unit = arg; + if ( unit != NULL && unit->mmap != NULL ) + { + unit->is_terminated = 1; + dv_unit_status_communicate( unit ); + munmap( unit->mmap, unit->mmap_length ); + /* this actually stops transmission as opposed to allowing the + last frame to loop in the OHCI DMA context. */ + ioctl( unit->dv1394_fd, DV1394_SHUTDOWN, NULL ); + } + + return NULL; +} + +/** The dv1394 transmission thread. + + \param arg A dv_unit handle. +*/ + +static void *output( void *arg ) +{ + dv_unit unit = arg; + dv_frame frames[ DV1394_MAX_FRAMES ]; + int frames_dropped = 0; /* count of total frames dropped (repeated) */ + struct dv1394_status status; + char errstr[64]; + int n_fill = unit->n_fill; + int n_frames = unit->n_frames; + + /* Determine the number of frames to wait for/fill on each iteration */ + if ( n_fill < 1 ) + n_fill = 1; + else if ( n_fill > unit->n_frames ) + n_fill = n_frames / 2; + + unit->mmap = mmap( NULL,unit->mmap_length,PROT_WRITE,MAP_SHARED,unit->dv1394_fd,0 ); + if ( unit->mmap == MAP_FAILED || unit->mmap == NULL ) + { + perror( "mmap" ); + return NULL; + } + + pthread_cleanup_push( output_cleanup, (void *)arg ); + + while ( dv_pump_get_available_output_count( unit->pump ) || + !( dv_unit_has_terminated( unit ) || dv_pump_has_terminated( unit->pump) ) ) + { + int available = 0; + + if ( ioctl( unit->dv1394_fd, DV1394_WAIT_FRAMES, n_fill ) < 0) + perror( "DV1394_WAIT_FRAMES" ); + + pthread_testcancel(); + + /* update the status for the next iteration and detect dropped frames */ + if ( ioctl( unit->dv1394_fd, DV1394_GET_STATUS, &status ) >= 0) + { + pthread_testcancel(); + + /* + printf( "dv1394 status: active=%02d, #clear=%02d, first clear=%02d\n", + status.active_frame, status.n_clear_frames, status.first_clear_frame); + */ + + /* report dropped frames */ + if( status.dropped_frames > 0 ) + { + frames_dropped += status.dropped_frames; + dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames with %d available.", + status.dropped_frames, dv_pump_get_available_output_count( unit->pump ) ); + } + + available = dv_pump_get_output_block( unit->pump, (void **)frames, n_fill ); + + dv_unit_status_communicate( unit ); + + /* The only time we get 0 frames is when the unit is being stopped. */ + if ( available != 0 ) + { + int size = dv_frame_size( frames[ 0 ] ); + int pos = status.first_clear_frame; + int index = 0; + + for ( index = 0; index < available; index ++ ) + memcpy( unit->mmap + ( ( pos + index ) % n_frames ) * size, dv_frame_data( frames[ index ] ), size ); + + if ( ioctl( unit->dv1394_fd, DV1394_SUBMIT_FRAMES, available ) >= 0) + { + for ( index = 0; index < available - 1; index ++ ) + { + dv_frame_clear_error( frames[ index ] ); + dv_frame_id_clear( dv_frame_get_id( frames[ index ] ) ); + } + dv_pump_return_output_block( unit->pump ); + pthread_testcancel(); + } + else + { + dv1394d_log( LOG_ERR, "failed to write frames to dv1394: %s.", strerror_r( errno, errstr, 63 ) ); + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + pthread_testcancel(); + } + } + } + else + { + dv1394d_log( LOG_ERR, "failed to get dv1394 status: %s.", strerror_r( errno, errstr, 63 ) ); + dv_pump_return_used_output( unit->pump ); + } + } + + if ( frames_dropped > 0 ) + dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames total during this transmission.", frames_dropped ); + + pthread_cleanup_pop( 1 ); + + return NULL; +} + +/** Start playing the clip. + + Start a dv-pump and commence dv1394 transmission. + + \todo error handling + \param unit A dv_unit handle. + \param speed An integer that specifies the playback rate as a + percentage multiplied by 100. +*/ + +void dv_unit_play( dv_unit_t *unit, int speed ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + + if ( unit->is_terminated == 1 && ( dv_player_get_total_frames( player ) > 0 || unit->allow_stdin ) ) + { + int retval; + dv_frame frame = NULL; + struct dv1394_init setup = + { + api_version: DV1394_API_VERSION, + channel: unit->channel, + /* this only sets the *requested* size of the ringbuffer, + in frames */ + n_frames: unit->n_frames, + /* we set the format later */ + cip_n: unit->dv1394_cip_n, + cip_d: unit->dv1394_cip_d, + syt_offset: unit->dv1394_syt_offset + }; + pthread_attr_t attr; + + if ( unit->in == NULL ) + { + if ( !unit->allow_stdin || dv_player_get_total_frames( player ) != 0 ) + unit->in = dv_player_get_dv_input( player ); + else + unit->in = dv_input_init( unit->pump ); + } + else + { + dv_input_join_thread( unit->in ); + pthread_join( unit->out, NULL ); + } + + unit->is_terminated = 0; + dv_pump_restart( unit->pump ); + dv_input_start_thread( unit->in ); + dv_player_set_speed( player, (double) speed/1000.0 ); + + /* first we read a little data to see if this is PAL or NTSC + so we can initialize dv1394 properly */ + frame = dv_pump_get_available_output( unit->pump ); + + /* initialize dv1394 */ + setup.format = dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC; + + retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup ); + if (retval < 0) + { + perror( "DV1394_INIT" ); + return; + } + + unit->mmap_length = unit->n_frames * dv_frame_size( frame ); + + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED ); + pthread_create( &unit->out, &attr, output, unit ); + } + else + { + dv_player_set_speed( player, (double) speed/1000.0 ); + } + dv_unit_status_communicate( unit ); +} + +/** Stop playback. + + Terminates the dv_pump and halts dv1394 transmission. + + \param unit A dv_unit handle. +*/ + +void dv_unit_terminate( dv_unit unit ) +{ + unit->is_terminated = 1; + if ( unit->pump != NULL ) + { + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + } +} + +/** Query the status of unit playback. + + \param unit A dv_unit handle. + \return 1 if the unit is not playing, 0 if playing. +*/ + +int dv_unit_has_terminated( dv_unit unit ) +{ + return unit->is_terminated; +} + +/** Get the dv_player from the dv_unit. + + \param unit A dv_unit handle. + \return A dv_player handle. +*/ + +dv_player dv_unit_get_dv_player( dv_unit unit ) +{ + if ( unit != NULL ) + { + if ( unit->pump == NULL ) + { + unit->pump = dv_pump_init( unit->buffer_size ); + if ( unit->pump != NULL ) + unit->player = dv_player_init( unit->pump ); + } + return unit->player; + } + return NULL; +} + + +/** Transfer the currently loaded clip to another unit +*/ + +int dv_unit_transfer( dv_unit dest_unit, dv_unit src_unit ) +{ + dv_player src_player = dv_unit_get_dv_player( src_unit ); + dv_player dest_player = dv_unit_get_dv_player( dest_unit ); + + if( dest_player != NULL && src_player != NULL ) + dv_player_replace_player( dest_player, src_player ); + + return 0; +} + +/** Get the guid associated to this unit. +*/ + +octlet_t dv_unit_get_guid( dv_unit unit ) +{ + return unit->guid; +} + +/** Get the node id associated to this unit. +*/ + +int dv_unit_get_nodeid( dv_unit unit ) +{ + return (unit->node_id & 0x3f); +} + +/** Get the channel associated to this unit. +*/ + +int dv_unit_get_channel( dv_unit unit ) +{ + return (unit->channel); +} + +/** Turn unit online. +*/ + +int dv_unit_online( dv_unit unit ) +{ + int result = 0; + int port, node_id; + + if ( unit->raw1394 != NULL ) + raw1394_close( unit->raw1394 ); + + node_id = raw1394_find_node( &(unit->raw1394), unit->guid ); + if ( node_id != -1 ) + { + unit->node_id = 0xffc0 | node_id; + port = dv_unit_get_port( unit ); + + unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR ); + if ( unit->dv1394_fd < 0 ) + { + dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] ); + dv_unit_close( unit ); + } + else + { + device_count[ port ] ++; + if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) ) + { + avc1394_vcr_record( unit->raw1394, unit->node_id ); + unit->online = 1; + dv_unit_status_communicate( unit ); + result = 1; + } + } + } + + return result; +} + +/** Turn unit offline. +*/ + +void dv_unit_offline( dv_unit unit ) +{ + if ( unit->online == 1 ) + { + if ( unit->is_terminated == 0 ) + dv_unit_terminate( unit ); + unit->online = 0; + if ( unit->raw1394 != NULL ) + { + avc1394_vcr_stop( unit->raw1394, unit->node_id ); + break_p2p_connection( unit->raw1394, unit->node_id, unit->channel ); + } + if ( unit->dv1394_fd > -1 ) + { + close( unit->dv1394_fd ); + device_count[ dv_unit_get_port( unit ) ] --; + } + dv_unit_status_communicate( unit ); + dv1394d_log( LOG_DEBUG, "Unit with GUID %08x%08x is now offline.", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + } +} + +/** Determine if unit is offline. +*/ + +int dv_unit_is_offline( dv_unit unit ) +{ + return (unit->online == 0); +} + +/** Obtain the status for a given unit +*/ + +int dv_unit_get_status( dv_unit unit, dv1394_status status ) +{ + int error = -1; + + memset( status, 0, sizeof( dv1394_status_t ) ); + + if ( unit != NULL ) + { + dv_player player = dv_unit_get_dv_player( unit ); + + error = 0; + + if ( player != NULL ) + { + dv_frame head = dv_pump_get_head( player->pump ); + dv_frame tail = dv_pump_get_tail( player->pump ); + + status->speed = (int)( dv_player_get_speed( player ) * 1000.0 ); + status->fps = dv_player_frames_per_second( player, 0 ); + + if ( head != NULL ) + { + dv_frame_id id = dv_frame_get_id( head ); + if ( id->resource != NULL ) + { + const char *resource = id->resource; + if ( resource != NULL && unit->root_dir != NULL ) + resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' ); + strncpy( status->clip, resource, sizeof( status->clip ) ); + } + else + { + char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir ); + if ( title != NULL ) + strncpy( status->clip, title, sizeof( status->clip ) ); + } + + status->position = id->relative; + status->in = id->in; + status->out = id->out; + status->length = id->length; + status->seek_flag = id->seek_flag; + } + else + { + char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir ); + if ( title != NULL ) + strncpy( status->clip, title, sizeof( status->clip ) ); + } + + if ( tail != NULL ) + { + dv_frame_id id = dv_frame_get_id( tail ); + const char *resource = id->resource; + if ( resource != NULL && unit->root_dir != NULL ) + resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' ); + if ( resource != NULL ) + strncpy( status->tail_clip, resource, sizeof( status->clip ) ); + status->tail_position = id->relative; + status->tail_in = id->in; + status->tail_out = id->out; + status->tail_length = id->length; + } + + status->generation = player->generation; + status->clip_index = dv_unit_get_current_clip( unit ); + } + + if ( dv_unit_is_offline( unit ) ) + status->status = unit_offline; + else if ( !strcmp( status->clip, "" ) ) + status->status = unit_not_loaded; + else if ( dv_unit_has_terminated( unit ) ) + status->status = unit_stopped; + else if ( status->speed == 0 ) + status->status = unit_paused; + else + status->status = unit_playing; + } + else + { + status->status = unit_undefined; + } + + status->unit = unit->unit; + + return error; +} + +/** Change position in the playlist. +*/ + +void dv_unit_change_position( dv_unit unit, int clip, long position ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_set_clip_position( player, clip, position ); + dv_unit_status_communicate( unit ); +} + +/** Change speed. +*/ + +void dv_unit_change_speed( dv_unit unit, int speed ) +{ + if ( dv_unit_has_terminated( unit ) ) + dv_unit_change_position( unit, 0, 0 ); + else + dv_unit_play( unit, speed ); +} + +int dv_unit_get_current_clip( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + unsigned long position = dv_player_get_position( player ); + return dv_player_get_clip_containing( player, position ); +} + +/** Set a clip's in point +*/ + +int dv_unit_set_clip_in( dv_unit unit, int index, long position ) +{ + int error = 0; + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_unit_change_speed( unit, 0 ); + if ( dv_player_set_in_point( player, index, (unsigned long) position ) == position ) + dv_player_set_clip_position( player, index, position ); + else + error = -2; + } + else + { + error = -1; + } + + dv_unit_status_communicate( unit ); + + return error; + +} + +/** Set a clip's out point. +*/ + +int dv_unit_set_clip_out( dv_unit unit, int index, long position ) +{ + int error = 0; + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_unit_change_speed( unit, 0 ); + if ( dv_player_set_out_point( player, index, position ) == position ) + dv_player_set_clip_position( player, index, position ); + else + error = -2; + } + else + { + error = -1; + } + + dv_unit_status_communicate( unit ); + + return error; +} + +/** Step by specified position. +*/ + +void dv_unit_step( dv_unit unit, int offset ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_change_position( player, dv_seek_relative, offset ); +} + +/** Set the unit's clip mode regarding in and out points. +*/ + +void dv_unit_set_mode( dv_unit unit, dv_player_clip_mode mode ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + if ( player != NULL ) + dv_player_set_clip_mode( player, mode ); + dv_unit_status_communicate( unit ); +} + +/** Get the unit's clip mode regarding in and out points. +*/ + +dv_player_clip_mode dv_unit_get_mode( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + return dv_player_get_clip_mode( player ); +} + +/** Set the unit's clip mode regarding eof handling. +*/ + +void dv_unit_set_eof_action( dv_unit unit, dv_player_eof_action action ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_set_eof_action( player, action ); + dv_unit_status_communicate( unit ); +} + +/** Get the unit's clip mode regarding eof handling. +*/ + +dv_player_eof_action dv_unit_get_eof_action( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + return dv_player_get_eof_action( player ); +} + +/** Release the unit + + \todo error handling + \param unit A dv_unit handle. +*/ + +void dv_unit_close( dv_unit unit ) +{ + if ( unit != NULL ) + { + dv1394d_log( LOG_DEBUG, "closing unit..." ); + dv_unit_offline( unit ); + if ( unit->pump != NULL ) + { + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + dv_pump_return_used_output( unit->pump ); + dv_input_join_thread( unit->in ); + if ( !unit->is_terminated ) + pthread_join( unit->out, NULL ); + dv_pump_close( unit->pump ); + unit->pump = NULL; + } + raw1394_close( unit->raw1394 ); + free( unit ); + dv1394d_log( LOG_DEBUG, "... unit closed." ); + } +} + +/** Get the raw1394 port associated to this unit. +*/ + +int dv_unit_get_port( dv_unit unit ) +{ + if ( unit->raw1394 != NULL ) + return (int) raw1394_get_userdata( unit->raw1394 ); + else + return -1; +} + +/** Set the dv1394 file descriptor for the unit. +*/ + +void dv_unit_set_dv1394_fd( dv_unit unit, int fd ) +{ + unit->dv1394_fd = fd; +} + +/** Get the dv1394 syt_offset (timestamp latency) property. +*/ + +unsigned int dv_unit_get_syt_offset( dv_unit unit ) +{ + return unit->dv1394_syt_offset; +} + +/** Get the dv1394 cip_n (timing numerator) property. +*/ + +unsigned int dv_unit_get_cip_n( dv_unit unit ) +{ + return unit->dv1394_cip_n; +} + +/** Get the dv1394 cip_d (timing denominator) property. +*/ + +unsigned int dv_unit_get_cip_d( dv_unit unit ) +{ + return unit->dv1394_cip_d; +} + +/** Set the dv1394 syt_offset (timestamp latency) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_syt_offset( dv_unit unit, unsigned int syt_offset ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_syt_offset = syt_offset; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Set the dv1394 cip_n (timing numerator) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_cip_n( dv_unit unit, unsigned int cip_n ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_cip_n = cip_n; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Set the dv1394 cip_d (timing denominator) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_cip_d( dv_unit unit, unsigned int cip_d ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_cip_d = cip_d; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Terminate, but only the output thread and close dv1394. +*/ + +void dv_unit_suspend( dv_unit unit ) +{ + if ( unit->is_terminated == 0 ) + { + unit->is_terminated = 1; + unit->is_suspended = 1; + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + pthread_cancel( unit->out ); + } + if ( unit->dv1394_fd > -1 ) + { + close( unit->dv1394_fd ); + device_count[ dv_unit_get_port( unit ) ] --; + } + unit->dv1394_fd = -1; + dv_unit_status_communicate( unit ); +} + + +/** Restore unit on the bus, re-open dv1394, start playback if pump is running. +*/ + +void dv_unit_restore( dv_unit unit ) +{ + int result = 0; + int port, node_id; + + if ( unit->raw1394 != NULL ) + raw1394_close( unit->raw1394 ); + + node_id = raw1394_find_node( &(unit->raw1394), unit->guid ); + if ( node_id != -1 ) + { + unit->node_id = 0xffc0 | node_id; + port = dv_unit_get_port( unit ); + + unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR ); + if ( unit->dv1394_fd < 0 ) + { + dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] ); + dv_unit_close( unit ); + } + else + { + device_count[ port ] ++; + break_p2p_connection( unit->raw1394, unit->node_id, unit->channel ); + if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) ) + { + avc1394_vcr_record( unit->raw1394, unit->node_id ); + unit->online = 1; + result = 1; + } + } + } + if ( unit->is_suspended == 1 ) + { + int retval; + dv_frame frame = dv_pump_get_available_output( unit->pump ); + struct dv1394_init setup = + { + api_version: DV1394_API_VERSION, + channel: unit->channel, + /* this only sets the *requested* size of the ringbuffer, + in frames */ + n_frames: unit->n_frames, + format: dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC, + cip_n: unit->dv1394_cip_n, + cip_d: unit->dv1394_cip_d, + syt_offset: unit->dv1394_syt_offset + }; + pthread_attr_t attr; + + dv_input_join_thread( unit->in ); + unit->is_terminated = 0; + unit->is_suspended = 0; + dv_pump_restart( unit->pump ); + dv_input_start_thread( unit->in ); + + /* initialize dv1394 */ + retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup ); + if ( retval < 0 ) + return; + + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED ); + /* pthread_attr_setschedpolicy( &attr, SCHED_RR ); */ + pthread_create( &unit->out, &attr, output, unit ); + } + dv_unit_status_communicate( unit ); +} diff --git a/mlt/src/miracle/miracle_unit.h b/mlt/src/miracle/miracle_unit.h new file mode 100644 index 00000000..54338182 --- /dev/null +++ b/mlt/src/miracle/miracle_unit.h @@ -0,0 +1,122 @@ +/* + * dvunit.h -- DV Transmission Unit Header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_UNIT_H_ +#define _DV_UNIT_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct +{ + int unit; + dv_pump pump; + dv_player player; + dv_input in; + int dv1394_fd; + int is_terminated; + int is_suspended; + pthread_t out; + int channel; + nodeid_t node_id; + octlet_t guid; + raw1394handle_t raw1394; + int allow_stdin; + int buffer_size; + int online; + dv1394_notifier notifier; + char *root_dir; + unsigned int dv1394_syt_offset; + unsigned int dv1394_cip_n; + unsigned int dv1394_cip_d; + unsigned int n_frames; + unsigned int n_fill; + uint8_t *mmap; + int mmap_pos; + int mmap_length; +} dv_unit_t, *dv_unit; + +extern dv_unit dv_unit_init( octlet_t guid, int channel ); +extern void dv_unit_allow_stdin( dv_unit unit, int flag ); +extern void dv_unit_set_buffer_size( dv_unit unit, int size ); +extern int dv_unit_get_buffer_size( dv_unit unit ); +extern void dv_unit_set_n_frames( dv_unit unit, int size ); +extern int dv_unit_get_n_frames( dv_unit unit ); +extern void dv_unit_set_n_fill( dv_unit unit, int size ); +extern int dv_unit_get_n_fill( dv_unit unit ); +extern dv_error_code dv_unit_load( dv_unit unit, const char *clip, long in, long out, int flush ); +extern dv_error_code dv_unit_insert( dv_unit unit, const char *clip, int index, long in, long out ); +extern dv_error_code dv_unit_append( dv_unit unit, const char *clip, long in, long out ); +extern dv_error_code dv_unit_remove( dv_unit unit, int index ); +extern dv_error_code dv_unit_clean( dv_unit unit ); +extern dv_error_code dv_unit_move( dv_unit unit, int src, int dest ); +extern int dv_unit_transfer( dv_unit dest_unit, dv_unit src_unit ); +extern void dv_unit_play( dv_unit_t *unit, int speed ); +extern void dv_unit_terminate( dv_unit ); +extern int dv_unit_has_terminated( dv_unit ); +extern octlet_t dv_unit_get_guid( dv_unit unit ); +extern int dv_unit_get_nodeid( dv_unit unit ); +extern int dv_unit_get_channel( dv_unit unit ); +extern int dv_unit_online( dv_unit unit ); +extern void dv_unit_offline( dv_unit unit ); +extern int dv_unit_is_offline( dv_unit unit ); +extern void dv_unit_set_notifier( dv_unit, dv1394_notifier, char * ); +extern int dv_unit_get_status( dv_unit, dv1394_status ); +extern void dv_unit_change_position( dv_unit, int, long position ); +extern void dv_unit_change_speed( dv_unit unit, int speed ); +extern int dv_unit_set_clip_in( dv_unit unit, int index, long position ); +extern int dv_unit_set_clip_out( dv_unit unit, int index, long position ); +extern void dv_unit_set_mode( dv_unit unit, dv_player_clip_mode mode ); +extern dv_player_clip_mode dv_unit_get_mode( dv_unit unit ); +extern void dv_unit_set_eof_action( dv_unit unit, dv_player_eof_action mode ); +extern dv_player_eof_action dv_unit_get_eof_action( dv_unit unit ); +extern void dv_unit_step( dv_unit unit, int offset ); +extern void dv_unit_close( dv_unit unit ); +extern int dv_unit_get_port( dv_unit unit ); +extern void dv_unit_set_dv1394_fd( dv_unit unit, int fd ); +extern unsigned int dv_unit_get_syt_offset( dv_unit unit ); +extern unsigned int dv_unit_get_cip_n( dv_unit unit ); +extern unsigned int dv_unit_get_cip_d( dv_unit unit ); +extern void dv_unit_set_syt_offset( dv_unit unit, unsigned int ); +extern void dv_unit_set_cip_n( dv_unit unit, unsigned int ); +extern void dv_unit_set_cip_d( dv_unit unit, unsigned int ); +extern void dv_unit_suspend( dv_unit ); +extern void dv_unit_restore( dv_unit ); +extern dv_player dv_unit_get_dv_player( dv_unit ); +extern int dv_unit_get_current_clip( dv_unit ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/miracle/miracle_unit_commands.c b/mlt/src/miracle/miracle_unit_commands.c new file mode 100644 index 00000000..b12f2c72 --- /dev/null +++ b/mlt/src/miracle/miracle_unit_commands.c @@ -0,0 +1,612 @@ +/* + * unit_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "dvunit.h" +#include "global_commands.h" +#include "dverror.h" +#include "dvframepool.h" +#include "log.h" + +int dv1394d_load( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + int flush = 1; + + if ( filename[0] == '!' ) + { + flush = 0; + filename ++; + } + + if ( 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; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 5 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 3 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + } + if ( dv_unit_load( unit, fullname, in, out, flush ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_list( command_argument cmd_arg ) +{ + int i = 0; + dv_unit unit = dv1394d_get_unit( cmd_arg->unit ); + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", player->generation ); + + for ( i = 0; i < dv_player_get_clip_count( player ); i ++ ) + { + dv_clip clip = dv_player_get_clip( player, i ); + + dv_response_printf( cmd_arg->response, 10240, + "%d \"%s\" %d %d %d %d %.2f\n", + i, + dv_clip_get_resource( clip, cmd_arg->root_dir ), + dv_clip_get_in( clip ), + ( !dv_clip_is_seekable( clip ) && clip->out_frame == -1 ? -1 : dv_clip_get_out( clip ) ), + dv_clip_get_max_frames( clip ), + ( !dv_clip_is_seekable( clip ) && clip->out_frame == -1 ? -1 : dv_player_get_length_of_clip( player, i ) ), + dv_clip_frames_per_second( clip ) ); + } + + dv_response_printf( cmd_arg->response, 2, "\n" ); + + return RESPONSE_SUCCESS; + } + return RESPONSE_INVALID_UNIT; +} + +static int parse_clip( command_argument cmd_arg, int arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = dv_unit_get_current_clip( unit ); + + if ( dv_tokeniser_count( cmd_arg->tokeniser ) > arg ) + { + dv_player player = dv_unit_get_dv_player( unit ); + char *token = dv_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 ); + if ( clip < 0 ) + clip = 0; + if ( clip >= player->size ) + clip = player->size - 1; + } + + return clip; +} + +int dv1394d_insert( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + + if ( 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 ( dv_tokeniser_count( cmd_arg->tokeniser ) == 6 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 5 ) ); + } + + switch( dv_unit_insert( unit, fullname, index, in, out ) ) + { + case dv_pump_ok: + return RESPONSE_SUCCESS; + case dv_pump_too_many_files_open: + return RESPONSE_TOO_MANY_FILES; + default: + return RESPONSE_BAD_FILE; + } + } + return RESPONSE_SUCCESS; +} + +int dv1394d_remove( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + int index = parse_clip( cmd_arg, 2 ); + + if ( dv_unit_remove( unit, index ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_clean( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + if ( dv_unit_clean( unit ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_move( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if ( unit != NULL ) + { + if ( dv_tokeniser_count( cmd_arg->tokeniser ) > 2 ) + { + int src = parse_clip( cmd_arg, 2 ); + int dest = parse_clip( cmd_arg, 3 ); + + if ( dv_unit_move( unit, src, dest ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + else + { + return RESPONSE_MISSING_ARG; + } + } + else + { + return RESPONSE_INVALID_UNIT; + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_append( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + + if ( 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; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 5 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 3 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + } + switch ( dv_unit_append( unit, fullname, in, out ) ) + { + case dv_pump_ok: + return RESPONSE_SUCCESS; + case dv_pump_too_many_files_open: + return RESPONSE_TOO_MANY_FILES; + default: + return RESPONSE_BAD_FILE; + } + } + return RESPONSE_SUCCESS; +} + +int dv1394d_play( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int speed = 1000; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 3 ) + speed = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 2 ) ); + dv_unit_play( unit, speed ); + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_stop( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_terminate( unit ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_pause( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_play( unit, 0 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_rewind( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_speed( unit, -2000 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_step( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + dv_unit_play( unit, 0 ); + dv_unit_step( unit, *(int*) cmd_arg->argument ); + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_goto( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_position( unit, clip, *(int*) cmd_arg->argument ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_ff( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_speed( unit, 2000 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_set_in_point( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int position = *(int *) cmd_arg->argument; + + switch( dv_unit_set_clip_in( unit, clip, position ) ) + { + case -1: + return RESPONSE_BAD_FILE; + case -2: + return RESPONSE_OUT_OF_RANGE; + } + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_set_out_point( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int position = *(int *) cmd_arg->argument; + + switch( dv_unit_set_clip_out( unit, clip, position ) ) + { + case -1: + return RESPONSE_BAD_FILE; + case -2: + return RESPONSE_OUT_OF_RANGE; + } + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_get_unit_status( command_argument cmd_arg ) +{ + dv1394_status_t status; + int error = dv_unit_get_status( dv1394d_get_unit( cmd_arg->unit ), &status ); + + if ( error == -1 ) + return RESPONSE_INVALID_UNIT; + else + { + char text[ 10240 ]; + + dv_response_printf( cmd_arg->response, + sizeof( text ), + dv1394_status_serialise( &status, text, sizeof( text ) ) ); + + return RESPONSE_SUCCESS_1; + } + + return 0; +} + + +int dv1394d_set_unit_property( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + char *key = (char*) cmd_arg->argument; + char *value = NULL; + + value = strchr( key, '=' ); + if (value == NULL) + return RESPONSE_OUT_OF_RANGE; + value[0] = 0; + value++; + dv1394d_log( LOG_DEBUG, "USET %s = %s", key, value ); + if ( strncasecmp( key, "eof", 1024) == 0 ) + { + if ( strncasecmp( value, "pause", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_pause ); + else if ( strncasecmp( value, "loop", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_loop ); + else if ( strncasecmp( value, "stop", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_terminate ); + else if ( strncasecmp( value, "clean", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_clean_loop ); + else + return RESPONSE_OUT_OF_RANGE; + } + else if ( strncasecmp( key, "points", 1024) == 0 ) + { + if ( strncasecmp( value, "use", 1024) == 0) + dv_unit_set_mode( unit, dv_clip_mode_restricted ); + else if ( strncasecmp( value, "ignore", 1024) == 0) + dv_unit_set_mode( unit, dv_clip_mode_unrestricted ); + else + return RESPONSE_OUT_OF_RANGE; + } + else if ( strncasecmp( key, "syt_offset", 1024) == 0 ) + { + dv_unit_set_syt_offset( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "cip_n", 1024) == 0 ) + { + dv_unit_set_cip_n( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "cip_d", 1024) == 0 ) + { + dv_unit_set_cip_d( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "size", 1024) == 0 ) + { + dv_unit_set_buffer_size( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "n_frames", 1024) == 0 ) + { + dv_unit_set_n_frames( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "n_fill", 1024) == 0 ) + { + dv_unit_set_n_fill( unit, atoi( value ) ); + } + else + return RESPONSE_OUT_OF_RANGE; + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_get_unit_property( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + char *key = (char*) cmd_arg->argument; + + if ( strncasecmp( key, "eof", 1024) == 0 ) + { + switch ( dv_unit_get_eof_action( unit ) ) + { + case dv_player_pause: + dv_response_write( cmd_arg->response, "pause", strlen("pause") ); + break; + case dv_player_loop: + dv_response_write( cmd_arg->response, "loop", strlen("loop") ); + break; + case dv_player_terminate: + dv_response_write( cmd_arg->response, "stop", strlen("stop") ); + break; + case dv_player_clean_loop: + dv_response_write( cmd_arg->response, "clean", strlen("clean") ); + break; + } + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "points", 1024) == 0 ) + { + if ( dv_unit_get_mode( unit ) == dv_clip_mode_restricted ) + dv_response_write( cmd_arg->response, "use", strlen("use") ); + else + dv_response_write( cmd_arg->response, "ignore", strlen("ignore") ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "syt_offset", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_syt_offset( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "cip_n", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_cip_n( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "cip_d", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_cip_d( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "size", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_buffer_size( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "n_frames", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_n_frames( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "n_fill", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_n_fill( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "all", 1024 ) == 0 ) + { + switch ( dv_unit_get_eof_action( unit ) ) + { + case dv_player_pause: + dv_response_write( cmd_arg->response, "eof=pause\n", strlen("pause") ); + break; + case dv_player_loop: + dv_response_write( cmd_arg->response, "eof=loop\n", strlen("loop") ); + break; + case dv_player_terminate: + dv_response_write( cmd_arg->response, "eof=stop\n", strlen("stop") ); + break; + case dv_player_clean_loop: + dv_response_write( cmd_arg->response, "eof=clean\n", strlen("clean") ); + break; + } + if ( dv_unit_get_mode( unit ) == dv_clip_mode_restricted ) + dv_response_write( cmd_arg->response, "points=use\n", strlen("use") ); + else + dv_response_write( cmd_arg->response, "points=ignore\n", strlen("ignore") ); + dv_response_printf( cmd_arg->response, 1024, "syt_offset=%d\n", dv_unit_get_syt_offset( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "cip_n=%d\n", dv_unit_get_cip_n( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "cip_d=%d\n", dv_unit_get_cip_d( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "size=%d\n", dv_unit_get_buffer_size( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "n_frames=%d\n", dv_unit_get_n_frames( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "n_fill=%d\n", dv_unit_get_n_fill( unit ) ); + } + } + + return RESPONSE_SUCCESS; +} + + +int dv1394d_transfer( command_argument cmd_arg ) +{ + dv_unit src_unit = dv1394d_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 ) + { + dv_unit dest_unit = dv1394d_get_unit( dest_unit_id ); + if ( dest_unit != NULL && !dv_unit_is_offline(dest_unit) && dest_unit != src_unit ) + { + dv_unit_transfer( dest_unit, src_unit ); + return RESPONSE_SUCCESS; + } + } + + return RESPONSE_INVALID_UNIT; +} diff --git a/mlt/src/miracle/miracle_unit_commands.h b/mlt/src/miracle/miracle_unit_commands.h new file mode 100644 index 00000000..a7163bdc --- /dev/null +++ b/mlt/src/miracle/miracle_unit_commands.h @@ -0,0 +1,57 @@ +/* + * unit_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _UNIT_COMMANDS_H_ +#define _UNIT_COMMANDS_H_ + +#include "dvconnection.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern response_codes dv1394d_list( command_argument ); +extern response_codes dv1394d_load( command_argument ); +extern response_codes dv1394d_insert( command_argument ); +extern response_codes dv1394d_remove( command_argument ); +extern response_codes dv1394d_clean( command_argument ); +extern response_codes dv1394d_move( command_argument ); +extern response_codes dv1394d_append( command_argument ); +extern response_codes dv1394d_play( command_argument ); +extern response_codes dv1394d_stop( command_argument ); +extern response_codes dv1394d_pause( command_argument ); +extern response_codes dv1394d_rewind( command_argument ); +extern response_codes dv1394d_step( command_argument ); +extern response_codes dv1394d_goto( command_argument ); +extern response_codes dv1394d_ff( command_argument ); +extern response_codes dv1394d_set_in_point( command_argument ); +extern response_codes dv1394d_set_out_point( command_argument ); +extern response_codes dv1394d_get_unit_status( command_argument ); +extern response_codes dv1394d_set_unit_property( command_argument ); +extern response_codes dv1394d_get_unit_property( command_argument ); +extern response_codes dv1394d_transfer( command_argument ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/modules/Makefile b/mlt/src/modules/Makefile new file mode 100644 index 00000000..0a4c7172 --- /dev/null +++ b/mlt/src/modules/Makefile @@ -0,0 +1,15 @@ +SUBDIRS = core gtk2 dv sdl # bluefish mcmpeg + +all clean depend install: + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + [ ! -f disable-$$subdir ] && $(MAKE) -C $$subdir $@; \ + done + +dist-clean: + rm -f consumers.dat filters.dat producers.dat transitions.dat; \ + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + [ ! -f disable-$$subdir ] && $(MAKE) -C $$subdir $@; \ + done + diff --git a/mlt/src/modules/configure b/mlt/src/modules/configure new file mode 100755 index 00000000..365fe433 --- /dev/null +++ b/mlt/src/modules/configure @@ -0,0 +1,26 @@ +#!/bin/bash + +# Clean up disables if not in help mode +[ "$help" != "1" ] && rm -f disable-* producers.dat filters.dat transitions.dat consumers.dat + +# 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 [ -x $i/configure -a \( "$help" = "1" -o ! -f disable-$i \) ] + then + echo "Configuring $i:" + pushd $i > /dev/null + ./configure $@ + [ $? != 0 ] && exit 1 + popd > /dev/null + fi +done + diff --git a/mlt/src/modules/dv/Makefile b/mlt/src/modules/dv/Makefile new file mode 100644 index 00000000..22d999ee --- /dev/null +++ b/mlt/src/modules/dv/Makefile @@ -0,0 +1,29 @@ + +TARGET = factory.o \ + libmltdv.so + +OBJS = producer_libdv.o + +CFLAGS=-I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS=-ldv -lpthread + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/mlt/src/modules/dv/configure b/mlt/src/modules/dv/configure new file mode 100755 index 00000000..876897c2 --- /dev/null +++ b/mlt/src/modules/dv/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +libdv libmltdv.so +EOF + +fi + diff --git a/mlt/src/modules/dv/factory.c b/mlt/src/modules/dv/factory.c new file mode 100644 index 00000000..e27510a2 --- /dev/null +++ b/mlt/src/modules/dv/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "producer_libdv.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "libdv" ) ) + return producer_libdv_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/mlt/src/modules/dv/producer_libdv.c b/mlt/src/modules/dv/producer_libdv.c new file mode 100644 index 00000000..5d2c768e --- /dev/null +++ b/mlt/src/modules/dv/producer_libdv.c @@ -0,0 +1,370 @@ +/* + * producer_libdv.c -- simple libdv test case + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "producer_libdv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct producer_libdv_s *producer_libdv; + +struct producer_libdv_s +{ + struct mlt_producer_s parent; + int fd; + dv_decoder_t *dv_decoder; + int is_pal; + uint64_t file_size; + int frame_size; + long frames_in_file; +}; + +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_producer producer_libdv_init( char *filename ) +{ + producer_libdv this = calloc( sizeof( struct producer_libdv_s ), 1 ); + + if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) + { + mlt_producer producer = &this->parent; + + // Register transport implementation with the producer + producer->close = producer_close; + + // Register our get_frame implementation with the producer + producer->get_frame = producer_get_frame; + + // Create the dv_decoder + this->dv_decoder = dv_decoder_new( FALSE, FALSE, FALSE ); + this->dv_decoder->quality = DV_QUALITY_BEST; + this->dv_decoder->audio->arg_audio_emphasis = 2; + dv_set_audio_correction( this->dv_decoder, DV_AUDIO_CORRECT_AVERAGE ); + + // Open the file if specified + if ( filename != NULL ) + { + this->fd = open( filename, O_RDONLY ); + producer_collect_info( this ); + } + + // 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 ) +{ + int valid = 0; + uint8_t *dv_data = malloc( 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 ); + + // 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 + double fps = this->is_pal ? 25 : 30000 / 1001; + mlt_timecode length = ( mlt_timecode )( this->frames_in_file ) / fps; + mlt_properties_set_double( properties, "fps", fps ); + mlt_properties_set_timecode( properties, "playtime", length ); + mlt_properties_set_timecode( properties, "length", length ); + mlt_properties_set_timecode( properties, "in", 0.0 ); + mlt_properties_set_timecode( properties, "out", length ); + + // Set the speed to normal + mlt_properties_set_double( properties, "speed", 1 ); + } + + free( dv_data ); + } + + return valid; +} + +#if 0 +static int mc_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 ); + + // Get the dv data + uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL ); + + // Determine bytes in frame + int bytes_in_frame = dv_data[ 3 ] & 0x80 ? frame_size_625_50 : frame_size_525_60; + + // Assign width and height from properties + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + if ( *format == mlt_image_yuv422 ) + { + // Allocate image + *buffer = malloc( *width * *height * 2 ); + + // Decompress + DecompressBuffer_DV( dv_data, bytes_in_frame, *buffer, *width * 2, *width, *height, 0, FOURCC_YUYV, 0, NULL ); + + // Set the image on the properties + mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, free, NULL ); + } + else + { + // Allocate image + *buffer = malloc( *width * *height * 3 ); + + // Decompress + DecompressBuffer_DV( dv_data, bytes_in_frame, *buffer, *width * 3, *width, *height, 0, FOURCC_R24C, 0, NULL ); + + // Set the image on the properties + mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, free, NULL ); + } + + return 0; +} +#endif + +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 the dv decoder + dv_decoder_t *decoder = mlt_properties_get_data( properties, "dv_decoder", NULL ); + + // Get the dv data + uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL ); + + // Assign width and height from properties + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + // Parse the header + dv_parse_header( decoder, dv_data ); + + // Extract an image of the format requested + if ( *format == mlt_image_yuv422 ) + { + // Allocate an image + uint8_t *image = malloc( *width * *height * 2 ); + + // Pass to properties for clean up + mlt_properties_set_data( properties, "image", image, *width * *height * 2, free, 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; + } + else if ( *format == mlt_image_rgb24 ) + { + // Allocate an image + uint8_t *image = malloc( *width * *height * 3 ); + + // Pass to properties for clean up + mlt_properties_set_data( properties, "image", image, *width * *height * 3, free, 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; + } + + 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 the dv decoder + dv_decoder_t *decoder = mlt_properties_get_data( properties, "dv_decoder", NULL ); + + // 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 ); + + // 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 ] = malloc( DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) ); + + // Create a workspace for the result + *buffer = malloc( *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 ), free, 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++ ) + free( audio_channels[ i ] ); + + return 0; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + producer_libdv this = producer->child; + uint8_t *data = malloc( frame_size_625_50 ); + + // Obtain the current frame number + uint64_t position = mlt_producer_frame( producer ); + + // Convert timecode to a file position (ensuring that we're on a frame boundary) + uint64_t offset = position * this->frame_size; + + // Create an empty frame + *frame = mlt_frame_init( ); + + // Seek and fetch + if ( this->fd != 0 && + lseek( this->fd, offset, SEEK_SET ) == offset && + read_frame( this->fd, data, &this->is_pal ) ) + { + // Get the frames properties + mlt_properties properties = mlt_frame_properties( *frame ); + + // Pass the dv decoder + mlt_properties_set_data( properties, "dv_decoder", this->dv_decoder, 0, NULL, NULL ); + + // Pass the dv data + mlt_properties_set_data( properties, "dv_data", data, frame_size_625_50, free, NULL ); + + // Update other info on the frame + mlt_properties_set_int( properties, "width", 720 ); + mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 ); + + // Hmm - register audio callback + ( *frame )->get_audio = producer_get_audio; + + // Push the get_image method on to the stack + mlt_frame_push_get_image( *frame, producer_get_image ); + } + else + { + free( data ); + } + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *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; + + // Free the dv deconder + dv_decoder_free( this->dv_decoder ); + + // Close the file + if ( this->fd != 0 ) + close( this->fd ); + + // Close the parent + parent->close = NULL; + mlt_producer_close( parent ); + + // Free the memory + free( this ); +} + diff --git a/mlt/src/modules/dv/producer_libdv.h b/mlt/src/modules/dv/producer_libdv.h new file mode 100644 index 00000000..c493eaef --- /dev/null +++ b/mlt/src/modules/dv/producer_libdv.h @@ -0,0 +1,28 @@ +/* + * producer_libdv.h -- a DV decoder based on libdv + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _PRODUCER_LIBDV_H_ +#define _PRODUCER_LIBDV_H_ + +#include + +extern mlt_producer producer_libdv_init( char *filename ); + +#endif diff --git a/mlt/src/modules/gtk2/Makefile b/mlt/src/modules/gtk2/Makefile new file mode 100644 index 00000000..0136b31f --- /dev/null +++ b/mlt/src/modules/gtk2/Makefile @@ -0,0 +1,29 @@ + +TARGET = libmltgtk2.so + +OBJS = factory.o \ + producer_pixbuf.o + +CFLAGS=`pkg-config gdk-pixbuf-2.0 --cflags` -I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS=`pkg-config gdk-pixbuf-2.0 --libs` + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/mlt/src/modules/gtk2/configure b/mlt/src/modules/gtk2/configure new file mode 100755 index 00000000..21123363 --- /dev/null +++ b/mlt/src/modules/gtk2/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +pixbuf libmltgtk2.so +EOF + +fi + diff --git a/mlt/src/modules/gtk2/factory.c b/mlt/src/modules/gtk2/factory.c new file mode 100644 index 00000000..fc3a7fa9 --- /dev/null +++ b/mlt/src/modules/gtk2/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "producer_pixbuf.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "pixbuf" ) ) + return producer_pixbuf_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/mlt/src/modules/gtk2/producer_pixbuf.c b/mlt/src/modules/gtk2/producer_pixbuf.c new file mode 100644 index 00000000..8a74a468 --- /dev/null +++ b/mlt/src/modules/gtk2/producer_pixbuf.c @@ -0,0 +1,231 @@ +/* + * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "producer_pixbuf.h" +#include +#include +#include +#include +#include + +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( const 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; + + producer->get_frame = producer_get_frame; + producer->close = producer_close; + + this->filename = strdup( filename ); + this->counter = -1; + g_type_init(); + + 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 ) +{ + // Obtain properties of frame + mlt_properties properties = mlt_frame_properties( this ); + + // May need to know the size of the image to clone it + int size = 0; + + // Get the image + uint8_t *image = mlt_properties_get_data( properties, "image", &size ); + + // Get width and height + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + // Clone if necessary + if ( writable ) + { + // Clone our image + uint8_t *copy = malloc( size ); + memcpy( copy, image, size ); + + // We're going to pass the copy on + image = copy; + + // Now update properties so we free the copy after + mlt_properties_set_data( properties, "image", copy, size, free, NULL ); + } + + // Pass on the image + *buffer = image; + + return 0; +} + +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_pixbuf this = producer->child; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + + // Generate a frame + *frame = mlt_frame_init( ); + + // Obtain properties of frame + mlt_properties properties = mlt_frame_properties( *frame ); + + // optimization for subsequent iterations on single picture + if ( this->image != NULL ) + { + // Set width/height + mlt_properties_set_int( properties, "width", this->width ); + mlt_properties_set_int( properties, "height", this->height ); + + // if picture sequence pass the image and alpha data without destructor + mlt_properties_set_data( properties, "image", this->image, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", this->alpha, 0, NULL, NULL ); + + // Set alpha mask call back + ( *frame )->get_alpha_mask = producer_get_alpha_mask; + + // Stack the get image callback + mlt_frame_push_get_image( *frame, producer_get_image ); + + } + else if ( strchr( this->filename, '%' ) != NULL ) + { + // handle picture sequences + char filename[1024]; + filename[1023] = 0; + int current = this->counter; + do + { + ++this->counter; + snprintf( filename, 1023, this->filename, this->counter ); + pixbuf = gdk_pixbuf_new_from_file( filename, &error ); + // allow discontinuity in frame numbers up to 99 + error = NULL; + } while ( pixbuf == NULL && ( this->counter - current ) < 100 ); + } + else + { + pixbuf = gdk_pixbuf_new_from_file( this->filename, &error ); + } + + // If we have a pixbuf + if ( pixbuf ) + { + // Store width and height + this->width = gdk_pixbuf_get_width( pixbuf ); + this->height = gdk_pixbuf_get_height( pixbuf ); + + // Allocate/define image and alpha + uint8_t *image = malloc( this->width * this->height * 2 ); + uint8_t *alpha = NULL; + + // Extract YUV422 and alpha + if ( gdk_pixbuf_get_has_alpha( pixbuf ) ) + { + // Allocate the alpha mask + alpha = malloc( 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 ), + image, 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 ), + image ); + } + + // Finished with pixbuf now + g_object_unref( pixbuf ); + + // Set width/height of frame + mlt_properties_set_int( properties, "width", this->width ); + mlt_properties_set_int( properties, "height", this->height ); + + // Pass alpha and image on properties with or without destructor + if ( this->counter >= 0 ) + { + // if picture sequence pass the image and alpha data with destructor + mlt_properties_set_data( properties, "image", image, 0, free, NULL ); + mlt_properties_set_data( properties, "alpha", alpha, 0, free, NULL ); + } + else + { + // if single picture, reference the image and alpha in the producer + this->image = image; + this->alpha = alpha; + + // pass the image and alpha data without destructor + mlt_properties_set_data( properties, "image", image, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", alpha, 0, NULL, NULL ); + } + + // 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 ); + } + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer parent ) +{ + producer_pixbuf this = parent->child; + if ( this->filename ) + free( this->filename ); + if ( this->image ) + free( this->image ); + if ( this->alpha ) + free( this->alpha ); + parent->close = NULL; + mlt_producer_close( parent ); + free( this ); +} + diff --git a/mlt/src/modules/gtk2/producer_pixbuf.h b/mlt/src/modules/gtk2/producer_pixbuf.h new file mode 100644 index 00000000..c7f76ef3 --- /dev/null +++ b/mlt/src/modules/gtk2/producer_pixbuf.h @@ -0,0 +1,41 @@ +/* + * producer_pixbuf.h -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _PRODUCER_PIXBUF_H_ +#define _PRODUCER_PIXBUF_H_ + +#include + +typedef struct producer_pixbuf_s *producer_pixbuf; + +struct producer_pixbuf_s +{ + struct mlt_producer_s parent; + char *filename; + int counter; + int width; + int height; + uint8_t *image; + uint8_t *alpha; +}; + +extern mlt_producer producer_pixbuf_init( const char *filename ); + +#endif diff --git a/mlt/src/modules/sdl/Makefile b/mlt/src/modules/sdl/Makefile new file mode 100644 index 00000000..51074412 --- /dev/null +++ b/mlt/src/modules/sdl/Makefile @@ -0,0 +1,29 @@ + +TARGET = libmltsdl.so + +OBJS = factory.o \ + consumer_sdl.o + +CFLAGS=-I../../ `sdl-config --cflags` -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS= `sdl-config --libs` + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/mlt/src/modules/sdl/configure b/mlt/src/modules/sdl/configure new file mode 100755 index 00000000..1c40faca --- /dev/null +++ b/mlt/src/modules/sdl/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../consumers.dat +sdl libmltsdl.so +EOF + +fi + diff --git a/mlt/src/modules/sdl/consumer_sdl.c b/mlt/src/modules/sdl/consumer_sdl.c new file mode 100644 index 00000000..59196d2e --- /dev/null +++ b/mlt/src/modules/sdl/consumer_sdl.c @@ -0,0 +1,366 @@ +/* + * consumer_sdl.c -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "consumer_sdl.h" +#include +#include +#include +#include +#include +#include +#include + +/** This classes definition. +*/ + +typedef struct consumer_sdl_s *consumer_sdl; + +struct consumer_sdl_s +{ + struct mlt_consumer_s parent; + mlt_properties properties; + int format; + int video; + pthread_t thread; + int running; + uint8_t audio_buffer[ 4096 * 6 ]; + int audio_avail; + pthread_mutex_t audio_mutex; + pthread_cond_t audio_cond; + int window_width; + int window_height; +}; + +/** Forward references to static functions. +*/ + +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); +static void consumer_get_dimensions( int *width, int *height ); + +/** 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( void *dummy ) +{ + // 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 ) == 0 ) + { + // 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 + this->running = 1; + pthread_mutex_init( &this->audio_mutex, NULL ); + pthread_cond_init( &this->audio_cond, NULL); + + // TODO: process actual param + + // Create the the thread + pthread_create( &this->thread, NULL, consumer_thread, this ); + + // Return the consumer produced + return parent; + } + + // malloc or consumer init failed + free( this ); + + // Indicate failure + return NULL; +} + +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 ); +} + +void sdl_fill_audio( void *udata, Uint8 *stream, int len ) +{ + consumer_sdl this = udata; + + // Get the volume + float volume = mlt_properties_get_double( this->properties, "volume" ); + + pthread_mutex_lock( &this->audio_mutex ); + + // Experimental - 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 + SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); + + // 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 ); + + // Copy what we have into the stream + memcpy( stream, this->audio_buffer, this->audio_avail ); + + // Mix the audio + SDL_MixAudio( stream, stream, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); + + // No audio left + this->audio_avail = 0; + } + pthread_cond_broadcast( &this->audio_cond ); + pthread_mutex_unlock( &this->audio_mutex ); +} + +/** 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; + + // Get the service assoicated to the consumer + mlt_service service = mlt_consumer_service( consumer ); + + // Define a frame pointer + mlt_frame frame; + + // internal intialization + int sdl_flags = SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL | SDL_RESIZABLE; + SDL_Surface *sdl_screen = NULL; + SDL_Overlay *sdl_overlay = NULL; + uint8_t *buffer = NULL; + int init_audio = 1; + int bytes; + + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); + return NULL; + } + + // Loop until told not to + while( this->running ) + { + // Get a frame from the service (should never return anything other than 0) + if ( mlt_service_get_frame( service, &frame, 0 ) == 0 ) + { + mlt_image_format vfmt = mlt_image_yuv422; + int width, height; + uint8_t *image; + + mlt_audio_format afmt = mlt_audio_pcm; + int channels; + int samples; + int frequency; + int16_t *pcm; + + mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples ); + if ( init_audio == 1 ) + { + SDL_AudioSpec request; + + // specify audio format + request.freq = frequency; + request.format = AUDIO_S16; + request.channels = channels; + request.samples = 1024; + request.callback = sdl_fill_audio; + request.userdata = (void *)this; + if ( SDL_OpenAudio( &request, NULL ) < 0 ) + { + fprintf( stderr, "SDL failed to open audio: %s\n", SDL_GetError() ); + break; + } + SDL_PauseAudio( 0 ); + init_audio = 0; + } + bytes = ( samples * channels * 2 ); + pthread_mutex_lock( &this->audio_mutex ); + while ( bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) ) + pthread_cond_wait( &this->audio_cond, &this->audio_mutex ); + memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes ); + this->audio_avail += bytes; + pthread_cond_broadcast( &this->audio_cond ); + pthread_mutex_unlock( &this->audio_mutex ); + + // Get the image, width and height + mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); + if ( width != this->window_width || height != this->window_height ) + { + SDL_Rect rect; + this->window_width = rect.w = width; + this->window_height = rect.h = height; + + // open SDL window with video overlay, if possible + if ( sdl_screen == NULL ) + sdl_screen = SDL_SetVideoMode( width, height, 0, sdl_flags ); + if ( sdl_screen != NULL ) + { + rect.x = rect.y = 0; + + + // XXX: this is a little hack until we have some sort of aspect + // ratio property on images. + if ( rect.h <= 486 ) + { + rect.w = 640; + rect.x = ( 720 - 640 ) / 2; + } + else + { + rect.h = 540; + rect.y = ( 576 - 540 ) / 2; + } + + SDL_SetClipRect( sdl_screen, &rect ); + + // Force an overlay recreation + if ( sdl_overlay != NULL ) + SDL_FreeYUVOverlay( sdl_overlay ); + sdl_lock_display(); + sdl_overlay = SDL_CreateYUVOverlay( width, height, SDL_YUY2_OVERLAY, sdl_screen ); + sdl_unlock_display(); + } + } + + if ( sdl_screen != NULL && sdl_overlay != NULL ) + { + buffer = sdl_overlay->pixels[ 0 ]; + if ( sdl_lock_display() ) + { + if ( SDL_LockYUVOverlay( sdl_overlay ) >= 0 ) + { + memcpy( buffer, image, width * height * 2 ); + SDL_UnlockYUVOverlay( sdl_overlay ); + SDL_DisplayYUVOverlay( sdl_overlay, &sdl_screen->clip_rect ); + } + sdl_unlock_display(); + } + } + else + { + // TODO: allocate buffer? + } + + // Close the frame + mlt_frame_close( frame ); + } + else + { + break; + } + } // while + + // internal cleanup + if ( init_audio == 0 ) + SDL_AudioQuit( ); + if ( sdl_overlay != NULL ) + SDL_FreeYUVOverlay( sdl_overlay ); + SDL_Quit( ); + + return NULL; +} + +static void consumer_get_dimensions( int *width, int *height ) +{ + // SDL windows manager structure + SDL_SysWMinfo wm; + + // Specify the SDL Version + SDL_VERSION( &wm.version ); + + // 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 ); + + // Return width and height + *width = attr.width; + *height = attr.height; + } + } +} + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + // Kill the thread and clean up + this->running = 0; + pthread_join( this->thread, NULL ); + + pthread_mutex_destroy( &this->audio_mutex ); + pthread_cond_destroy( &this->audio_cond ); + + // Now clean up the rest (the close = NULL is a bit nasty but needed for now) + parent->close = NULL; + mlt_consumer_close( parent ); + + // Finally clean up this + free( this ); +} + diff --git a/mlt/src/modules/sdl/consumer_sdl.h b/mlt/src/modules/sdl/consumer_sdl.h new file mode 100644 index 00000000..6c8114e4 --- /dev/null +++ b/mlt/src/modules/sdl/consumer_sdl.h @@ -0,0 +1,28 @@ +/* + * consumer_sdl.h -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _CONSUMER_SDL_H_ +#define _CONSUMER_SDL_H_ + +#include + +extern mlt_consumer consumer_sdl_init( void * ); + +#endif diff --git a/mlt/src/modules/sdl/factory.c b/mlt/src/modules/sdl/factory.c new file mode 100644 index 00000000..9e5f56ea --- /dev/null +++ b/mlt/src/modules/sdl/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "consumer_sdl.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + if ( !strcmp( id, "sdl" ) ) + return consumer_sdl_init( arg ); + return NULL; +} + diff --git a/mlt/src/tests/charlie.c b/mlt/src/tests/charlie.c new file mode 100644 index 00000000..174cb20c --- /dev/null +++ b/mlt/src/tests/charlie.c @@ -0,0 +1,79 @@ +#include "mlt_producer.h" +#include "mlt_consumer.h" +#include "mlt_filter.h" +#include "mlt_tractor.h" +#include "mlt_transition.h" +#include "mlt_multitrack.h" + +#include "producer_libdv.h" +#include "producer_ppm.h" +#include "filter_deinterlace.h" +#include "filter_greyscale.h" +#include "consumer_sdl.h" + +#include + +int main( int argc, char **argv ) +{ + char temp[ 132 ]; + char *file1 = NULL; + char *file2 = NULL; + + // Start the consumer... + mlt_consumer sdl_out = consumer_sdl_init( NULL ); + + fprintf( stderr, "Press return to continue\n" ); + fgets( temp, 132, stdin ); + + if ( argc >= 2 ) + file1 = argv[ 1 ]; + if ( argc >= 3 ) + file2 = argv[ 2 ]; + + // Create the producer(s) + //mlt_producer dv1 = producer_ppm_init( NULL ); + //mlt_producer dv2 = producer_ppm_init( NULL ); + mlt_producer dv1 = producer_libdv_init( file1 ); + mlt_producer dv2 = producer_libdv_init( file2 ); + + mlt_consumer_connect( sdl_out, mlt_producer_service( dv1 ) ); + + fprintf( stderr, "Press return to continue\n" ); + fgets( temp, 132, stdin ); + + // Register producers(s) with a multitrack object + mlt_multitrack multitrack = mlt_multitrack_init( ); + mlt_multitrack_connect( multitrack, dv1, 0 ); + mlt_multitrack_connect( multitrack, dv2, 1 ); + + // Create a filter and associate it to track 0 + mlt_filter filter = filter_deinterlace_init( NULL ); + mlt_filter_connect( filter, mlt_multitrack_service( multitrack ), 0 ); + mlt_filter_set_in_and_out( filter, 0, 5 ); + + // Create another + mlt_filter greyscale = filter_greyscale_init( NULL ); + mlt_filter_connect( greyscale, mlt_filter_service( filter ), 0 ); + mlt_filter_set_in_and_out( greyscale, 0, 10 ); + + // Buy a tractor and connect it to the filter + mlt_tractor tractor = mlt_tractor_init( ); + mlt_tractor_connect( tractor, mlt_filter_service( greyscale ) ); + + // Connect the tractor to the consumer + mlt_consumer_connect( sdl_out, 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( sdl_out ); + mlt_tractor_close( tractor ); + mlt_filter_close( filter ); + mlt_multitrack_close( multitrack ); + mlt_producer_close( dv1 ); + mlt_producer_close( dv2 ); + + return 0; +} diff --git a/mlt/src/tests/dan.c b/mlt/src/tests/dan.c new file mode 100644 index 00000000..ee76bb01 --- /dev/null +++ b/mlt/src/tests/dan.c @@ -0,0 +1,72 @@ +#include "mlt_producer.h" +#include "mlt_consumer.h" +#include "mlt_filter.h" +#include "mlt_tractor.h" +#include "mlt_transition.h" +#include "mlt_multitrack.h" + +#include "producer_libdv.h" +#include "filter_deinterlace.h" +#include "consumer_sdl.h" +#include "producer_ppm.h" +#include "producer_pixbuf.h" +#include "transition_composite.h" + +#include + +int main( int argc, char **argv ) +{ + char temp[ 132 ]; + char *file1 = NULL; + char *file2 = NULL; + + if ( argc >= 2 ) + file1 = argv[ 1 ]; + if ( argc >= 3 ) + file2 = argv[ 2 ]; + + // Start the consumer... + mlt_consumer sdl_out = consumer_sdl_init( NULL ); + + // Create the producer(s) + mlt_producer dv1 = producer_libdv_init( file1 ); + //mlt_producer dv1 = producer_pixbuf_init( file1 ); + //mlt_producer dv2 = producer_libdv_init( file2 ); + mlt_producer dv2 = producer_pixbuf_init( file2 ); + + // Register producers(s) with a multitrack object + mlt_multitrack multitrack = mlt_multitrack_init( ); + mlt_multitrack_connect( multitrack, dv1, 0 ); + mlt_multitrack_connect( multitrack, dv2, 1 ); + + // Create a filter and associate it to track 0 + mlt_filter filter = filter_deinterlace_init( NULL ); + mlt_filter_connect( filter, mlt_multitrack_service( multitrack ), 0 ); + mlt_filter_set_in_and_out( filter, 0, 1000 ); + + // Define a transition + mlt_transition transition = transition_composite_init( NULL ); + mlt_transition_connect( transition, mlt_filter_service( filter ), 0, 1 ); + mlt_transition_set_in_and_out( transition, 0, 1000 ); + + // 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( sdl_out, 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( sdl_out ); + mlt_tractor_close( tractor ); + mlt_filter_close( filter ); + mlt_multitrack_close( multitrack ); + mlt_producer_close( dv1 ); + mlt_producer_close( dv2 ); + + return 0; +} diff --git a/mlt/src/tests/test.png b/mlt/src/tests/test.png new file mode 100644 index 0000000000000000000000000000000000000000..b3fca64519876231ae9a060fc194a0ae48244fd8 GIT binary patch literal 1352 zcmV-O1-JT%P)Q2Ue;Z@vP-uW*q~F{#Qkvx3{iucMMBURf(ro( z64WDz%3wsI1WiZ?IwmqfE=>#|LBSY)C;}3QD2(82BPa;o3Yo5hZNR#99qZa2f1I_H ztzGq(@2_*d%lmxq_4@>AL{Ystz*E3%(8@tLfC8W$@PYO#i2cA(x63(@PM;VBR0zBZ zsDLq@BESzc0Gr${XE4` z1|ntJ0SGg&TT#7n-^VOm1LP#zVloOEUoK*PO)iZ~?3fIKZ+`3pz?LmDX}oDv8-aU) zg>j&wdadZGpWHfwAkV6w>%po_8W!8RaAkmwZr!a5X4uFyCr$Q5SsnWjit2q9*fZMd z9E-%}x?(0zFayy1bCC8MVV<6D1t1cIGrt5mcsjs$=YoX7S~4|NZkO}C?5JM08kjs9 zFcj7}?&(9Ruwb_tC@nHlV3Wzp&@Gb$l-iBdRA*89h+Zx?15rA=BZ+GM_Ia94Nx-DU z(X0%KbuZZY^sTXc{HBAN>MR7%+aKlCO;@>aMekmAo1O=n2FE>pM54M?)+~wIN386A z!@<`ZO3BHLyL%FV1ezl@Qc-H=;5*}ad`cE~jWtnOW@f!=XG>i%7PB-03LpgRJkrgK zm8~@Hzs_%eh2nF2kNN2eMq|GY&|??@B#T+_$(pf@D>UNoi*oREFSDj)W6PES@O6cG zzQISAKSH)eVoO3yTQ2rfx8)jxL$RwUH8HWo#NqD)3BJ?ZZddsf3AC#Ki)PvwS7_vV zPlQ>kTG{mZ3g)eCC{RK3XAoU0l3r}8U_Op9YZb^ z?EobOdXH}F3^OpOpR~&#;idQ5Is3;g0LqGtY+F%+HA@-+eW)sv4_+%Ctr4^j0XYY7 zvscfk`zD&Hz9%ETYG6=f>HAk{KCgF|3B^V}tSceIBt}3_pAVuXtq6#5WJmQffO&tm z3NmN96{A5=qhxd9Y#-eLJ>Zcj9P${1up%f)^|VT~g#0ouC4e0HKI z1xwRFEb)1o{1R{k=;`PVvwm+E02wC1j`|YH9k<(7IHFO%^%|`mg83iL;5+;<%SgDg_rhe<@N zjeW(caw9`Q-F)B%2Tu0h0Ud9@ed)`Ro)sFm@%;ckPgB@Yz03nV1IVX- z3Q~}3pt8)2#Vk?#s1;cf6pb= zFFs*IeR9kQEtgscSoCgtywFof2zc1-a<(K6j;NZUx5y5_3^1j_LjBx4${i-c5sh7k z{d{z&n{Z@QSSK*Y?Q)(RK9RIc71cWmv@b#AgWfOAMnO12IuO9Oz-+h6c{ADMXzx-P zSOlyB%F|JXKpPSh.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) libvalerie.a + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/mlt/src/valerie/configure b/mlt/src/valerie/configure new file mode 100755 index 00000000..1a248525 --- /dev/null +++ b/mlt/src/valerie/configure @@ -0,0 +1 @@ +#!/bin/sh diff --git a/mlt/src/valerie/valerie.c b/mlt/src/valerie/valerie.c new file mode 100644 index 00000000..2e1f2895 --- /dev/null +++ b/mlt/src/valerie/valerie.c @@ -0,0 +1,875 @@ +/* + * valerie.c -- High Level Client API for miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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, 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; +} + +/** 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 - 2 ); + 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, double in, double out ) +{ + return valerie_execute( this, 10240, "LOAD U%d \"%s\" %e %e", 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, double in, double out ) +{ + return valerie_execute( this, 10240, "LOAD U%d \"!%s\" %e %e", unit, file, in, out ); +} + +/** Append a file on the specified unit. +*/ + +valerie_error_code valerie_unit_append( valerie this, int unit, char *file, double in, double out ) +{ + return valerie_execute( this, 10240, "APND U%d \"%s\" %e %e", unit, file, in, out ); +} + +/** 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 ); +} + +/** 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, double in, double out ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "INSERT U%d %s %s %e %e", 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, double speed ) +{ + return valerie_execute( this, 10240, "PLAY U%d %e", 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, double step ) +{ + return valerie_execute( this, 1024, "STEP U%d %e", unit, step ); +} + +/** Goto the specified frame on the specified unit. +*/ + +valerie_error_code valerie_unit_goto( valerie this, int unit, double position ) +{ + return valerie_execute( this, 1024, "GOTO U%d %e", 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, double position ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "GOTO U%d %e %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, double in ) +{ + return valerie_execute( this, 1024, "SIN U%d %e", 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, double in ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "SIN U%d %e %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, double out ) +{ + return valerie_execute( this, 1024, "SOUT U%d %e", 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, double in ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "SOUT U%d %e %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, char *name, 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, 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 = atof( valerie_tokeniser_get_string( tokeniser, 2 ) ); + entry->out = atof( valerie_tokeniser_get_string( tokeniser, 3 ) ); + entry->max = atof( valerie_tokeniser_get_string( tokeniser, 4 ) ); + entry->size = atof( 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. +*/ + +char *valerie_error_description( valerie_error_code error ) +{ + 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/mlt/src/valerie/valerie.h b/mlt/src/valerie/valerie.h new file mode 100644 index 00000000..91c09878 --- /dev/null +++ b/mlt/src/valerie/valerie.h @@ -0,0 +1,256 @@ +/* + * valerie.h -- High Level Client API for miracle + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _VALERIE_H_ +#define _VALERIE_H_ + +/* System header files */ +#include + +/* 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 *, double, double ); +extern valerie_error_code valerie_unit_load_back( valerie, int, char * ); +extern valerie_error_code valerie_unit_load_back_clipped( valerie, int, char *, double, double ); +extern valerie_error_code valerie_unit_append( valerie, int, char *, double, double ); +extern valerie_error_code valerie_unit_clean( 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 *, double, double ); +extern valerie_error_code valerie_unit_play( valerie, int ); +extern valerie_error_code valerie_unit_play_at_speed( valerie, int, double ); +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, double ); +extern valerie_error_code valerie_unit_goto( valerie, int, double ); +extern valerie_error_code valerie_unit_clip_goto( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_clip_set_in( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_clip_set_out( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_set_in( valerie, int, double ); +extern valerie_error_code valerie_unit_set_out( valerie, int, double ); +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, char *, 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, 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 ]; + double in; + double out; + double max; + double size; + double 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[ 17 ]; + 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 char *valerie_error_description( valerie_error_code ); + +/* Courtesy functions. */ +extern valerie_error_code valerie_execute( valerie, size_t, char *, ... ); + +/* Close function. */ +extern void valerie_close( valerie ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/valerie/valerie_notifier.c b/mlt/src/valerie/valerie_notifier.c new file mode 100644 index 00000000..5e374a85 --- /dev/null +++ b/mlt/src/valerie/valerie_notifier.c @@ -0,0 +1,142 @@ +/* + * valerie_notifier.c -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include + +/* Application header files */ +#include "valerie_notifier.h" + +/** Notifier initialisation. +*/ + +valerie_notifier valerie_notifier_init( ) +{ + valerie_notifier this = malloc( sizeof( valerie_notifier_t ) ); + if ( this != NULL ) + { + int index = 0; + memset( this, 0, sizeof( valerie_notifier_t ) ); + pthread_mutex_init( &this->mutex, NULL ); + pthread_cond_init( &this->cond, NULL ); + pthread_mutex_init( &this->cond_mutex, 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 ) ); + + pthread_mutex_lock( &this->cond_mutex ); + gettimeofday( &now, NULL ); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + if ( pthread_cond_timedwait( &this->cond, &this->cond_mutex, &timeout ) != ETIMEDOUT ) + { + pthread_mutex_lock( &this->mutex ); + valerie_status_copy( status, &this->last ); + pthread_mutex_unlock( &this->mutex ); + } + else + { + error = -1; + } + pthread_mutex_unlock( &this->cond_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_mutex_destroy( &this->cond_mutex ); + pthread_cond_destroy( &this->cond ); + free( this ); + } +} diff --git a/mlt/src/valerie/valerie_notifier.h b/mlt/src/valerie/valerie_notifier.h new file mode 100644 index 00000000..bc123ed1 --- /dev/null +++ b/mlt/src/valerie/valerie_notifier.h @@ -0,0 +1,61 @@ +/* + * valerie_notifier.h -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_NOTIFIER_H_ +#define _VALERIE_NOTIFIER_H_ + +/* System header files */ +#include + +/* 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_mutex_t cond_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/mlt/src/valerie/valerie_parser.c b/mlt/src/valerie/valerie_parser.c new file mode 100644 index 00000000..4ead1c04 --- /dev/null +++ b/mlt/src/valerie/valerie_parser.c @@ -0,0 +1,131 @@ +/* + * valerie_parser.c -- Valerie Parser for Miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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 ); +} + +/** Execute a formatted command via the parser. +*/ + +valerie_response valerie_parser_executef( valerie_parser parser, 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/mlt/src/valerie/valerie_parser.h b/mlt/src/valerie/valerie_parser.h new file mode 100644 index 00000000..71446d90 --- /dev/null +++ b/mlt/src/valerie/valerie_parser.h @@ -0,0 +1,67 @@ +/* + * valerie_parser.h -- Valerie Parser for Miracle Server + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_PARSER_H_ +#define _VALERIE_PARSER_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 void (*parser_close)( void * ); + +/** Structure for the valerie parser. +*/ + +typedef struct +{ + parser_connect connect; + parser_execute execute; + 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_execute( valerie_parser, char * ); +extern valerie_response valerie_parser_executef( valerie_parser, 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/mlt/src/valerie/valerie_remote.c b/mlt/src/valerie/valerie_remote.c new file mode 100644 index 00000000..4b5023e5 --- /dev/null +++ b/mlt/src/valerie/valerie_remote.c @@ -0,0 +1,260 @@ +/* + * valerie_remote.c -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#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 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 = malloc( sizeof( valerie_parser_t ) ); + valerie_remote remote = malloc( sizeof( valerie_remote_t ) ); + + if ( parser != NULL ) + { + memset( parser, 0, sizeof( valerie_parser_t ) ); + + parser->connect = (parser_connect)valerie_remote_connect; + parser->execute = (parser_execute)valerie_remote_execute; + parser->close = (parser_close)valerie_remote_close; + parser->real = remote; + + if ( remote != NULL ) + { + memset( remote, 0, sizeof( valerie_remote_t ) ); + 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; +} + +/** Disconnect. +*/ + +static void valerie_remote_disconnect( valerie_remote remote ) +{ + if ( remote != NULL && remote->terminated ) + { + 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/mlt/src/valerie/valerie_remote.h b/mlt/src/valerie/valerie_remote.h new file mode 100644 index 00000000..291184a2 --- /dev/null +++ b/mlt/src/valerie/valerie_remote.h @@ -0,0 +1,41 @@ +/* + * valerie_remote.h -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +#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/mlt/src/valerie/valerie_response.c b/mlt/src/valerie/valerie_response.c new file mode 100644 index 00000000..5b9491ca --- /dev/null +++ b/mlt/src/valerie/valerie_response.c @@ -0,0 +1,244 @@ +/* + * valerie_response.c -- Response + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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. +*/ + +char *valerie_response_get_error_string( valerie_response response ) +{ + 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, 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, 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, char *text, int size ) +{ + int ret = 0; + char *ptr = text; + + while ( size > 0 ) + { + int index = response->count - 1; + 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/mlt/src/valerie/valerie_response.h b/mlt/src/valerie/valerie_response.h new file mode 100644 index 00000000..1e8f1fa6 --- /dev/null +++ b/mlt/src/valerie/valerie_response.h @@ -0,0 +1,59 @@ +/* + * valerie_response.h -- Response + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_RESPONSE_H_ +#define _VALERIE_RESPONSE_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 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, char * ); +extern int valerie_response_printf( valerie_response, size_t, char *, ... ); +extern int valerie_response_write( valerie_response, char *, int ); +extern void valerie_response_close( valerie_response ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/valerie/valerie_socket.c b/mlt/src/valerie/valerie_socket.c new file mode 100644 index 00000000..74e84834 --- /dev/null +++ b/mlt/src/valerie/valerie_socket.c @@ -0,0 +1,175 @@ +/* + * valerie_socket.c -- Client Socket + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, 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/mlt/src/valerie/valerie_socket.h b/mlt/src/valerie/valerie_socket.h new file mode 100644 index 00000000..f016ca1a --- /dev/null +++ b/mlt/src/valerie/valerie_socket.h @@ -0,0 +1,56 @@ +/* + * valerie_socket.h -- Client Socket + * Copyright (C) 2002-2003 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 + * 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, char *, int ); +extern void valerie_socket_close( valerie_socket ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mlt/src/valerie/valerie_status.c b/mlt/src/valerie/valerie_status.c new file mode 100644 index 00000000..5725b0c4 --- /dev/null +++ b/mlt/src/valerie/valerie_status.c @@ -0,0 +1,160 @@ +/* + * valerie_status.c -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include + +/* 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 = atof( 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 = atof( valerie_tokeniser_get_string( tokeniser, 6 ) ); + status->out = atof( valerie_tokeniser_get_string( tokeniser, 7 ) ); + status->length = atof( 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 = atof( valerie_tokeniser_get_string( tokeniser, 10 ) ); + status->tail_in = atof( valerie_tokeniser_get_string( tokeniser, 11 ) ); + status->tail_out = atof( valerie_tokeniser_get_string( tokeniser, 12 ) ); + status->tail_length = atof( valerie_tokeniser_get_string( tokeniser, 13 ) ); + status->seek_flag = atof( valerie_tokeniser_get_string( tokeniser, 14 ) ); + status->generation = atof( valerie_tokeniser_get_string( tokeniser, 15 ) ); + status->clip_index = atof( 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 ) +{ + 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\" %e %e %.2f %e %e %e \"%s\" %e %e %e %e %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/mlt/src/valerie/valerie_status.h b/mlt/src/valerie/valerie_status.h new file mode 100644 index 00000000..d0e45a94 --- /dev/null +++ b/mlt/src/valerie/valerie_status.h @@ -0,0 +1,83 @@ +/* + * valerie_status.h -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_STATUS_H_ +#define _VALERIE_STATUS_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 ]; + double position; + double speed; + double fps; + double in; + double out; + double length; + char tail_clip[ 2048 ]; + double tail_position; + double tail_in; + double tail_out; + double 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/mlt/src/valerie/valerie_tokeniser.c b/mlt/src/valerie/valerie_tokeniser.c new file mode 100644 index 00000000..7acaf853 --- /dev/null +++ b/mlt/src/valerie/valerie_tokeniser.c @@ -0,0 +1,172 @@ +/* + * valerie_tokeniser.c -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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, 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/mlt/src/valerie/valerie_tokeniser.h b/mlt/src/valerie/valerie_tokeniser.h new file mode 100644 index 00000000..9d0838cc --- /dev/null +++ b/mlt/src/valerie/valerie_tokeniser.h @@ -0,0 +1,55 @@ +/* + * valerie_tokeniser.h -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +#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 *, 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/mlt/src/valerie/valerie_util.c b/mlt/src/valerie/valerie_util.c new file mode 100644 index 00000000..168f6a74 --- /dev/null +++ b/mlt/src/valerie/valerie_util.c @@ -0,0 +1,77 @@ +/* + * valerie_util.c -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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/mlt/src/valerie/valerie_util.h b/mlt/src/valerie/valerie_util.h new file mode 100644 index 00000000..492eba04 --- /dev/null +++ b/mlt/src/valerie/valerie_util.h @@ -0,0 +1,37 @@ +/* + * valerie_util.h -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +#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 diff --git a/src/framework/Makefile b/src/framework/Makefile new file mode 100644 index 00000000..e2d60ee3 --- /dev/null +++ b/src/framework/Makefile @@ -0,0 +1,38 @@ + +FRAMEWORK_OBJS = mlt_frame.o \ + mlt_property.o \ + mlt_properties.o \ + mlt_service.o \ + mlt_producer.o \ + mlt_multitrack.o \ + mlt_consumer.o \ + mlt_filter.o \ + mlt_transition.o \ + mlt_tractor.o \ + mlt_factory.o \ + mlt_repository.o + +OBJS = $(FRAMEWORK_OBJS) + +SRCS := $(OBJS:.o=.c) + +CFLAGS=-g -Wall -D_FILE_OFFSET_BITS=64 -pthread + +all: libmlt.a + +libmlt.a: $(OBJS) + $(AR) rvu $@ $(OBJS) + ranlib $@ + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(FRAMEWORK_OBJS) libmlt.a + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/framework/config.h b/src/framework/config.h new file mode 100644 index 00000000..fec9861d --- /dev/null +++ b/src/framework/config.h @@ -0,0 +1,10 @@ +/** GENERATED FILE - DON'T EDIT */ + +#ifndef _MLT_CONFIG_H_ +#define _MLT_CONFIG_H_ + +#define PREFIX "/usr/local" +#define PREFIX_DATA PREFIX "/share" + +#endif + diff --git a/src/framework/configure b/src/framework/configure new file mode 100755 index 00000000..a9bf588e --- /dev/null +++ b/src/framework/configure @@ -0,0 +1 @@ +#!/bin/bash diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c new file mode 100644 index 00000000..635ad651 --- /dev/null +++ b/src/framework/mlt_consumer.c @@ -0,0 +1,63 @@ +/* + * mlt_consumer.c -- abstraction for all consumer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_consumer.h" +#include +#include +#include + +/** Public final methods +*/ + +int mlt_consumer_init( mlt_consumer this, void *child ) +{ + memset( this, 0, sizeof( struct mlt_consumer_s ) ); + this->child = child; + return mlt_service_init( &this->parent, this ); +} + +/** Get the parent service object. +*/ + +mlt_service mlt_consumer_service( mlt_consumer this ) +{ + return &this->parent; +} + +/** Connect the consumer to the producer. +*/ + +int mlt_consumer_connect( mlt_consumer this, mlt_service producer ) +{ + return mlt_service_connect_producer( &this->parent, producer, 0 ); +} + +/** Close the consumer. +*/ + +void mlt_consumer_close( mlt_consumer this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} + diff --git a/src/framework/mlt_consumer.h b/src/framework/mlt_consumer.h new file mode 100644 index 00000000..cbe90781 --- /dev/null +++ b/src/framework/mlt_consumer.h @@ -0,0 +1,50 @@ +/* + * mlt_consumer.h -- abstraction for all consumer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_CONSUMER_H_ +#define _MLT_CONSUMER_H_ + +#include "mlt_service.h" + +/** The interface definition for all consumers. +*/ + +struct mlt_consumer_s +{ + // We're implementing service here + struct mlt_service_s parent; + + // public virtual + void ( *close )( mlt_consumer ); + + // Private data + void *private; + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_consumer_init( mlt_consumer this, void *child ); +extern mlt_service mlt_consumer_service( mlt_consumer this ); +extern int mlt_consumer_connect( mlt_consumer this, mlt_service producer ); +extern void mlt_consumer_close( mlt_consumer ); + +#endif diff --git a/src/framework/mlt_factory.c b/src/framework/mlt_factory.c new file mode 100644 index 00000000..3b40464d --- /dev/null +++ b/src/framework/mlt_factory.c @@ -0,0 +1,89 @@ +/* + * mlt_factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_factory.h" +#include "mlt_repository.h" + +#include + +/** Singleton repositories +*/ + +static mlt_repository producers = NULL; +static mlt_repository filters = NULL; +static mlt_repository transitions = NULL; +static mlt_repository consumers = NULL; + +/** Construct the factories. +*/ + +int mlt_factory_init( ) +{ + producers = mlt_repository_init( PREFIX_DATA "/producers.dat", "mlt_create_producer" ); + filters = mlt_repository_init( PREFIX_DATA "/filters.dat", "mlt_create_filter" ); + transitions = mlt_repository_init( PREFIX_DATA "/transitions.dat", "mlt_create_transition" ); + consumers = mlt_repository_init( PREFIX_DATA "/consumers.dat", "mlt_create_consumer" ); + return 0; +} + +/** Fetch a producer from the repository. +*/ + +mlt_producer mlt_factory_producer( char *service, void *input ) +{ + return ( mlt_producer )mlt_repository_fetch( producers, service, input ); +} + +/** Fetch a filter from the repository. +*/ + +mlt_filter mlt_factory_filter( char *service, void *input ) +{ + return ( mlt_filter )mlt_repository_fetch( filters, service, input ); +} + +/** Fetch a transition from the repository. +*/ + +mlt_transition mlt_transition_filter( char *service, void *input ) +{ + return ( mlt_transition )mlt_repository_fetch( transitions, service, input ); +} + +/** Fetch a consumer from the repository +*/ + +mlt_consumer mlt_factory_consumer( char *service, void *input ) +{ + return ( mlt_consumer )mlt_repository_fetch( consumers, service, input ); +} + +/** Close the factory. +*/ + +void mlt_factory_close( ) +{ + mlt_repository_close( producers ); + mlt_repository_close( filters ); + mlt_repository_close( transitions ); + mlt_repository_close( consumers ); +} + diff --git a/src/framework/mlt_factory.h b/src/framework/mlt_factory.h new file mode 100644 index 00000000..4cece1da --- /dev/null +++ b/src/framework/mlt_factory.h @@ -0,0 +1,33 @@ +/* + * mlt_factory.h -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FACTORY_H +#define _MLT_FACTORY_H + +#include "mlt_types.h" + +extern int mlt_factory_init( ); +extern mlt_producer mlt_factory_producer( char *name, void *input ); +extern mlt_filter mlt_factory_filter( char *name, void *input ); +extern mlt_transition mlt_factory_transition( char *name, void *input ); +extern mlt_consumer mlt_factory_consumer( char *name, void *input ); +extern void mlt_factory_close( ); + +#endif diff --git a/src/framework/mlt_filter.c b/src/framework/mlt_filter.c new file mode 100644 index 00000000..22fefe22 --- /dev/null +++ b/src/framework/mlt_filter.c @@ -0,0 +1,163 @@ +/* + * mlt_filter.c -- abstraction for all filter services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_filter.h" +#include "mlt_frame.h" + +#include +#include +#include + +static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor method. +*/ + +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 ) + { + service->get_frame = filter_get_frame; + return 0; + } + return 1; +} + +/** Get the service associated to this filter +*/ + +mlt_service mlt_filter_service( mlt_filter this ) +{ + return &this->parent; +} + +/** 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. +*/ + +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 ) + { + this->producer = producer; + this->track = index; + this->in = 0; + this->out = 0; + } + + return ret; +} + +/** Tune the in/out points. +*/ + +void mlt_filter_set_in_and_out( mlt_filter this, mlt_timecode in, mlt_timecode out ) +{ + this->in = in; + this->out = out; +} + +/** Return the track that this filter is operating on. +*/ + +int mlt_filter_get_track( mlt_filter this ) +{ + return this->track; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_filter_get_in( mlt_filter this ) +{ + return this->in; +} + +/** Get the out point. +*/ + +mlt_timecode mlt_filter_get_out( mlt_filter this ) +{ + return this->out; +} + +/** Process the frame. +*/ + +static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) +{ + if ( this->process == NULL ) + return frame; + else + return this->process( this, frame ); +} + +/** Get a frame from this filter. +*/ + +static int filter_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + mlt_filter this = service->child; + + // If the frame request is for this filters track, we need to process it + if ( index == this->track ) + { + int ret = mlt_service_get_frame( this->producer, frame, index ); + if ( ret == 0 ) + { + if ( !mlt_frame_is_test_card( *frame ) ) + { + mlt_timecode timecode = mlt_frame_get_timecode( *frame ); + if ( timecode >= this->in && ( this->out == 0 || timecode < this->out ) ) + *frame = filter_process( this, *frame ); + } + return 0; + } + else + { + *frame = mlt_frame_init( ); + return 0; + } + } + else + { + return mlt_service_get_frame( this->producer, frame, index ); + } +} + +/** Close the filter. +*/ + +void mlt_filter_close( mlt_filter this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} + diff --git a/src/framework/mlt_filter.h b/src/framework/mlt_filter.h new file mode 100644 index 00000000..99212a92 --- /dev/null +++ b/src/framework/mlt_filter.h @@ -0,0 +1,62 @@ +/* + * mlt_filter.h -- abstraction for all filter services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FILTER_H_ +#define _MLT_FILTER_H_ + +#include "mlt_service.h" + +/** The interface definition for all filters. +*/ + +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 ); + + // track and in/out points + mlt_service producer; + int track; + mlt_timecode in; + mlt_timecode out; + + // Protected + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_filter_init( mlt_filter this, void *child ); +extern mlt_service mlt_filter_service( mlt_filter this ); +extern int mlt_filter_connect( mlt_filter this, mlt_service producer, int index ); +extern void mlt_filter_set_in_and_out( mlt_filter this, mlt_timecode in, mlt_timecode out ); +extern int mlt_filter_get_track( mlt_filter this ); +extern mlt_timecode mlt_filter_get_in( mlt_filter this ); +extern mlt_timecode mlt_filter_get_out( mlt_filter this ); +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 index 00000000..9c9eac14 --- /dev/null +++ b/src/framework/mlt_frame.c @@ -0,0 +1,415 @@ +/* + * mlt_frame.c -- interface for all frame classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_frame.h" +#include +#include +#include + +typedef struct +{ + mlt_image_format vfmt; + int width; + int height; + uint8_t *image; + uint8_t *alpha; + mlt_audio_format afmt; + int16_t *audio; +} +frame_test; + +static frame_test test_card = { mlt_image_none, 0, 0, NULL, NULL, mlt_audio_none, NULL }; + +/** Constructor for a frame. +*/ + +mlt_frame mlt_frame_init( ) +{ + // Allocate a frame + mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 ); + + if ( this != NULL ) + { + // Initialise the properties + mlt_properties properties = &this->parent; + mlt_properties_init( properties, this ); + + // Set default properties on the frame + mlt_properties_set_timecode( properties, "timecode", 0.0 ); + mlt_properties_set_data( properties, "image", NULL, 0, NULL, NULL ); + mlt_properties_set_int( properties, "width", 720 ); + mlt_properties_set_int( properties, "height", 576 ); + mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 ); + mlt_properties_set_data( properties, "audio", NULL, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL ); + } + return this; +} + +/** Fetch the frames properties. +*/ + +mlt_properties mlt_frame_properties( mlt_frame this ) +{ + return &this->parent; +} + +/** Check if we have a way to derive something other than a test card. +*/ + +int mlt_frame_is_test_card( mlt_frame this ) +{ + return this->stack_get_image_size == 0; +} + +/** Get the aspect ratio of the frame. +*/ + +double mlt_frame_get_aspect_ratio( mlt_frame this ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_get_double( properties, "aspect_ratio" ); +} + +/** Set the aspect ratio of the frame. +*/ + +int mlt_frame_set_aspect_ratio( mlt_frame this, double value ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_set_double( properties, "aspect_ratio", value ); +} + +/** Get the timecode of this frame. +*/ + +mlt_timecode mlt_frame_get_timecode( mlt_frame this ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_get_timecode( properties, "timecode" ); +} + +/** Set the timecode of this frame. +*/ + +int mlt_frame_set_timecode( mlt_frame this, mlt_timecode value ) +{ + mlt_properties properties = mlt_frame_properties( this ); + return mlt_properties_set_timecode( properties, "timecode", value ); +} + +/** Stack a get_image callback. +*/ + +int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ) +{ + int ret = this->stack_get_image_size >= 10; + if ( ret == 0 ) + this->stack_get_image[ this->stack_get_image_size ++ ] = get_image; + return ret; +} + +/** Pop a get_image callback. +*/ + +mlt_get_image mlt_frame_pop_get_image( mlt_frame this ) +{ + mlt_get_image result = NULL; + if ( this->stack_get_image_size > 0 ) + result = this->stack_get_image[ -- this->stack_get_image_size ]; + return result; +} + +/** Push a frame. +*/ + +int mlt_frame_push_frame( mlt_frame this, mlt_frame that ) +{ + int ret = this->stack_frame_size >= 10; + if ( ret == 0 ) + this->stack_frame[ this->stack_frame_size ++ ] = that; + return ret; +} + +/** Pop a frame. +*/ + +mlt_frame mlt_frame_pop_frame( mlt_frame this ) +{ + mlt_frame result = NULL; + if ( this->stack_frame_size > 0 ) + result = this->stack_frame[ -- this->stack_frame_size ]; + return result; +} + +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 ); + + if ( get_image != NULL ) + { + return get_image( this, buffer, format, width, height, writable ); + } + else if ( mlt_properties_get_data( properties, "image", NULL ) != NULL ) + { + *format = mlt_image_yuv422; + *buffer = mlt_properties_get_data( properties, "image", NULL ); + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + } + else + { + if ( test_card.vfmt != *format ) + { + uint8_t *p; + uint8_t *q; + + test_card.vfmt = *format; + test_card.width = 720; + test_card.height = 576; + + switch( *format ) + { + case mlt_image_none: + break; + case mlt_image_rgb24: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 3 ); + memset( test_card.image, 255, test_card.width * test_card.height * 3 ); + break; + case mlt_image_rgb24a: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 4 ); + memset( test_card.image, 255, test_card.width * test_card.height * 4 ); + break; + case mlt_image_yuv422: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 2 ); + p = test_card.image; + q = test_card.image + test_card.width * test_card.height * 2; + while ( p != q ) + { + *p ++ = 255; + *p ++ = 128; + } + break; + case mlt_image_yuv420p: + test_card.image = realloc( test_card.image, test_card.width * test_card.height * 3 / 2 ); + memset( test_card.image, 255, test_card.width * test_card.height * 3 / 2 ); + break; + } + } + + *width = test_card.width; + *height = test_card.height; + *buffer = test_card.image; + } + + return 0; +} + +uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ) +{ + if ( this->get_alpha_mask != NULL ) + return this->get_alpha_mask( this ); + return test_card.alpha; +} + +int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + if ( this->get_audio != NULL ) + { + return this->get_audio( this, buffer, format, frequency, channels, samples ); + } + else + { + if ( test_card.afmt != *format ) + { + test_card.afmt = *format; + test_card.audio = realloc( test_card.audio, 1920 * 2 * sizeof( int16_t ) ); + memset( test_card.audio, 0, 1920 * 2 * sizeof( int16_t ) ); + } + + *buffer = test_card.audio; + *frequency = 48000; + *channels = 2; + *samples = 1920; + } + return 0; +} + +void mlt_frame_close( mlt_frame this ) +{ + mlt_frame frame = mlt_frame_pop_frame( this ); + + while ( frame != NULL ) + { + mlt_frame_close( frame); + frame = mlt_frame_pop_frame( this ); + } + + mlt_properties_close( &this->parent ); + + free( this ); +} + +/***** convenience functions *****/ +#define RGB2YUV(r, g, b, y, u, v)\ + y = (306*r + 601*g + 117*b) >> 10;\ + u = ((-172*r - 340*g + 512*b) >> 10) + 128;\ + v = ((512*r - 429*g - 83*b) >> 10) + 128;\ + y = y < 0 ? 0 : y;\ + u = u < 0 ? 0 : u;\ + v = v < 0 ? 0 : v;\ + y = y > 255 ? 255 : y;\ + u = u > 255 ? 255 : u;\ + v = v > 255 ? 255 : v + +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; + + 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; + } + } + 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; + } + } + return ret; +} + +int mlt_frame_composite_yuv( mlt_frame this, mlt_frame that, int x, int y, float weight ) +{ + int ret = 0; + int x_start = 0; + int width_src, height_src; + int width_dest, height_dest; + mlt_image_format format_src, format_dest; + uint8_t *p_src, *p_dest; + int x_end; + int i, j; + int stride_src; + int stride_dest; + + format_src = mlt_image_yuv422; + format_dest = mlt_image_yuv422; + + mlt_frame_get_image( this, &p_dest, &format_dest, &width_dest, &height_dest, 1 /* writable */ ); + mlt_frame_get_image( that, &p_src, &format_src, &width_src, &height_src, 0 /* writable */ ); + + x_end = width_src; + + stride_src = width_src * 2; + stride_dest = width_dest * 2; + uint8_t *lower = p_dest; + uint8_t *upper = p_dest + height_dest * stride_dest; + + p_dest += ( y * stride_dest ) + ( x * 2 ); + + if ( x < 0 ) + { + x_start = -x; + x_end += x_start; + } + + uint8_t *z = mlt_frame_get_alpha_mask( that ); + + for ( i = 0; i < height_src; i++ ) + { + uint8_t *p = p_src; + uint8_t *q = p_dest; + uint8_t *o = p_dest; + + for ( j = 0; j < width_src; j ++ ) + { + if ( q >= lower && q < upper && j >= x_start && j < x_end ) + { + uint8_t y = *p ++; + uint8_t uv = *p ++; + uint8_t a = ( z == NULL ) ? 255 : *z ++; + float value = ( weight * ( float ) a / 255.0 ); + *o ++ = (uint8_t)( y * value + *q++ * ( 1 - value ) ); + *o ++ = (uint8_t)( uv * value + *q++ * ( 1 - value ) ); + } + else + { + p += 2; + o += 2; + q += 2; + if ( z != NULL ) + z += 1; + } + } + + p_src += stride_src; + p_dest += stride_dest; + } + + return ret; +} + diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h new file mode 100644 index 00000000..fd86f232 --- /dev/null +++ b/src/framework/mlt_frame.h @@ -0,0 +1,85 @@ +/* + * mlt_frame.h -- interface for all frame classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_FRAME_H_ +#define _MLT_FRAME_H_ + +#include "mlt_properties.h" + +typedef enum +{ + mlt_image_none = 0, + mlt_image_rgb24, + mlt_image_rgb24a, + mlt_image_yuv422, + mlt_image_yuv420p +} +mlt_image_format; + +typedef enum +{ + mlt_audio_none = 0, + mlt_audio_pcm +} +mlt_audio_format; + +typedef int ( *mlt_get_image )( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); + +struct mlt_frame_s +{ + // We're extending properties here + struct mlt_properties_s parent; + + // Virtual methods + int ( *get_audio )( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); + uint8_t * ( *get_alpha_mask )( mlt_frame this ); + + // Private properties + mlt_get_image stack_get_image[ 10 ]; + int stack_get_image_size; + mlt_frame stack_frame[ 10 ]; + int stack_frame_size; +}; + +extern mlt_frame mlt_frame_init( ); +extern mlt_properties mlt_frame_properties( mlt_frame this ); +extern int mlt_frame_is_test_card( mlt_frame this ); +extern double mlt_frame_get_aspect_ratio( mlt_frame this ); +extern int mlt_frame_set_aspect_ratio( mlt_frame this, double value ); +extern mlt_timecode mlt_frame_get_timecode( mlt_frame this ); +extern int mlt_frame_set_timecode( mlt_frame this, mlt_timecode value ); + +extern int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); +extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ); +extern int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); + +extern int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ); +extern mlt_get_image mlt_frame_pop_get_image( mlt_frame this ); +extern int mlt_frame_push_frame( mlt_frame this, mlt_frame that ); +extern mlt_frame mlt_frame_pop_frame( mlt_frame this ); +extern void mlt_frame_close( mlt_frame this ); + +/* convenience functions */ +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_frame_composite_yuv( mlt_frame this, mlt_frame that, int x, int y, float weight ); + +#endif + diff --git a/src/framework/mlt_manager.h b/src/framework/mlt_manager.h new file mode 100644 index 00000000..1567e644 --- /dev/null +++ b/src/framework/mlt_manager.h @@ -0,0 +1,26 @@ +/* + * mlt_manager.h -- manager service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_MANAGER_H_ +#define _MLT_MANAGER_H_ + +mlt_producer mlt_manager_init( char **config ); + +#endif diff --git a/src/framework/mlt_multitrack.c b/src/framework/mlt_multitrack.c new file mode 100644 index 00000000..77158c76 --- /dev/null +++ b/src/framework/mlt_multitrack.c @@ -0,0 +1,259 @@ +/* + * mlt_multitrack.c -- multitrack service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_multitrack.h" +#include "mlt_frame.h" + +#include +#include + +/** Private definition. +*/ + +struct mlt_multitrack_s +{ + // We're extending producer here + struct mlt_producer_s parent; + mlt_producer *list; + int size; + int count; +}; + +/** Forward reference. +*/ + +static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); + +/** Constructor. +*/ + +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 ) + { + producer->get_frame = producer_get_frame; + } + else + { + free( this ); + this = NULL; + } + } + + return this; +} + +/** Get the producer associated to this multitrack. +*/ + +mlt_producer mlt_multitrack_producer( mlt_multitrack this ) +{ + return &this->parent; +} + +/** Get the service associated this multitrack. +*/ + +mlt_service mlt_multitrack_service( mlt_multitrack this ) +{ + return mlt_producer_service( mlt_multitrack_producer( this ) ); +} + +/** Get the properties associated this multitrack. +*/ + +mlt_properties mlt_multitrack_properties( mlt_multitrack this ) +{ + return mlt_service_properties( mlt_multitrack_service( this ) ); +} + +/** Initialise timecode related information. +*/ + +static 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_timecode length = 0; + + // We need to ensure that fps are the same on all services + double fps = 0; + + // Obtain stats on all connected services + for ( i = 0; i < this->count; i ++ ) + { + // Get the producer from this index + mlt_producer producer = this->list[ i ]; + + // If it's allocated then, update our stats + if ( producer != NULL ) + { + // Determine the longest length + length = mlt_producer_get_length( producer ) > length ? mlt_producer_get_length( producer ) : length; + + // Handle fps + if ( fps == 0 ) + { + // This is the first producer, so it controls the fps + fps = mlt_producer_get_fps( producer ); + } + else if ( fps != mlt_producer_get_fps( producer ) ) + { + // Generate a warning for now - the following attempt to fix may fail + fprintf( stderr, "Warning: fps mismatch on track %d\n", i ); + + // It should be safe to impose fps on an image producer, but not necessarily safe for video + mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps ); + } + } + } + + // Update multitrack properties now - we'll not destroy the in point here + mlt_properties_set_timecode( properties, "length", length ); + mlt_properties_set_timecode( properties, "out", length ); + mlt_properties_set_timecode( properties, "playtime", length - mlt_properties_get_timecode( properties, "in" ) ); +} + +/** Connect a producer to a given track. +*/ + +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_producer ) ); + for ( i = this->size; i < track + 10; i ++ ) + this->list[ i ] = NULL; + this->size = track + 10; + } + + // Assign the track in our list here + this->list[ track ] = producer; + + // 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 frame method. + + 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. +*/ + +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 ]; + + // Obtain the current timecode + uint64_t position = mlt_producer_frame( parent ); + + // Make sure we're at the same point + mlt_producer_seek_frame( producer, position ); + + // Get the frame from the producer + mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 ); + } + else + { + // Generate a test frame + *frame = mlt_frame_init( ); + + // Let tractor know if we've reached the end + mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", index >= this->count ); + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *frame, mlt_producer_position( parent ) ); + + // Move on to the next frame + if ( index >= this->count ) + mlt_producer_prepare_next( parent ); + } + + fprintf( stderr, "timestamp for %d = %f\n", index, ( float )mlt_frame_get_timecode( *frame ) ); + + return 0; +} + +/** Close this instance. +*/ + +void mlt_multitrack_close( mlt_multitrack this ) +{ + // Close the producer + 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 index 00000000..9f85fc1a --- /dev/null +++ b/src/framework/mlt_multitrack.h @@ -0,0 +1,36 @@ +/* + * mlt_multitrack.h -- multitrack service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_MULITRACK_H_ +#define _MLT_MULITRACK_H_ + +#include "mlt_producer.h" + +/** Public final methods +*/ + +extern mlt_multitrack mlt_multitrack_init( ); +extern mlt_producer mlt_multitrack_producer( mlt_multitrack this ); +extern mlt_service mlt_multitrack_service( mlt_multitrack this ); +extern int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track ); +extern void mlt_multitrack_close( mlt_multitrack this ); + +#endif + diff --git a/src/framework/mlt_playlist.c b/src/framework/mlt_playlist.c new file mode 100644 index 00000000..ffcb3d56 --- /dev/null +++ b/src/framework/mlt_playlist.c @@ -0,0 +1,101 @@ +/* + * mlt_playlist.c -- playlist service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_playlist.h" + +/** Private definition. +*/ + +struct mlt_playlist_s +{ + struct mlt_producer_s parent; + int count; + int track; +}; + +/** Constructor. + + TODO: Override and implement all transport related method. + TODO: Override and implement a time code normalising service get_frame +*/ + +mlt_playlist mlt_playlist_init( ) +{ + mlt_playlist this = calloc( sizeof( struct mlt_playlist_s ), 1 ); + if ( this != NULL ) + { + mlt_producer producer = &this->parent; + if ( mlt_producer_init( producer, this ) != 0 ) + { + free( this ); + this = NULL; + } + } + + return this; +} + +/** Get the producer associated to this playlist. +*/ + +mlt_producer mlt_playlist_producer( mlt_playlist this ) +{ + return &this->parent; +} + +/** Get the service associated to this playlist. +*/ + +mlt_service mlt_playlist_service( mlt_playlist this ) +{ + return mlt_producer_service( &this->parent ); +} + +/** Append a producer to the playlist. + + TODO: Implement + TODO: Extract length information from the producer. +*/ + +int mlt_playlist_append( mlt_playlist this, mlt_producer producer ) +{ + return 0; +} + +/** Append a blank to the playlist of a given length. + + TODO: Implement +*/ + +int mlt_playlist_blank( mlt_playlist this, mlt_timecode length ) +{ + return 0; +} + +/** Close the playlist. +*/ + +mlt_playlist_close( mlt_playlist this ) +{ + mlt_producer_close( &this->parent ); + free( this ); +} diff --git a/src/framework/mlt_playlist.h b/src/framework/mlt_playlist.h new file mode 100644 index 00000000..aa916c8e --- /dev/null +++ b/src/framework/mlt_playlist.h @@ -0,0 +1,37 @@ +/* + * mlt_playlist.h -- playlist service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PLAYLIST_H_ +#define _MLT_PLAYLIST_H_ + +#include "mlt_producer.h" + +/** Public final methods +*/ + +extern mlt_playlist mlt_playlist_init( ); +extern mlt_producer mlt_playlist_producer( mlt_playlist this ); +extern mlt_service mlt_playlist_service( mlt_playlist this ); +extern int mlt_playlist_append( mlt_playlist this, mlt_producer producer ); +extern int mlt_playlist_pad( mlt_playlist this, mlt_timecode length ); +extern mlt_playlist_close( mlt_playlist this ); + +#endif + diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c new file mode 100644 index 00000000..91e39896 --- /dev/null +++ b/src/framework/mlt_producer.c @@ -0,0 +1,291 @@ +/* + * mlt_producer.c -- abstraction for all producer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_producer.h" +#include "mlt_frame.h" +#include +#include +#include +#include + +/** Forward references. +*/ + +static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor +*/ + +int mlt_producer_init( mlt_producer this, void *child ) +{ + // 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; + + // Get the properties of the parent + mlt_properties properties = mlt_service_properties( parent ); + + // Set the default properties + mlt_properties_set_timecode( properties, "position", 0.0 ); + mlt_properties_set_double( properties, "frame", 1 ); + mlt_properties_set_double( properties, "fps", 25.0 ); + mlt_properties_set_double( properties, "speed", 1.0 ); + mlt_properties_set_timecode( properties, "in", 0.0 ); + mlt_properties_set_timecode( properties, "out", 36000.0 ); + mlt_properties_set_timecode( properties, "playtime", 36000.0 ); + mlt_properties_set_timecode( properties, "length", 36000.0 ); + + // Override service get_frame + parent->get_frame = producer_get_frame; + } + + return 0; +} + +/** Get the parent service object. +*/ + +mlt_service mlt_producer_service( mlt_producer this ) +{ + return &this->parent; +} + +/** Get the producer properties. +*/ + +mlt_properties mlt_producer_properties( mlt_producer this ) +{ + return mlt_service_properties( &this->parent ); +} + +/** Seek to a specified time code. +*/ + +int mlt_producer_seek( mlt_producer this, mlt_timecode timecode ) +{ + // Check bounds + if ( timecode < 0 ) + timecode = 0; + if ( timecode > mlt_producer_get_playtime( this ) ) + timecode = mlt_producer_get_playtime( this ); + + // Set the position + mlt_properties_set_timecode( mlt_producer_properties( this ), "position", timecode ); + + // Calculate the absolute frame + double frame = ( mlt_producer_get_in( this ) + timecode ) * mlt_producer_get_fps( this ); + mlt_properties_set_double( mlt_producer_properties( this ), "frame", floor( frame + 0.5 ) ); + + return 0; +} + +/** Seek to a specified absolute frame. +*/ + +int mlt_producer_seek_frame( mlt_producer this, uint64_t frame ) +{ + // Calculate the time code + double timecode = ( frame / mlt_producer_get_fps( this ) ) - mlt_producer_get_in( this ); + + // If timecode is invalid, then seek on time + if ( timecode < 0 ) + { + // Seek to the in point + mlt_producer_seek( this, 0 ); + } + else if ( timecode > mlt_producer_get_playtime( this ) ) + { + // Seek to the out point + mlt_producer_seek( this, mlt_producer_get_playtime( this ) ); + } + else + { + // Set the position + mlt_properties_set_timecode( mlt_producer_properties( this ), "position", timecode ); + + // Set the absolute frame + mlt_properties_set_double( mlt_producer_properties( this ), "frame", frame ); + } + + return 0; +} + +/** Get the current time code. +*/ + +mlt_timecode mlt_producer_position( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "position" ); +} + +/** Get the current frame. +*/ + +uint64_t mlt_producer_frame( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "frame" ); +} + +/** Set the playing speed. +*/ + +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. +*/ + +double mlt_producer_get_speed( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "speed" ); +} + +/** Get the frames per second. +*/ + +double mlt_producer_get_fps( mlt_producer this ) +{ + return mlt_properties_get_double( mlt_producer_properties( this ), "fps" ); +} + +/** Set the in and out points. +*/ + +int mlt_producer_set_in_and_out( mlt_producer this, mlt_timecode in, mlt_timecode out ) +{ + // Correct ins and outs if necessary + if ( in < 0 ) + in = 0; + if ( in > mlt_producer_get_length( this ) ) + in = mlt_producer_get_length( this ); + if ( out < 0 ) + out = 0; + if ( out > mlt_producer_get_length( this ) ) + out = mlt_producer_get_length( this ); + + // Swap ins and outs if wrong + if ( out < in ) + { + mlt_timecode t = in; + in = out; + out = t; + } + + // Set the values + mlt_properties_set_timecode( mlt_producer_properties( this ), "in", in ); + mlt_properties_set_timecode( mlt_producer_properties( this ), "out", out ); + mlt_properties_set_timecode( mlt_producer_properties( this ), "playtime", out - in ); + + // Seek to the in point + mlt_producer_seek( this, 0 ); + + return 0; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_producer_get_in( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "in" ); +} + +/** Get the out point. +*/ + +mlt_timecode mlt_producer_get_out( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "out" ); +} + +/** Get the total play time. +*/ + +mlt_timecode mlt_producer_get_playtime( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "playtime" ); +} + +/** Get the total length of the producer. +*/ + +mlt_timecode mlt_producer_get_length( mlt_producer this ) +{ + return mlt_properties_get_timecode( mlt_producer_properties( this ), "length" ); +} + +/** Prepare for next frame. +*/ + +void mlt_producer_prepare_next( mlt_producer this ) +{ + mlt_producer_seek_frame( this, mlt_producer_frame( this ) + mlt_producer_get_speed( this ) ); +} + +/** Get a frame. +*/ + +static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + int result = 1; + mlt_producer this = service->child; + + // A properly instatiated producer will have a get_frame method... + if ( this->get_frame != NULL ) + { + // Get the frame from the implementation + result = this->get_frame( this, frame, index ); + } + else + { + // Generate a test frame + *frame = mlt_frame_init( ); + + // Set the timecode + result = mlt_frame_set_timecode( *frame, mlt_producer_position( this ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( this ); + } + + return 0; +} + +/** Close the producer. +*/ + +void mlt_producer_close( mlt_producer this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} diff --git a/src/framework/mlt_producer.h b/src/framework/mlt_producer.h new file mode 100644 index 00000000..a59a82d2 --- /dev/null +++ b/src/framework/mlt_producer.h @@ -0,0 +1,64 @@ +/* + * mlt_producer.h -- abstraction for all producer services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PRODUCER_H_ +#define _MLT_PRODUCER_H_ + +#include "mlt_service.h" + +/** The interface definition for all producers. +*/ + +struct mlt_producer_s +{ + // We're implementing service here + struct mlt_service_s parent; + + // Public virtual methods + int ( *get_frame )( mlt_producer, mlt_frame_ptr, int ); + void ( *close )( mlt_producer ); + + // Private data + void *private; + void *child; +}; + +/** Public final methods +*/ + +extern int mlt_producer_init( mlt_producer this, void *child ); +extern mlt_service mlt_producer_service( mlt_producer this ); +extern mlt_properties mlt_producer_properties( mlt_producer this ); +extern int mlt_producer_seek( mlt_producer this, mlt_timecode timecode ); +extern int mlt_producer_seek_frame( mlt_producer this, uint64_t frame ); +extern mlt_timecode mlt_producer_position( mlt_producer this ); +extern uint64_t mlt_producer_frame( mlt_producer this ); +extern int mlt_producer_set_speed( mlt_producer this, double speed ); +extern double mlt_producer_get_speed( mlt_producer this ); +extern double mlt_producer_get_fps( mlt_producer this ); +extern int mlt_producer_set_in_and_out( mlt_producer this, mlt_timecode in, mlt_timecode out ); +extern mlt_timecode mlt_producer_get_in( mlt_producer this ); +extern mlt_timecode mlt_producer_get_out( mlt_producer this ); +extern mlt_timecode mlt_producer_get_playtime( mlt_producer this ); +extern mlt_timecode mlt_producer_get_length( mlt_producer this ); +extern void mlt_producer_prepare_next( mlt_producer this ); +extern void mlt_producer_close( mlt_producer this ); + +#endif diff --git a/src/framework/mlt_properties.c b/src/framework/mlt_properties.c new file mode 100644 index 00000000..233e8ace --- /dev/null +++ b/src/framework/mlt_properties.c @@ -0,0 +1,324 @@ +/* + * mlt_properties.c -- base properties class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_properties.h" +#include "mlt_property.h" + +#include +#include +#include + +/* ---------------- // Private Implementation // ---------------- */ + +/** Private implementation of the property list. +*/ + +typedef struct +{ + char **name; + mlt_property *value; + int count; + int size; +} +property_list; + +/** Basic implementation. +*/ + +int mlt_properties_init( mlt_properties this, void *child ) +{ + // NULL all methods + memset( this, 0, sizeof( struct mlt_properties_s ) ); + + // Assign the child of the object + this->child = child; + + // Allocate the private structure + this->private = calloc( sizeof( property_list ), 1 ); + + return this->private == NULL; +} + +/** Locate a property by name +*/ + +static mlt_property mlt_properties_find( mlt_properties this, char *name ) +{ + mlt_property value = NULL; + property_list *list = this->private; + int i = 0; + + // Locate the item + for ( i = 0; value == NULL && i < list->count; i ++ ) + if ( !strcmp( list->name[ i ], name ) ) + value = list->value[ i ]; + + return value; +} + +/** Add a new property. +*/ + +static mlt_property mlt_properties_add( mlt_properties this, char *name ) +{ + property_list *list = this->private; + + // Check that we have space and resize if necessary + if ( list->count == list->size ) + { + list->size += 10; + list->name = realloc( list->name, list->size * sizeof( 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( ); + + // Return and increment count accordingly + return list->value[ list->count ++ ]; +} + +/** Fetch a property by name - this includes add if not found. +*/ + +static mlt_property mlt_properties_fetch( mlt_properties this, 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; +} + +/** Set the property. +*/ + +int mlt_properties_set( mlt_properties this, char *name, 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 ) + error = mlt_property_set_string( property, value ); + + return error; +} + +/** Get a string value by name. +*/ + +char *mlt_properties_get( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? NULL : mlt_property_get_string( value ); +} + +/** Get a name by index. +*/ + +char *mlt_properties_get_name( mlt_properties this, int index ) +{ + property_list *list = this->private; + if ( index >= 0 && index < list->count ) + return list->name[ index ]; + return NULL; +} + +/** Get a string value by index. +*/ + +char *mlt_properties_get_value( mlt_properties this, int index ) +{ + property_list *list = this->private; + if ( index >= 0 && index < list->count ) + return mlt_property_get_string( list->value[ index ] ); + return NULL; +} + +/** Return the number of items in the list. +*/ + +int mlt_properties_count( mlt_properties this ) +{ + property_list *list = this->private; + return list->count; +} + +/** Set a value by parsing a name=value string +*/ + +int mlt_properties_parse( mlt_properties this, char *namevalue ) +{ + char *name = strdup( namevalue ); + char *value = strdup( namevalue ); + int error = 0; + + if ( strchr( name, '=' ) ) + { + *( strchr( name, '=' ) ) = '\0'; + strcpy( value, strchr( value, '=' ) + 1 ); + } + else + { + strcpy( value, "" ); + } + + error = mlt_properties_set( this, name, value ); + + free( name ); + free( value ); + + return error; +} + +/** Get a value associated to the name. +*/ + +int mlt_properties_get_int( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_int( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_int( mlt_properties this, 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 ); + + return error; +} + +/** Get a value associated to the name. +*/ + +double mlt_properties_get_double( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_double( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_double( mlt_properties this, 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 ); + + return error; +} + +/** Get a value associated to the name. +*/ + +mlt_timecode mlt_properties_get_timecode( mlt_properties this, char *name ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? 0 : mlt_property_get_timecode( value ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_timecode( mlt_properties this, char *name, mlt_timecode 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_timecode( property, value ); + + return error; +} + +/** Get a value associated to the name. +*/ + +void *mlt_properties_get_data( mlt_properties this, char *name, int *length ) +{ + mlt_property value = mlt_properties_find( this, name ); + return value == NULL ? NULL : mlt_property_get_data( value, length ); +} + +/** Set a value associated to the name. +*/ + +int mlt_properties_set_data( mlt_properties this, 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 ); + + return error; +} + +/** Close the list. +*/ + +void mlt_properties_close( mlt_properties this ) +{ + property_list *list = this->private; + int index = 0; + + // Clean up names and values + for ( index = 0; index < list->count; index ++ ) + { + free( list->name[ index ] ); + mlt_property_close( list->value[ index ] ); + } + + // Clear up the list + free( list->name ); + free( list->value ); + free( list ); +} + diff --git a/src/framework/mlt_properties.h b/src/framework/mlt_properties.h new file mode 100644 index 00000000..b64753bc --- /dev/null +++ b/src/framework/mlt_properties.h @@ -0,0 +1,56 @@ +/* + * mlt_properties.h -- base properties class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PROPERTIES_H_ +#define _MLT_PROPERTIES_H_ + +#include "mlt_types.h" + +/** The properties base class defines the basic property propagation and + handling. +*/ + +struct mlt_properties_s +{ + void *child; + void *private; +}; + +/** Public interface. +*/ + +extern int mlt_properties_init( mlt_properties, void *child ); +extern int mlt_properties_set( mlt_properties this, char *name, char *value ); +extern int mlt_properties_parse( mlt_properties this, char *namevalue ); +extern char *mlt_properties_get( mlt_properties this, char *name ); +extern char *mlt_properties_get_name( mlt_properties this, int index ); +extern char *mlt_properties_get_value( mlt_properties this, int index ); +extern int mlt_properties_get_int( mlt_properties this, char *name ); +extern int mlt_properties_set_int( mlt_properties this, char *name, int value ); +extern double mlt_properties_get_double( mlt_properties this, char *name ); +extern int mlt_properties_set_double( mlt_properties this, char *name, double value ); +extern mlt_timecode mlt_properties_get_timecode( mlt_properties this, char *name ); +extern int mlt_properties_set_timecode( mlt_properties this, char *name, mlt_timecode value ); +extern int mlt_properties_set_data( mlt_properties this, char *name, void *value, int length, mlt_destructor, mlt_serialiser ); +extern void *mlt_properties_get_data( mlt_properties this, char *name, int *length ); +extern int mlt_properties_count( mlt_properties this ); +extern void mlt_properties_close( mlt_properties this ); + +#endif diff --git a/src/framework/mlt_property.c b/src/framework/mlt_property.c new file mode 100644 index 00000000..021dbc17 --- /dev/null +++ b/src/framework/mlt_property.c @@ -0,0 +1,220 @@ +/* + * mlt_property.c -- property class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_property.h" + +#include +#include +#include + +/** Construct and uninitialised property. +*/ + +mlt_property mlt_property_init( ) +{ + return calloc( sizeof( struct mlt_property_s ), 1 ); +} + +/** Clear a property. +*/ + +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 ); + + // We can wipe it now. + memset( this, 0, sizeof( struct mlt_property_s ) ); +} + +/** Set an int on this property. +*/ + +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 a double on this property. +*/ + +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 a timecode on this property. +*/ + +int mlt_property_set_timecode( mlt_property this, mlt_timecode value ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_timecode; + this->prop_timecode = value; + return 0; +} + +/** Set a string on this property. +*/ + +int mlt_property_set_string( mlt_property this, char *value ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_string; + if ( value != NULL ) + this->prop_string = strdup( value ); + return this->prop_string != NULL; +} + +/** Set a data on this property. +*/ + +int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser ) +{ + mlt_property_clear( this ); + this->types = mlt_prop_data; + this->data = value; + this->length = length; + this->destructor = destructor; + this->serialiser = serialiser; + return 0; +} + +/** Get an int from this property. +*/ + +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_timecode ) + return ( int )this->prop_timecode; + else if ( this->types & mlt_prop_string ) + return atoi( this->prop_string ); + return 0; +} + +/** Get a double from this property. +*/ + +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_timecode ) + return ( double )this->prop_timecode; + else if ( this->types & mlt_prop_string ) + return atof( this->prop_string ); + return 0; +} + +/** Get a timecode from this property. +*/ + +mlt_timecode mlt_property_get_timecode( mlt_property this ) +{ + if ( this->types & mlt_prop_timecode ) + return this->prop_timecode; + else if ( this->types & mlt_prop_int ) + return ( mlt_timecode )this->prop_int; + else if ( this->types & mlt_prop_double ) + return ( mlt_timecode )this->prop_double; + else if ( this->types & mlt_prop_string ) + return ( mlt_timecode )atof( this->prop_string ); + return 0; +} + +/** Get a string from this property. +*/ + +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, "%e", this->prop_double ); + } + else if ( this->types & mlt_prop_timecode ) + { + this->types |= mlt_prop_string; + this->prop_string = malloc( 32 ); + sprintf( this->prop_string, "%e", this->prop_timecode ); + } + 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 a data and associated length. +*/ + +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; +} + +/** Close this property. +*/ + +void mlt_property_close( mlt_property this ) +{ + mlt_property_clear( this ); + free( this ); +} + + diff --git a/src/framework/mlt_property.h b/src/framework/mlt_property.h new file mode 100644 index 00000000..8fb209c4 --- /dev/null +++ b/src/framework/mlt_property.h @@ -0,0 +1,82 @@ +/* + * mlt_property.h -- property class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_PROPERTY_H_ +#define _MLT_PROPERTY_H_ + +#include "mlt_types.h" + +/** Bit pattern for properties. +*/ + +typedef enum +{ + mlt_prop_none = 0, + mlt_prop_int = 1, + mlt_prop_string = 2, + mlt_prop_timecode = 4, + mlt_prop_double = 8, + mlt_prop_data = 16 +} +mlt_property_type; + +/** Property structure. +*/ + +typedef 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_timecode prop_timecode; + double prop_double; + + // String handling + char *prop_string; + + // Generic type handling + void *data; + int length; + mlt_destructor destructor; + mlt_serialiser serialiser; +} +*mlt_property; + +/** API +*/ + +extern mlt_property mlt_property_init( ); +extern void mlt_property_clear( mlt_property this ); +extern int mlt_property_set_int( mlt_property this, int value ); +extern int mlt_property_set_double( mlt_property this, double value ); +extern int mlt_property_set_timecode( mlt_property this, mlt_timecode value ); +extern int mlt_property_set_string( mlt_property this, char *value ); +extern int mlt_property_set_data( mlt_property this, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser ); +extern int mlt_property_get_int( mlt_property this ); +extern double mlt_property_get_double( mlt_property this ); +extern mlt_timecode mlt_property_get_timecode( mlt_property this ); +extern char *mlt_property_get_string( mlt_property this ); +extern void *mlt_property_get_data( mlt_property this, int *length ); +extern void mlt_property_close( mlt_property this ); + +#endif + diff --git a/src/framework/mlt_repository.c b/src/framework/mlt_repository.c new file mode 100644 index 00000000..9c41bf92 --- /dev/null +++ b/src/framework/mlt_repository.c @@ -0,0 +1,42 @@ +/* + * repository.c -- provides a map between service and shared objects + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mlt_repository.h" +#include + +struct mlt_repository_s +{ +}; + +mlt_repository mlt_repository_init( char *file, char *symbol ) +{ + return NULL; +} + +void *mlt_repository_fetch( mlt_repository this, char *service, void *input ) +{ + return NULL; +} + +void mlt_repository_close( mlt_repository this ) +{ +} + + diff --git a/src/framework/mlt_repository.h b/src/framework/mlt_repository.h new file mode 100644 index 00000000..afab1cc8 --- /dev/null +++ b/src/framework/mlt_repository.h @@ -0,0 +1,37 @@ +/* + * repository.h -- provides a map between service and shared objects + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_REPOSITORY_H_ +#define _MLT_REPOSITORY_H_ + +/** Repository structure forward reference. +*/ + +typedef struct mlt_repository_s *mlt_repository; + +/** Public functions. +*/ + +extern mlt_repository mlt_repository_init( char *file, char *symbol ); +extern void *mlt_repository_fetch( mlt_repository this, char *service, void *input ); +extern void mlt_repository_close( mlt_repository this ); + +#endif + diff --git a/src/framework/mlt_service.c b/src/framework/mlt_service.c new file mode 100644 index 00000000..8e626d41 --- /dev/null +++ b/src/framework/mlt_service.c @@ -0,0 +1,275 @@ +/* + * mlt_service.c -- interface for all service classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "mlt_service.h" +#include "mlt_frame.h" +#include +#include +#include + +/** 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. +*/ + +/** Private service definition. +*/ + +typedef struct +{ + int size; + int count; + mlt_service *in; + mlt_service out; +} +mlt_service_base; + +/** Friends? +*/ + +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 ); + +/** Constructor +*/ + +int mlt_service_init( mlt_service this, void *child ) +{ + // Initialise everything to NULL + memset( this, 0, sizeof( struct mlt_service_s ) ); + + // Assign the child + this->child = child; + + // Generate private space + this->private = calloc( sizeof( mlt_service_base ), 1 ); + + // Associate the methods + this->get_frame = service_get_frame; + + // Initialise the properties + return mlt_properties_init( &this->parent, this ); +} + +/** Return the properties object. +*/ + +mlt_properties mlt_service_properties( mlt_service this ) +{ + return &this->parent; +} + +/** Connect a producer service. + Returns: > 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->private; + + // Does this service accept input? + if ( mlt_service_accepts_input( this ) == 0 ) + return 1; + + // Does the producer service accept output connections? + if ( mlt_service_accepts_output( producer ) == 0 ) + return 2; + + // 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 ) + { + // 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 ); + + // Inform caller that all went well + return 0; + } + else + { + return -1; + } +} + +/** Disconnect this service from its consumer. +*/ + +void mlt_service_disconnect( mlt_service this ) +{ + // Get the service base + mlt_service_base *base = this->private; + + // There's a bit more required here... + base->out = NULL; +} + +/** Associate this service to the its consumer. +*/ + +void mlt_service_connect( mlt_service this, mlt_service that ) +{ + // Get the service base + mlt_service_base *base = this->private; + + // There's a bit more required here... + base->out = that; +} + +/** Get the service state. +*/ + +mlt_service_state mlt_service_get_state( mlt_service this ) +{ + mlt_service_state state = mlt_state_unknown; + if ( mlt_service_has_input( this ) ) + state |= mlt_state_providing; + if ( mlt_service_has_output( this ) ) + state |= mlt_state_connected; + if ( state != ( mlt_state_providing | mlt_state_connected ) ) + state |= mlt_state_dormant; + return state; +} + +/** Get the maximum number of inputs accepted. + Returns: -1 for many, 0 for none or n for fixed. +*/ + +int mlt_service_accepts_input( mlt_service this ) +{ + if ( this->accepts_input == NULL ) + return -1; + else + return this->accepts_input( this ); +} + +/** Get the maximum number of outputs accepted. +*/ + +int mlt_service_accepts_output( mlt_service this ) +{ + if ( this->accepts_output == NULL ) + return 1; + else + return this->accepts_output( this ); +} + +/** Determines if this service has input +*/ + +int mlt_service_has_input( mlt_service this ) +{ + if ( this->has_input == NULL ) + return 1; + else + return this->has_input( this ); +} + +/** Determine if this service has output +*/ + +int mlt_service_has_output( mlt_service this ) +{ + mlt_service_base *base = this->private; + if ( this->has_output == NULL ) + return base->out != NULL; + else + return this->has_output( this ); +} + +/** Check if the service is active. +*/ + +int mlt_service_is_active( mlt_service this ) +{ + return !( mlt_service_get_state( this ) & mlt_state_dormant ); +} + +/** Obtain a frame to pass on. +*/ + +static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) +{ + mlt_service_base *base = this->private; + 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( ); + return 0; +} + +int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) +{ + return this->get_frame( this, frame, index ); +} + +/** Close the service. +*/ + +void mlt_service_close( mlt_service this ) +{ + mlt_service_base *base = this->private; + free( base->in ); + free( base ); + mlt_properties_close( &this->parent ); +} + diff --git a/src/framework/mlt_service.h b/src/framework/mlt_service.h new file mode 100644 index 00000000..88b91b50 --- /dev/null +++ b/src/framework/mlt_service.h @@ -0,0 +1,79 @@ +/* + * mlt_service.h -- interface for all service classes + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_SERVICE_H_ +#define _MLT_SERVICE_H_ + +#include "mlt_properties.h" + +/** State of a service. + + Note that a service may be dormant even though it's fully connected, + providing or consuming. +*/ + +typedef enum +{ + mlt_state_unknown = 0, + mlt_state_dormant = 1, + mlt_state_connected = 2, + mlt_state_providing = 4, + mlt_state_consuming = 8 +} +mlt_service_state; + +/** The interface definition for all services. +*/ + +struct mlt_service_s +{ + // We're extending properties here + struct mlt_properties_s parent; + + // Protected virtual + int ( *accepts_input )( mlt_service this ); + int ( *accepts_output )( mlt_service this ); + int ( *has_input )( mlt_service this ); + int ( *has_output )( mlt_service this ); + int ( *get_frame )( mlt_service this, mlt_frame_ptr frame, int index ); + + // Private data + void *private; + void *child; +}; + +/** The public API. +*/ + +extern int mlt_service_init( mlt_service this, void *child ); +extern mlt_properties mlt_service_properties( mlt_service this ); +extern int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index ); +extern mlt_service_state mlt_service_get_state( mlt_service this ); +extern void mlt_service_close( mlt_service this ); + +extern int mlt_service_accepts_input( mlt_service this ); +extern int mlt_service_accepts_output( mlt_service this ); +extern int mlt_service_has_input( mlt_service this ); +extern int mlt_service_has_output( mlt_service this ); +extern int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); +extern int mlt_service_is_active( mlt_service this ); + +#endif + diff --git a/src/framework/mlt_tractor.c b/src/framework/mlt_tractor.c new file mode 100644 index 00000000..f4b77c46 --- /dev/null +++ b/src/framework/mlt_tractor.c @@ -0,0 +1,155 @@ +/* + * mlt_tractor.c -- tractor service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_tractor.h" +#include "mlt_frame.h" + +#include +#include + +/** Private structure. +*/ + +struct mlt_tractor_s +{ + struct mlt_service_s parent; + mlt_service producer; +}; + +/** Forward references to static methods. +*/ + +static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int track ); + +/** Constructor for the tractor. + + TODO: thread this service... +*/ + +mlt_tractor mlt_tractor_init( ) +{ + mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 ); + if ( this != NULL ) + { + mlt_service service = &this->parent; + if ( mlt_service_init( service, this ) == 0 ) + { + service->get_frame = service_get_frame; + } + else + { + free( this ); + this = NULL; + } + } + return this; +} + +/** Get the service object associated to the tractor. +*/ + +mlt_service mlt_tractor_service( mlt_tractor this ) +{ + return &this->parent; +} + +/** Connect the tractor. +*/ + +int mlt_tractor_connect( mlt_tractor this, mlt_service producer ) +{ + int ret = mlt_service_connect_producer( &this->parent, producer, 0 ); + + if ( ret == 0 ) + { + // This is the producer we're going to connect to + this->producer = producer; + } + + return ret; +} + +/** Get the next frame. + + TODO: This should be reading a pump being populated by the thread... +*/ + +static int service_get_frame( mlt_service 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 looking = 1; + int done = 0; + mlt_frame temp; + + // 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 ); + + // Check for last track + done = mlt_properties_get_int( mlt_frame_properties( temp ), "last_track" ); + + // Handle the frame + if ( done && looking ) + { + // Use this as output if we don't have one already + *frame = temp; + } + else if ( !mlt_frame_is_test_card( temp ) && looking ) + { + // This is the one we want and we can stop looking + *frame = temp; + looking = 0; + } + else + { + // We discard all other frames + mlt_frame_close( temp ); + } + } + + // Indicate our found status + return 0; + } + else + { + // Generate a test card + *frame = mlt_frame_init( ); + return 0; + } +} + +/** Close the tractor. +*/ + +void mlt_tractor_close( mlt_tractor this ) +{ + mlt_service_close( &this->parent ); + free( this ); +} + diff --git a/src/framework/mlt_tractor.h b/src/framework/mlt_tractor.h new file mode 100644 index 00000000..28a6071b --- /dev/null +++ b/src/framework/mlt_tractor.h @@ -0,0 +1,31 @@ +/* + * mlt_tractor.h -- tractor service class + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TRACTOR_H_ +#define _MLT_TRACTOR_H_ + +#include "mlt_producer.h" + +extern mlt_tractor mlt_tractor_init( ); +extern mlt_service mlt_tractor_service( mlt_tractor this ); +extern int mlt_tractor_connect( mlt_tractor this, mlt_service service ); +extern void mlt_tractor_close( mlt_tractor this ); + +#endif diff --git a/src/framework/mlt_transition.c b/src/framework/mlt_transition.c new file mode 100644 index 00000000..dd4f2f64 --- /dev/null +++ b/src/framework/mlt_transition.c @@ -0,0 +1,232 @@ +/* + * mlt_transition.c -- abstraction for all transition services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "mlt_transition.h" +#include "mlt_frame.h" + +#include +#include +#include + +/** Forward references. +*/ + +static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + +/** Constructor. +*/ + +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 ) + { + service->get_frame = transition_get_frame; + return 0; + } + return 1; +} + +/** Get the service associated to the transition. +*/ + +mlt_service mlt_transition_service( mlt_transition this ) +{ + return &this->parent; +} + +/** Connect this transition with a producers a and b tracks. +*/ + +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 ) + { + this->producer = producer; + this->a_track = a_track; + this->b_track = b_track; + this->in = 0; + this->out = 0; + } + return ret; +} + +/** Set the in and out points. +*/ + +void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out ) +{ + this->in = in; + this->out = out; +} + +/** Get the index of the a track. +*/ + +int mlt_transition_get_a_track( mlt_transition this ) +{ + return this->a_track; +} + +/** Get the index of the b track. +*/ + +int mlt_transition_get_b_track( mlt_transition this ) +{ + return this->b_track; +} + +/** Get the in point. +*/ + +mlt_timecode mlt_transition_get_in( mlt_transition this ) +{ + return this->in; +} + +/** Get the out point. +*/ + +mlt_timecode mlt_transition_get_out( mlt_transition this ) +{ + return this->out; +} + +/** Process the frame. +*/ + +static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame ) +{ + if ( this->process == NULL ) + { + if ( !mlt_frame_is_test_card( a_frame ) ) + { + mlt_frame_close( b_frame ); + return a_frame; + } + else + { + mlt_frame_close( a_frame ); + return b_frame; + } + } + else + { + return this->process( this, a_frame, b_frame ); + } +} + +/** Get a frame from this filter. + + The logic is complex here. A transition is applied to frames on the a and b tracks + specified in the connect method above. Since all frames are obtained via this + method for all tracks, we have to take special care that we only obtain the a and + b frames once - we do this on the first call to get a frame from either a or b. + + After that, we have 3 cases to resolve: + + 1) if the track is the a_track and we're in the time zone, then we need to call the + process method to do the effect on the frame (we assign NULL to the a_frame and + b_frames here) otherwise, we pass on the a_frame unmolested; + 2) if the track is the b_track and we're the in the time zone OR the b_frame is NULL, + then we generate a test card frame, otherwise we pass on the b frame unmolested; + 3) For all other tracks, we get the frames on demand. +*/ + +static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) +{ + mlt_transition this = service->child; + + // Fetch a and b frames together... + if ( ( index == this->a_track || index == this->b_track ) && + ( this->a_frame == NULL && this->b_frame == NULL ) ) + { + mlt_service_get_frame( this->producer, &this->a_frame, this->a_track ); + mlt_service_get_frame( this->producer, &this->b_frame, this->b_track ); + } + + // Special case track processing + if ( index == this->a_track ) + { + // Determine if we're in the right time zone + mlt_timecode timecode = mlt_frame_get_timecode( this->a_frame ); + if ( timecode >= this->in && timecode < this->out ) + { + // Process the transition + *frame = transition_process( this, this->a_frame, this->b_frame ); + + // Important - NULL both frames now so that we know they're done... + this->a_frame = NULL; + this->b_frame = NULL; + } + else + { + // Pass on the 'a frame' and remember that we've done it + *frame = this->a_frame; + this->a_frame = NULL; + } + return 0; + } + if ( index == this->b_track ) + { + if ( this->b_frame == NULL ) + { + // We're *probably* in the zone and the a frame has been requested + *frame = mlt_frame_init( ); + } + else + { + mlt_timecode timecode = mlt_frame_get_timecode( this->b_frame ); + if ( timecode >= this->in && timecode < this->out ) + { + // We're in the zone, but the 'a frame' has not been requested yet + *frame = mlt_frame_init( ); + } + else + { + // We're out of the zone, pass on b and remember that we've done it + *frame = this->b_frame; + this->b_frame = NULL; + } + } + return 0; + } + else + { + // Pass through + return mlt_service_get_frame( this->producer, frame, index ); + } +} + +/** Close the transition. +*/ + +void mlt_transitition_close( mlt_transition this ) +{ + if ( this->close != NULL ) + this->close( this ); + else + mlt_service_close( &this->parent ); +} diff --git a/src/framework/mlt_transition.h b/src/framework/mlt_transition.h new file mode 100644 index 00000000..397483f1 --- /dev/null +++ b/src/framework/mlt_transition.h @@ -0,0 +1,68 @@ +/* + * mlt_transition.h -- abstraction for all transition services + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TRANSITION_H_ +#define _MLT_TRANSITION_H_ + +#include "mlt_service.h" + +/** The interface definition for all transitions. +*/ + +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; + int a_track; + int b_track; + mlt_timecode in; + mlt_timecode out; + + // Private + mlt_frame a_frame; + mlt_frame b_frame; +}; + +/** Public final methods +*/ + +extern int mlt_transition_init( mlt_transition this, void *child ); +extern mlt_service mlt_transition_service( mlt_transition this ); +extern int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track ); +extern void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out ); +extern int mlt_transition_get_a_track( mlt_transition this ); +extern int mlt_transition_get_b_track( mlt_transition this ); +extern mlt_timecode mlt_transition_get_in( mlt_transition this ); +extern mlt_timecode mlt_transition_get_out( mlt_transition this ); +extern void mlt_transitition_close( mlt_transition this ); + +#endif diff --git a/src/framework/mlt_types.h b/src/framework/mlt_types.h new file mode 100644 index 00000000..aa974632 --- /dev/null +++ b/src/framework/mlt_types.h @@ -0,0 +1,43 @@ +/* + * mlt_types.h -- provides forward definitions of all public types + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _MLT_TYPES_H_ +#define _MLT_TYPES_H_ + +#include + +typedef double mlt_timecode; +typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; +typedef struct mlt_properties_s *mlt_properties; +typedef struct mlt_service_s *mlt_service; +typedef struct mlt_producer_s *mlt_producer; +typedef struct mlt_manager_s *mlt_manager; +typedef struct mlt_playlist_s *mlt_playlist; +typedef struct mlt_multitrack_s *mlt_multitrack; +typedef struct mlt_filter_s *mlt_filter; +typedef struct mlt_transition_s *mlt_transition; +typedef struct mlt_consumer_s *mlt_consumer; +typedef struct mlt_tractor_s *mlt_tractor; + +typedef void ( *mlt_destructor )( void * ); +typedef char *( *mlt_serialiser )( void *, int length ); + + +#endif diff --git a/src/miracle/configure b/src/miracle/configure new file mode 100755 index 00000000..1a248525 --- /dev/null +++ b/src/miracle/configure @@ -0,0 +1 @@ +#!/bin/sh diff --git a/src/miracle/miracle.c b/src/miracle/miracle.c new file mode 100644 index 00000000..47a645ab --- /dev/null +++ b/src/miracle/miracle.c @@ -0,0 +1,110 @@ +/* + * dv1394d.c -- A DV over IEEE 1394 TCP Server + * + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Authors: + * Dan Dennedy + * Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#include "dvserver.h" +#include "log.h" + +/** Our dv server. +*/ + +static dv_server server = NULL; + +/** atexit shutdown handler for the server. +*/ + +static void main_cleanup( ) +{ + dv_server_shutdown( 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 }; + + server = dv_server_init( argv[ 0 ] ); + + for ( index = 1; index < argc; index ++ ) + { + if ( !strcmp( argv[ index ], "-port" ) ) + dv_server_set_port( server, atoi( argv[ ++ index ] ) ); + else if ( !strcmp( argv[ index ], "-proxy" ) ) + dv_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(); + dv1394d_log_init( log_syslog, LOG_INFO ); + } + else + { + dv1394d_log_init( log_stderr, LOG_INFO ); + } + + atexit( main_cleanup ); + + /* Execute the server */ + error = dv_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 index 00000000..8a492d1a --- /dev/null +++ b/src/miracle/miracle_commands.c @@ -0,0 +1,453 @@ +/* + * global_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvunit.h" +#include "global_commands.h" +#include "raw1394util.h" +#include +#include "log.h" + +static dv_unit g_units[MAX_UNITS]; + + +/** Return the dv_unit given a numeric index. +*/ + +dv_unit dv1394d_get_unit( int n ) +{ + if (n < MAX_UNITS) + return g_units[n]; + else + return NULL; +} + +/** Destroy the dv_unit given its numeric index. +*/ + +void dv1394d_delete_unit( int n ) +{ + if (n < MAX_UNITS) + { + dv_unit unit = dv1394d_get_unit(n); + if (unit != NULL) + { + dv_unit_close( unit ); + g_units[ n ] = NULL; + dv1394d_log( LOG_NOTICE, "Deleted unit U%d.", n ); + } + } +} + +/** Destroy all allocated units on the server. +*/ + +void dv1394d_delete_all_units( void ) +{ + int i; + for (i = 0; i < MAX_UNITS; i++) + if ( dv1394d_get_unit(i) != NULL ) + { + dv_unit_close( dv1394d_get_unit(i) ); + dv1394d_log( LOG_NOTICE, "Deleted unit U%d.", i ); + } +} + +/** Add a DV virtual vtr to the server. +*/ +response_codes dv1394d_add_unit( command_argument cmd_arg ) +{ + int i; + int channel = -1; + char *guid_str = (char*) cmd_arg->argument; + octlet_t guid; + uint32_t guid_hi; + uint32_t guid_lo; + + sscanf( guid_str, "%08x%08x", &guid_hi, &guid_lo ); + guid = (octlet_t)guid_hi << 32 | (octlet_t) guid_lo; + + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 3 ) + channel = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 2 ) ); + + /* make sure unit does not already exit */ + for (i = 0; i < MAX_UNITS; i++) + { + if (g_units[i] != NULL) + if ( dv_unit_get_guid( g_units[i] ) == guid ) + { + dv_response_printf( cmd_arg->response, 1024, "a unit already exists for that node\n\n" ); + return RESPONSE_ERROR; + } + } + + for (i = 0; i < MAX_UNITS; i++) + { + if (g_units[i] == NULL) + { + + g_units[ i ] = dv_unit_init( guid, channel ); + if ( g_units[ i ] == NULL ) + { + dv_response_printf( cmd_arg->response, 1024, "failed to allocate unit\n" ); + return RESPONSE_ERROR; + } + g_units[ i ]->unit = i; + dv_unit_set_notifier( g_units[ i ], dv_parser_get_notifier( cmd_arg->parser ), cmd_arg->root_dir ); + + dv1394d_log( LOG_NOTICE, "added unit %d to send to node %d over channel %d", + i, dv_unit_get_nodeid( g_units[i] ), dv_unit_get_channel( g_units[i] ) ); + dv_response_printf( cmd_arg->response, 10, "U%1d\n\n", i ); + return RESPONSE_SUCCESS_N; + } + } + + dv_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 dv1394d_list_nodes( command_argument cmd_arg ) +{ + response_codes error = RESPONSE_SUCCESS_N; + raw1394handle_t handle; + int i, j; + char line[1024]; + octlet_t guid; + rom1394_directory dir; + + for ( j = 0; j < raw1394_get_num_ports(); j++ ) + { + handle = raw1394_open(j); + for ( i = 0; i < raw1394_get_nodecount(handle); ++i ) + { + rom1394_get_directory( handle, i, &dir); + if ( (rom1394_get_node_type(&dir) == ROM1394_NODE_TYPE_AVC) ) + { + guid = rom1394_get_guid(handle, i); + if (dir.label != NULL) + { + snprintf( line, 1023, "%02d %08x%08x \"%s\"\n", i, + (quadlet_t) (guid>>32), (quadlet_t) (guid & 0xffffffff), dir.label ); + } else { + snprintf( line, 1023, "%02d %08x%08x \"Unlabeled Node %d\"\n", i, + (quadlet_t) (guid>>32), (quadlet_t) (guid & 0xffffffff), i ); + } + dv_response_write( cmd_arg->response, line, strlen(line) ); + rom1394_free_directory( &dir); + } + } + raw1394_close( handle ); + } + dv_response_write( cmd_arg->response, "\n", 1 ); + return error; +} + + +/** List units already added to server. +*/ +response_codes dv1394d_list_units( command_argument cmd_arg ) +{ + response_codes error = RESPONSE_SUCCESS_N; + char line[1024]; + int i; + + for (i = 0; i < MAX_UNITS; i++) + { + if (dv1394d_get_unit(i) != NULL) + { + snprintf( line, 1023, "U%d %02d %08x%08x %d\n", i, dv_unit_get_nodeid(g_units[i]), + (quadlet_t) (dv_unit_get_guid(g_units[i]) >> 32), + (quadlet_t) (dv_unit_get_guid(g_units[i]) & 0xffffffff), + !dv_unit_is_offline( g_units[i] ) ); + dv_response_write( cmd_arg->response, line, strlen(line) ); + } + } + dv_response_write( cmd_arg->response, "\n", 1 ); + + return error; +} + +static int +filter_files( const struct dirent *de ) +{ + if ( de->d_name[ 0 ] != '.' ) + return 1; + else + return 0; +} + +/** List clips in a directory. +*/ +response_codes dv1394d_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 ) ) + dv_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 ) || ( strstr( fullname, ".clip" ) && info.st_mode | S_IXUSR ) ) ) + dv_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 ); + dv_response_write( cmd_arg->response, "\n", 1 ); + } + + return error; +} + +/** Set a server configuration property. +*/ + +response_codes dv1394d_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++; + dv1394d_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) + dv_unit_terminate( g_units[i] ); + } + + /* set the property */ + strncpy( cmd_arg->root_dir, value, 1023 ); + + /* add a trailing slash if needed */ + if ( 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 dv1394d_get_global_property( command_argument cmd_arg ) +{ + char *key = (char*) cmd_arg->argument; + + if ( strncasecmp( key, "root", 1024) == 0 ) + { + dv_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; +} + +/** IEEE 1394 Bus Reset handler + + This is included here for now due to all the unit management involved. +*/ + +static int reset_handler( raw1394handle_t h, unsigned int generation ) +{ + int i, j, count, retry = 3; + int port = (int) raw1394_get_userdata( h ); + + raw1394_update_generation( h, generation ); + dv1394d_log( LOG_NOTICE, "bus reset on port %d", port ); + + while ( retry-- > 0 ) + { + raw1394handle_t handle = raw1394_open( port ); + count = raw1394_get_nodecount( handle ); + + if ( count > 0 ) + { + dv1394d_log( LOG_DEBUG, "bus reset, checking units" ); + + /* suspend all units on this port */ + for ( j = MAX_UNITS; j > 0; j-- ) + { + if ( g_units[ j-1 ] != NULL && dv_unit_get_port( g_units[ j-1 ] ) == port ) + dv_unit_suspend( g_units[ j-1 ] ); + } + dv1394d_log( LOG_DEBUG, "All units are now stopped" ); + + /* restore units with known guid, take others offline */ + for ( j = 0; j < MAX_UNITS; j++ ) + { + if ( g_units[j] != NULL && + ( dv_unit_get_port( g_units[ j ] ) == port || dv_unit_get_port( g_units[ j ] ) == -1 ) ) + { + int found = 0; + for ( i = 0; i < count; i++ ) + { + octlet_t guid; + dv1394d_log( LOG_DEBUG, "attempting to get guid for node %d", i ); + guid = rom1394_get_guid( handle, i ); + if ( guid == g_units[ j ]->guid ) + { + dv1394d_log( LOG_NOTICE, "unit with GUID %08x%08x found", + (quadlet_t) (g_units[j]->guid>>32), (quadlet_t) (g_units[j]->guid & 0xffffffff)); + if ( dv_unit_is_offline( g_units[ j ] ) ) + dv_unit_online( g_units[ j ] ); + else + dv_unit_restore( g_units[ j ] ); + found = 1; + break; + } + } + if ( found == 0 ) + dv_unit_offline( g_units[ j ] ); + } + } + dv1394d_log( LOG_DEBUG, "completed bus reset handler"); + raw1394_close( handle ); + return 0; + } + raw1394_close( handle ); + } + dv1394d_log( LOG_CRIT, "raw1394 reported zero nodes on the bus!" ); + return 0; +} + + +/** One pthread per IEEE 1394 port +*/ + +static pthread_t raw1394service_thread[4]; + +/** One raw1394 handle for each pthread/port +*/ + +static raw1394handle_t raw1394service_handle[4]; + +/** The service thread that polls raw1394 for new events. +*/ + +static void* raw1394_service( void *arg ) +{ + raw1394handle_t handle = (raw1394handle_t) arg; + struct pollfd raw1394_poll; + raw1394_poll.fd = raw1394_get_fd( handle ); + raw1394_poll.events = POLLIN; + raw1394_poll.revents = 0; + while ( 1 ) + { + if ( poll( &raw1394_poll, 1, 200) > 0 ) + { + if ( (raw1394_poll.revents & POLLIN) + || (raw1394_poll.revents & POLLPRI) ) + raw1394_loop_iterate( handle ); + } + pthread_testcancel(); + } + +} + + +/** Start the raw1394 service threads for handling bus reset. + + One thread is launched per port on the system. +*/ + +void raw1394_start_service_threads( void ) +{ + int port; + for ( port = 0; port < raw1394_get_num_ports(); port++ ) + { + raw1394service_handle[port] = raw1394_open( port ); + raw1394_set_bus_reset_handler( raw1394service_handle[port], reset_handler ); + pthread_create( &(raw1394service_thread[port]), NULL, raw1394_service, raw1394service_handle[port] ); + } + for ( ; port < 4; port++ ) + raw1394service_handle[port] = NULL; +} + +/** Shutdown all the raw1394 service threads. +*/ + +void raw1394_stop_service_threads( void ) +{ + int i; + for ( i = 0; i < 4; i++ ) + { + if ( raw1394service_handle[i] != NULL ) + { + pthread_cancel( raw1394service_thread[i] ); + pthread_join( raw1394service_thread[i], NULL ); + raw1394_close( raw1394service_handle[i] ); + } + } +} + + diff --git a/src/miracle/miracle_commands.h b/src/miracle/miracle_commands.h new file mode 100644 index 00000000..6c60d7da --- /dev/null +++ b/src/miracle/miracle_commands.h @@ -0,0 +1,52 @@ +/* + * global_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _GLOBAL_COMMANDS_H_ +#define _GLOBAL_COMMANDS_H_ + +#include +#include "dvunit.h" +#include "dvconnection.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +dv_unit dv1394d_get_unit( int ); +void dv1394d_delete_unit( int ); +void dv1394d_delete_all_units( void ); +int dv1394d_unit_status( int n, dv1394_status status, int root_offset ); +void raw1394_start_service_threads( void ); +void raw1394_stop_service_threads( void ); + +extern response_codes dv1394d_add_unit( command_argument ); +extern response_codes dv1394d_list_nodes( command_argument ); +extern response_codes dv1394d_list_units( command_argument ); +extern response_codes dv1394d_list_clips( command_argument ); +extern response_codes dv1394d_set_global_property( command_argument ); +extern response_codes dv1394d_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 index 00000000..6c65e351 --- /dev/null +++ b/src/miracle/miracle_connection.c @@ -0,0 +1,250 @@ +/* + * dvconnection.c -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#include "global_commands.h" +#include "dvconnection.h" +#include "dvsocket.h" +#include "dvserver.h" +#include "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, dv_response ); +static int connection_read( int, char *, int ); +static void connection_close( int ); + +static int connection_initiate( int fd ) +{ + int error = 0; + dv_response response = dv_response_init( ); + dv_response_set_error( response, 100, "VTR Ready" ); + error = connection_send( fd, response ); + dv_response_close( response ); + return error; +} + +static int connection_send( int fd, dv_response response ) +{ + int error = 0; + int index = 0; + int code = dv_response_get_error_code( response ); + + if ( code != -1 ) + { + int items = dv_response_count( response ); + + if ( items == 0 ) + dv_response_set_error( response, 500, "Unknown error" ); + + if ( code == 200 && items > 2 ) + dv_response_set_error( response, 201, "OK" ); + else if ( code == 200 && items > 1 ) + dv_response_set_error( response, 202, "OK" ); + + code = dv_response_get_error_code( response ); + items = dv_response_count( response ); + + for ( index = 0; !error && index < items; index ++ ) + { + char *line = dv_response_get_line( response, index ); + int length = strlen( line ); + if ( length == 0 && index != dv_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( dv_response_get_line( response, items - 1 ), "" ) ) + write( fd, "\r\n", 2 ); + } + else + { + char *message = "500 Empty Response\r\n\r\n"; + write( fd, message, strlen( 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 || strncmp( command, "BYE", 3 ) == 0 ) + nchars = 0; + return nchars; +} + +int connection_status( int fd, dv1394_notifier notifier ) +{ + int error = 0; + int index = 0; + dv1394_status_t status; + char text[ 10240 ]; + dv_socket socket = dv_socket_init_fd( fd ); + + for ( index = 0; !error && index < MAX_UNITS; index ++ ) + { + dv1394_notifier_get( notifier, &status, index ); + dv1394_status_serialise( &status, text, sizeof( text ) ); + error = dv_socket_write_data( socket, text, strlen( text ) ) != strlen( text ); + } + + while ( !error ) + { + if ( dv1394_notifier_wait( notifier, &status ) == 0 ) + { + dv1394_status_serialise( &status, text, sizeof( text ) ); + error = dv_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; + } + } + + dv_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; + char address[ 512 ]; + char command[ 1024 ]; + int fd = connection->fd; + dv_parser parser = connection->parser; + dv_response response = NULL; + + /* We definitely want to ignore broken pipes. */ + signal( SIGPIPE, SIG_IGN ); + + /* 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 ); + + dv1394d_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 ) ) + { + if ( strncmp( command, "STATUS", 6 ) ) + { + response = dv_parser_execute( parser, command ); + dv1394d_log( LOG_INFO, "%s \"%s\" %d", address, command, dv_response_get_error_code( response ) ); + error = connection_send( fd, response ); + dv_response_close( response ); + } + else + { + error = connection_status( fd, dv_parser_get_notifier( parser ) ); + } + } + } + + /* Free the resources associated with this connection. */ + connection_close( fd ); + + dv1394d_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 index 00000000..1c5d00b0 --- /dev/null +++ b/src/miracle/miracle_connection.h @@ -0,0 +1,91 @@ +/* + * dvconnection.h -- DV Connection Handler + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_CONNECTION_H_ +#define _DV_CONNECTION_H_ + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Connection structure +*/ + +typedef struct +{ + int fd; + struct sockaddr_in sin; + dv_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 +{ + dv_parser parser; + dv_response response; + dv_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 index 00000000..fce4ab22 --- /dev/null +++ b/src/miracle/miracle_local.c @@ -0,0 +1,460 @@ +/* + * dvlocal.c -- Local dv1394d Parser + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include + +/* Library header files */ +#include + +/* Application header files */ +#include +#include +#include "dvlocal.h" +#include "dvconnection.h" +#include "global_commands.h" +#include "unit_commands.h" +#include "log.h" +#include "raw1394util.h" + +/** Private dv_local structure. +*/ + +typedef struct +{ + dv_parser parser; + char root_dir[1024]; +} +*dv_local, dv_local_t; + +/** Forward declarations. +*/ + +static dv_response dv_local_connect( dv_local ); +static dv_response dv_local_execute( dv_local, char * ); +static void dv_local_close( dv_local ); +response_codes print_help( command_argument arg ); +response_codes dv1394d_run( command_argument arg ); +response_codes dv1394d_shutdown( command_argument arg ); + +/** DV Parser constructor. +*/ + +dv_parser dv_parser_init_local( ) +{ + dv_parser parser = malloc( sizeof( dv_parser_t ) ); + dv_local local = malloc( sizeof( dv_local_t ) ); + + if ( parser != NULL ) + { + memset( parser, 0, sizeof( dv_parser_t ) ); + + parser->connect = (parser_connect)dv_local_connect; + parser->execute = (parser_execute)dv_local_execute; + parser->close = (parser_close)dv_local_close; + parser->real = local; + + if ( local != NULL ) + { + memset( local, 0, sizeof( dv_local_t ) ); + local->parser = parser; + local->root_dir[0] = '/'; + } + } + return parser; +} + +/** response status code/message pair +*/ + +typedef struct +{ + int code; + 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 +} +arguments_types; + +/** A command definition. +*/ + +typedef struct +{ +/* The command string corresponding to this operation (e.g. "play") */ + 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 */ + 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", print_help, 0, ATYPE_NONE, "Display this information!"}, + {"NLS", dv1394d_list_nodes, 0, ATYPE_NONE, "List the AV/C nodes on the 1394 bus."}, + {"UADD", dv1394d_add_unit, 0, ATYPE_STRING, "Create a new DV unit (virtual VTR) to transmit to receiver specified in GUID argument."}, + {"ULS", dv1394d_list_units, 0, ATYPE_NONE, "Lists the units that have already been added to the server."}, + {"CLS", dv1394d_list_clips, 0, ATYPE_STRING, "Lists the clips at directory name argument."}, + {"SET", dv1394d_set_global_property, 0, ATYPE_STRING, "Set a server configuration property."}, + {"GET", dv1394d_get_global_property, 0, ATYPE_STRING, "Get a server configuration property."}, + {"RUN", dv1394d_run, 0, ATYPE_STRING, "Run a batch file." }, + {"LIST", dv1394d_list, 1, ATYPE_NONE, "List the playlist associated to a unit."}, + {"LOAD", dv1394d_load, 1, ATYPE_STRING, "Load clip specified in absolute filename argument."}, + {"INSERT", dv1394d_insert, 1, ATYPE_STRING, "Insert a clip at the given clip index."}, + {"REMOVE", dv1394d_remove, 1, ATYPE_NONE, "Remove a clip at the given clip index."}, + {"CLEAN", dv1394d_clean, 1, ATYPE_NONE, "Clean a unit by removing all but the currently playing clip."}, + {"MOVE", dv1394d_move, 1, ATYPE_INT, "Move a clip to another clip index."}, + {"APND", dv1394d_append, 1, ATYPE_STRING, "Append a clip specified in absolute filename argument."}, + {"PLAY", dv1394d_play, 1, ATYPE_NONE, "Play a loaded clip at speed -2000 to 2000 where 1000 = normal forward speed."}, + {"STOP", dv1394d_stop, 1, ATYPE_NONE, "Stop a loaded and playing clip."}, + {"PAUSE", dv1394d_pause, 1, ATYPE_NONE, "Pause a playing clip."}, + {"REW", dv1394d_rewind, 1, ATYPE_NONE, "Rewind a unit. If stopped, seek to beginning of clip. If playing, play fast backwards."}, + {"FF", dv1394d_ff, 1, ATYPE_NONE, "Fast forward a unit. If stopped, seek to beginning of clip. If playing, play fast forwards."}, + {"STEP", dv1394d_step, 1, ATYPE_INT, "Step argument number of frames forward or backward."}, + {"GOTO", dv1394d_goto, 1, ATYPE_INT, "Jump to frame number supplied as argument."}, + {"SIN", dv1394d_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", dv1394d_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", dv1394d_get_unit_status, 1, ATYPE_NONE, "Report information about the unit."}, + {"USET", dv1394d_set_unit_property, 1, ATYPE_STRING, "Set a unit configuration property."}, + {"UGET", dv1394d_get_unit_property, 1, ATYPE_STRING, "Get a unit configuration property."}, + {"XFER", dv1394d_transfer, 1, ATYPE_STRING, "Transfer the unit's clip to another unit specified as argument."}, + {"SHUTDOWN", dv1394d_shutdown, 0, ATYPE_NONE, "Shutdown the server."}, + {NULL, NULL, 0, ATYPE_NONE, NULL} +}; + +/** Usage message +*/ + +static char helpstr [] = + "dv1394d -- A DV over IEEE 1394 TCP Server\n" + " Copyright (C) 2002-2003 Ushodaya Enterprises Limited\n" + " Authors:\n" + " Dan Dennedy \n" + " Charles Yates \n" + "Available commands:\n"; + +/** Lookup the response message for a status code. +*/ + +inline 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 dv1394d command set +*/ + +response_codes print_help( command_argument cmd_arg ) +{ + int i = 0; + + dv_response_printf( cmd_arg->response, 10240, "%s", helpstr ); + + for ( i = 0; vocabulary[ i ].command != NULL; i ++ ) + dv_response_printf( cmd_arg->response, 1024, + "%-10.10s%s\n", + vocabulary[ i ].command, + vocabulary[ i ].help ); + + dv_response_printf( cmd_arg->response, 2, "\n" ); + + return RESPONSE_SUCCESS_N; +} + +/** Execute a batch file. +*/ + +response_codes dv1394d_run( command_argument cmd_arg ) +{ + dv_response temp = dv_parser_run( cmd_arg->parser, (char *)cmd_arg->argument ); + + if ( temp != NULL ) + { + int index = 0; + + dv_response_set_error( cmd_arg->response, + dv_response_get_error_code( temp ), + dv_response_get_error_string( temp ) ); + + for ( index = 1; index < dv_response_count( temp ); index ++ ) + dv_response_printf( cmd_arg->response, 10240, "%s\n", dv_response_get_line( temp, index ) ); + + dv_response_close( temp ); + } + + return dv_response_get_error_code( cmd_arg->response ); +} + +response_codes dv1394d_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 + dv1394d_log( LOG_DEBUG, "Received %s - shutting down.", strsignal(sig) ); +#else + dv1394d_log( LOG_DEBUG, "Received signal %i - shutting down.", sig ); +#endif + + exit(EXIT_SUCCESS); + } +} + +/** Local 'connect' function. +*/ + +static dv_response dv_local_connect( dv_local local ) +{ + dv_response response = dv_response_init( ); + + self = pthread_self( ); + + dv_response_set_error( response, 100, "VTR Ready" ); + + signal( SIGHUP, signal_handler ); + signal( SIGINT, signal_handler ); + signal( SIGTERM, signal_handler ); + signal( SIGSTOP, signal_handler ); + signal( SIGCHLD, SIG_IGN ); + + raw1394_reconcile_bus(); + /* Start the raw1394 service threads for handling bus resets */ + raw1394_start_service_threads(); + + return response; +} + +/** Set the error and determine the message associated to this command. +*/ + +void dv_command_set_error( command_argument cmd, response_codes code ) +{ + dv_response_set_error( cmd->response, code, get_response_msg( code ) ); +} + +/** Parse the unit argument. +*/ + +int dv_command_parse_unit( command_argument cmd, int argument ) +{ + int unit = -1; + char *string = dv_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 *dv_command_parse_argument( command_argument cmd, int argument, arguments_types type ) +{ + void *ret = NULL; + char *value = dv_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_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 dv_command_get_error( command_argument cmd ) +{ + response_codes ret = dv_response_get_error_code( cmd->response ); + if ( ret == RESPONSE_SUCCESS_N || ret == RESPONSE_SUCCESS_1 ) + ret = RESPONSE_SUCCESS; + return ret; +} + +/** Execute the command. +*/ + +static dv_response dv_local_execute( dv_local local, char *command ) +{ + command_argument_t cmd; + cmd.parser = local->parser; + cmd.response = dv_response_init( ); + cmd.tokeniser = dv_tokeniser_init( ); + cmd.command = command; + cmd.unit = -1; + cmd.argument = NULL; + cmd.root_dir = local->root_dir; + + /* Set the default error */ + dv_command_set_error( &cmd, RESPONSE_UNKNOWN_COMMAND ); + + /* Parse the command */ + if ( dv_tokeniser_parse_new( cmd.tokeniser, command, " " ) > 0 ) + { + int index = 0; + char *value = dv_tokeniser_get_string( cmd.tokeniser, 0 ); + int found = 0; + + /* Strip quotes from all tokens */ + for ( index = 0; index < dv_tokeniser_count( cmd.tokeniser ); index ++ ) + dv_util_strip( dv_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; + + dv_command_set_error( &cmd, RESPONSE_SUCCESS ); + + if ( vocabulary[ index ].is_unit ) + { + cmd.unit = dv_command_parse_unit( &cmd, position ); + if ( cmd.unit == -1 ) + dv_command_set_error( &cmd, RESPONSE_MISSING_ARG ); + position ++; + } + + if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS ) + { + cmd.argument = dv_command_parse_argument( &cmd, position, vocabulary[ index ].type ); + if ( cmd.argument == NULL && vocabulary[ index ].type != ATYPE_NONE ) + dv_command_set_error( &cmd, RESPONSE_MISSING_ARG ); + position ++; + } + + if ( dv_command_get_error( &cmd ) == RESPONSE_SUCCESS ) + { + response_codes error = vocabulary[ index ].operation( &cmd ); + dv_command_set_error( &cmd, error ); + } + + free( cmd.argument ); + } + } + + dv_tokeniser_close( cmd.tokeniser ); + + return cmd.response; +} + +/** Close the parser. +*/ + +static void dv_local_close( dv_local local ) +{ + raw1394_stop_service_threads(); + dv1394d_delete_all_units(); + pthread_kill_other_threads_np(); + dv1394d_log( LOG_DEBUG, "Clean shutdown." ); + free( local ); + dv_clip_factory_close( ); + dv_frame_pool_close( ); +} diff --git a/src/miracle/miracle_local.h b/src/miracle/miracle_local.h new file mode 100644 index 00000000..fbbe4447 --- /dev/null +++ b/src/miracle/miracle_local.h @@ -0,0 +1,41 @@ +/* + * dvlocal.h -- Local dv1394d Parser + * Copyright (C) 2002-2003 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 + */ + +#ifndef _DV_LOCAL_H_ +#define _DV_LOCAL_H_ + +/* Application header files */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Local parser API. +*/ + +extern dv_parser dv_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 index 00000000..cdb5d82f --- /dev/null +++ b/src/miracle/miracle_log.c @@ -0,0 +1,63 @@ +/* + * log.h -- logging facility implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "log.h" + +static int log_output = log_stderr; +static int threshold = LOG_DEBUG; + +void +dv1394d_log_init( enum log_output method, int new_threshold ) +{ + log_output = method; + threshold = new_threshold; + if (method == log_syslog) + openlog( "dv1394d", LOG_CONS, LOG_DAEMON ); + +} + +void +dv1394d_log( int priority, 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 index 00000000..04505e97 --- /dev/null +++ b/src/miracle/miracle_log.h @@ -0,0 +1,43 @@ +/* + * log.h -- logging facility header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum log_output { + log_stderr, + log_syslog +}; + +void dv1394d_log_init( enum log_output method, int threshold ); +void dv1394d_log( int priority, char *format, ... ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/miracle/miracle_server.c b/src/miracle/miracle_server.c new file mode 100644 index 00000000..d4f886b9 --- /dev/null +++ b/src/miracle/miracle_server.c @@ -0,0 +1,275 @@ +/* + * dvserver.c -- DV Server + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "log.h" +#include +#include +#include + +/* Application header files */ +#include "dvserver.h" +#include "dvconnection.h" +#include "dvlocal.h" +#include "log.h" +#include +#include + +/** Initialise a server structure. +*/ + +dv_server dv_server_init( char *id ) +{ + dv_server server = malloc( sizeof( dv_server_t ) ); + if ( server != NULL ) + { + memset( server, 0, sizeof( dv_server_t ) ); + server->id = id; + server->port = DEFAULT_TCP_PORT; + server->socket = -1; + } + return server; +} + +/** Set the port of the server. +*/ + +void dv_server_set_port( dv_server server, int port ) +{ + server->port = port; +} + +void dv_server_set_proxy( dv_server server, char *proxy ) +{ + dv_tokeniser tokeniser = dv_tokeniser_init( ); + server->proxy = 1; + server->remote_port = DEFAULT_TCP_PORT; + dv_tokeniser_parse_new( tokeniser, proxy, ":" ); + strcpy( server->remote_server, dv_tokeniser_get_string( tokeniser, 0 ) ); + if ( dv_tokeniser_count( tokeniser ) == 2 ) + server->remote_port = atoi( dv_tokeniser_get_string( tokeniser, 1 ) ); + dv_tokeniser_close( tokeniser ); +} + +/** Wait for a connection. +*/ + +static int dv_server_wait_for_connect( dv_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 *dv_server_run( void *arg ) +{ + dv_server server = arg; + pthread_t cmd_parse_info; + connection_t *tmp = NULL; + pthread_attr_t thread_attributes; + int socksize; + + socksize = sizeof( struct sockaddr ); + + dv1394d_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 ); + pthread_attr_init( &thread_attributes ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_INHERIT_SCHED ); + /* pthread_attr_setschedpolicy( &thread_attributes, SCHED_RR ); */ + + while ( !server->shutdown ) + { + /* Wait for a new connection. */ + if ( dv_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->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 ); + } + } + + dv1394d_log( LOG_NOTICE, "%s version %s server terminated.", server->id, VERSION ); + + return NULL; +} + +/** Execute the server thread. +*/ + +int dv_server_execute( dv_server server ) +{ + int error = 0; + dv_response response = NULL; + int index = 0; + struct sockaddr_in ServerAddr; + int flag = 1; + + 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" ); + dv1394d_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" ); + dv1394d_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" ); + dv1394d_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 ) + { + dv1394d_log( LOG_NOTICE, "Starting server on %d.", server->port ); + server->parser = dv_parser_init_local( ); + } + else + { + dv1394d_log( LOG_NOTICE, "Starting proxy for %s:%d on %d.", server->remote_server, server->remote_port, server->port ); + server->parser = dv_parser_init_remote( server->remote_server, server->remote_port ); + } + + response = dv_parser_connect( server->parser ); + + if ( response != NULL && dv_response_get_error_code( response ) == 100 ) + { + /* read configuration file */ + if ( response != NULL && !server->proxy ) + { + dv_response_close( response ); + response = dv_parser_run( server->parser, "/etc/dv1394d.conf" ); + + if ( dv_response_count( response ) > 1 ) + { + if ( dv_response_get_error_code( response ) > 299 ) + dv1394d_log( LOG_ERR, "Error evaluating server configuration. Processing stopped." ); + for ( index = 0; index < dv_response_count( response ); index ++ ) + dv1394d_log( LOG_DEBUG, "%4d: %s", index, dv_response_get_line( response, index ) ); + } + } + + if ( response != NULL ) + { + pthread_attr_t attr; + int result; + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setschedpolicy( &attr, SCHED_FIFO ); + pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); + dv_response_close( response ); + result = pthread_create( &server->thread, &attr, dv_server_run, server ); + if ( result ) + { + dv1394d_log( LOG_WARNING, "Failed to schedule realtime (%s)", strerror(errno) ); + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); + result = pthread_create( &server->thread, &attr, dv_server_run, server ); + if ( result ) + { + dv1394d_log( LOG_CRIT, "Failed to launch TCP listener thread" ); + error = -1; + } + } + } + } + else + { + dv1394d_log( LOG_ERR, "Error connecting to parser. Processing stopped." ); + server->shutdown = 1; + error = -1; + } + + return error; +} + +/** Shutdown the server. +*/ + +void dv_server_shutdown( dv_server server ) +{ + if ( server != NULL && !server->shutdown ) + { + server->shutdown = 1; + pthread_join( server->thread, NULL ); + dv_parser_close( server->parser ); + close( server->socket ); + } +} diff --git a/src/miracle/miracle_server.h b/src/miracle/miracle_server.h new file mode 100644 index 00000000..96b22cee --- /dev/null +++ b/src/miracle/miracle_server.h @@ -0,0 +1,70 @@ +/* + * dvserver.h -- DV Server + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_SERVER_H_ +#define _DV_SERVER_H_ + +/* System header files */ +#include + +/* Application header files */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Servers default port +*/ + +#define DEFAULT_TCP_PORT 5250 + +/** Structure for the server +*/ + +typedef struct +{ + char *id; + int port; + int socket; + dv_parser parser; + pthread_t thread; + int shutdown; + int proxy; + char remote_server[ 50 ]; + int remote_port; +} +*dv_server, dv_server_t; + +/** API for the server +*/ + +extern dv_server dv_server_init( char * ); +extern void dv_server_set_port( dv_server, int ); +extern void dv_server_set_proxy( dv_server, char * ); +extern int dv_server_execute( dv_server ); +extern void dv_server_shutdown( dv_server ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/miracle/miracle_unit.c b/src/miracle/miracle_unit.c new file mode 100644 index 00000000..bc8adcfb --- /dev/null +++ b/src/miracle/miracle_unit.c @@ -0,0 +1,1100 @@ +/* + * dvunit.c -- DV Transmission Unit Implementation + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dvunit.h" +#include "dvframe.h" +#include "dvframepool.h" +#include "dvqueue.h" +#include "dvpump.h" +#include "dverror.h" +#include "dvplayer.h" +#include "raw1394util.h" +#include "log.h" +#include "dvlocal.h" + +/* Forward references */ +static void dv_unit_status_communicate( dv_unit ); + +/** dv1394 device file names based upon devfs default names. */ + +static char *devices[4][4] = { + { + "/dev/ieee1394/dv/host0/NTSC/in", + "/dev/ieee1394/dv/host0/NTSC/out", + "/dev/ieee1394/dv/host0/PAL/in", + "/dev/ieee1394/dv/host0/PAL/out", + },{ + "/dev/ieee1394/dv/host1/NTSC/in", + "/dev/ieee1394/dv/host1/NTSC/out", + "/dev/ieee1394/dv/host1/PAL/in", + "/dev/ieee1394/dv/host1/PAL/out" + },{ + "/dev/ieee1394/dv/host2/NTSC/in", + "/dev/ieee1394/dv/host2/NTSC/out", + "/dev/ieee1394/dv/host2/PAL/in", + "/dev/ieee1394/dv/host2/PAL/out" + },{ + "/dev/ieee1394/dv/host3/NTSC/in", + "/dev/ieee1394/dv/host3/NTSC/out", + "/dev/ieee1394/dv/host3/PAL/in", + "/dev/ieee1394/dv/host3/PAL/out" + } +}; + +static int device_count[4] = {0,0,0,0}; + +/** Allocate a new DV transmission unit. + + \param dv1394d_fd The file descriptor of a dv1394 device file to + use for transmission. + \param guid The node GUID of the receiving device. + \param channel The channel to use for transmission. + \return A new dv_unit handle. +*/ + +dv_unit dv_unit_init( octlet_t guid, int channel ) +{ + dv_unit unit = malloc( sizeof( dv_unit_t ) ); + if ( unit != NULL ) + { + int node_id; + + memset( unit, 0, sizeof( dv_unit_t ) ); + unit->guid = guid; + unit->buffer_size = 25; + unit->is_terminated = 1; + unit->channel = channel; + unit->dv1394_fd = -1; + unit->n_frames = DV1394_MAX_FRAMES / 2; + unit->n_fill = 1; + + /* get a raw1394 handle for plug control */ + if ( ( node_id = raw1394_find_node( &(unit->raw1394), guid ) ) != -1 ) + { + if ( dv_unit_online( unit ) == 1 ) + dv1394d_log( LOG_DEBUG, "Added online unit with GUID %08x%08x", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + else + { + dv_unit_close( unit ); + unit = NULL; + } + } + else + { + dv1394d_log( LOG_DEBUG, "Added offline unit with GUID %08x%08x", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + } + } + return unit; +} + +/** Allow stdin to feed the unit (redundant now that senddv has been dropped). +*/ + +void dv_unit_allow_stdin( dv_unit unit, int flag ) +{ + unit->allow_stdin = flag; +} + +/** Override the default buffer/pump size - this must be done prior to the pumps + creation. +*/ + +void dv_unit_set_buffer_size( dv_unit unit, int size ) +{ + if ( size > 0 ) + { + if ( unit->pump == NULL ) + unit->buffer_size = size; + else + unit->buffer_size = dv_pump_resize( unit->pump, size ); + } +} + +int dv_unit_get_buffer_size( dv_unit unit ) +{ + return unit->buffer_size; +} + +void dv_unit_set_n_frames( dv_unit unit, int size ) +{ + if ( size > 0 && size <= DV1394_MAX_FRAMES / 2 ) + unit->n_frames = size; +} + +int dv_unit_get_n_frames( dv_unit unit ) +{ + return unit->n_frames; +} + +void dv_unit_set_n_fill( dv_unit unit, int size ) +{ + unit->n_fill = size; +} + +int dv_unit_get_n_fill( dv_unit unit ) +{ + return unit->n_fill; +} + +/** Set the notifier info +*/ + +void dv_unit_set_notifier( dv_unit this, dv1394_notifier notifier, char *root_dir ) +{ + this->notifier = notifier; + this->root_dir = root_dir; + dv_unit_status_communicate( this ); +} + +/** Communicate the current status to all threads waiting on the notifier. +*/ + +static void dv_unit_status_communicate( dv_unit unit ) +{ + if ( unit != NULL && unit->notifier != NULL && unit->root_dir != NULL ) + { + dv1394_status_t status; + if ( dv_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 ) ) + dv1394_notifier_put( unit->notifier, &status ); + } +} + +/** Load a clip into the unit clearing existing play list. + + \todo error handling + \param unit A dv_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) +*/ + +dv_error_code dv_unit_load( dv_unit unit, const char *clip, long in, long out, int flush ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_replace_file( player, (char*) clip, in, out, flush ); + dv1394d_log( LOG_DEBUG, "loaded clip %s", clip ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_insert( dv_unit unit, const char *clip, int index, long in, long out ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_insert_file( player, (char*) clip, index, in, out ); + dv1394d_log( LOG_DEBUG, "inserted clip %s", clip ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_remove( dv_unit unit, int index ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_remove_clip( player, index ); + dv1394d_log( LOG_DEBUG, "removed clip %d", index ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_clean( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_clean( player ); + dv1394d_log( LOG_DEBUG, "Cleaned playlist" ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +dv_error_code dv_unit_move( dv_unit unit, int src, int dest ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_get_error( player ); + if ( error == dv_pump_ok ) + { + error = dv_player_move_clip( player, src, dest ); + dv1394d_log( LOG_DEBUG, "moved clip %d to %d", src, dest ); + if ( unit->is_terminated ) + dv_unit_status_communicate( unit ); + } + return error; +} + +/** Add a clip to the unit play list. + + \todo error handling + \param unit A dv_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) +*/ + +dv_error_code dv_unit_append( dv_unit unit, const char *clip, long in, long out ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_error_code error = dv_player_add_file( player, (char*) clip, in, out ); + dv_unit_status_communicate( unit ); + return error; +} + +void *output_cleanup( void *arg ) +{ + dv_unit unit = arg; + if ( unit != NULL && unit->mmap != NULL ) + { + unit->is_terminated = 1; + dv_unit_status_communicate( unit ); + munmap( unit->mmap, unit->mmap_length ); + /* this actually stops transmission as opposed to allowing the + last frame to loop in the OHCI DMA context. */ + ioctl( unit->dv1394_fd, DV1394_SHUTDOWN, NULL ); + } + + return NULL; +} + +/** The dv1394 transmission thread. + + \param arg A dv_unit handle. +*/ + +static void *output( void *arg ) +{ + dv_unit unit = arg; + dv_frame frames[ DV1394_MAX_FRAMES ]; + int frames_dropped = 0; /* count of total frames dropped (repeated) */ + struct dv1394_status status; + char errstr[64]; + int n_fill = unit->n_fill; + int n_frames = unit->n_frames; + + /* Determine the number of frames to wait for/fill on each iteration */ + if ( n_fill < 1 ) + n_fill = 1; + else if ( n_fill > unit->n_frames ) + n_fill = n_frames / 2; + + unit->mmap = mmap( NULL,unit->mmap_length,PROT_WRITE,MAP_SHARED,unit->dv1394_fd,0 ); + if ( unit->mmap == MAP_FAILED || unit->mmap == NULL ) + { + perror( "mmap" ); + return NULL; + } + + pthread_cleanup_push( output_cleanup, (void *)arg ); + + while ( dv_pump_get_available_output_count( unit->pump ) || + !( dv_unit_has_terminated( unit ) || dv_pump_has_terminated( unit->pump) ) ) + { + int available = 0; + + if ( ioctl( unit->dv1394_fd, DV1394_WAIT_FRAMES, n_fill ) < 0) + perror( "DV1394_WAIT_FRAMES" ); + + pthread_testcancel(); + + /* update the status for the next iteration and detect dropped frames */ + if ( ioctl( unit->dv1394_fd, DV1394_GET_STATUS, &status ) >= 0) + { + pthread_testcancel(); + + /* + printf( "dv1394 status: active=%02d, #clear=%02d, first clear=%02d\n", + status.active_frame, status.n_clear_frames, status.first_clear_frame); + */ + + /* report dropped frames */ + if( status.dropped_frames > 0 ) + { + frames_dropped += status.dropped_frames; + dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames with %d available.", + status.dropped_frames, dv_pump_get_available_output_count( unit->pump ) ); + } + + available = dv_pump_get_output_block( unit->pump, (void **)frames, n_fill ); + + dv_unit_status_communicate( unit ); + + /* The only time we get 0 frames is when the unit is being stopped. */ + if ( available != 0 ) + { + int size = dv_frame_size( frames[ 0 ] ); + int pos = status.first_clear_frame; + int index = 0; + + for ( index = 0; index < available; index ++ ) + memcpy( unit->mmap + ( ( pos + index ) % n_frames ) * size, dv_frame_data( frames[ index ] ), size ); + + if ( ioctl( unit->dv1394_fd, DV1394_SUBMIT_FRAMES, available ) >= 0) + { + for ( index = 0; index < available - 1; index ++ ) + { + dv_frame_clear_error( frames[ index ] ); + dv_frame_id_clear( dv_frame_get_id( frames[ index ] ) ); + } + dv_pump_return_output_block( unit->pump ); + pthread_testcancel(); + } + else + { + dv1394d_log( LOG_ERR, "failed to write frames to dv1394: %s.", strerror_r( errno, errstr, 63 ) ); + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + pthread_testcancel(); + } + } + } + else + { + dv1394d_log( LOG_ERR, "failed to get dv1394 status: %s.", strerror_r( errno, errstr, 63 ) ); + dv_pump_return_used_output( unit->pump ); + } + } + + if ( frames_dropped > 0 ) + dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames total during this transmission.", frames_dropped ); + + pthread_cleanup_pop( 1 ); + + return NULL; +} + +/** Start playing the clip. + + Start a dv-pump and commence dv1394 transmission. + + \todo error handling + \param unit A dv_unit handle. + \param speed An integer that specifies the playback rate as a + percentage multiplied by 100. +*/ + +void dv_unit_play( dv_unit_t *unit, int speed ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + + if ( unit->is_terminated == 1 && ( dv_player_get_total_frames( player ) > 0 || unit->allow_stdin ) ) + { + int retval; + dv_frame frame = NULL; + struct dv1394_init setup = + { + api_version: DV1394_API_VERSION, + channel: unit->channel, + /* this only sets the *requested* size of the ringbuffer, + in frames */ + n_frames: unit->n_frames, + /* we set the format later */ + cip_n: unit->dv1394_cip_n, + cip_d: unit->dv1394_cip_d, + syt_offset: unit->dv1394_syt_offset + }; + pthread_attr_t attr; + + if ( unit->in == NULL ) + { + if ( !unit->allow_stdin || dv_player_get_total_frames( player ) != 0 ) + unit->in = dv_player_get_dv_input( player ); + else + unit->in = dv_input_init( unit->pump ); + } + else + { + dv_input_join_thread( unit->in ); + pthread_join( unit->out, NULL ); + } + + unit->is_terminated = 0; + dv_pump_restart( unit->pump ); + dv_input_start_thread( unit->in ); + dv_player_set_speed( player, (double) speed/1000.0 ); + + /* first we read a little data to see if this is PAL or NTSC + so we can initialize dv1394 properly */ + frame = dv_pump_get_available_output( unit->pump ); + + /* initialize dv1394 */ + setup.format = dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC; + + retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup ); + if (retval < 0) + { + perror( "DV1394_INIT" ); + return; + } + + unit->mmap_length = unit->n_frames * dv_frame_size( frame ); + + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED ); + pthread_create( &unit->out, &attr, output, unit ); + } + else + { + dv_player_set_speed( player, (double) speed/1000.0 ); + } + dv_unit_status_communicate( unit ); +} + +/** Stop playback. + + Terminates the dv_pump and halts dv1394 transmission. + + \param unit A dv_unit handle. +*/ + +void dv_unit_terminate( dv_unit unit ) +{ + unit->is_terminated = 1; + if ( unit->pump != NULL ) + { + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + } +} + +/** Query the status of unit playback. + + \param unit A dv_unit handle. + \return 1 if the unit is not playing, 0 if playing. +*/ + +int dv_unit_has_terminated( dv_unit unit ) +{ + return unit->is_terminated; +} + +/** Get the dv_player from the dv_unit. + + \param unit A dv_unit handle. + \return A dv_player handle. +*/ + +dv_player dv_unit_get_dv_player( dv_unit unit ) +{ + if ( unit != NULL ) + { + if ( unit->pump == NULL ) + { + unit->pump = dv_pump_init( unit->buffer_size ); + if ( unit->pump != NULL ) + unit->player = dv_player_init( unit->pump ); + } + return unit->player; + } + return NULL; +} + + +/** Transfer the currently loaded clip to another unit +*/ + +int dv_unit_transfer( dv_unit dest_unit, dv_unit src_unit ) +{ + dv_player src_player = dv_unit_get_dv_player( src_unit ); + dv_player dest_player = dv_unit_get_dv_player( dest_unit ); + + if( dest_player != NULL && src_player != NULL ) + dv_player_replace_player( dest_player, src_player ); + + return 0; +} + +/** Get the guid associated to this unit. +*/ + +octlet_t dv_unit_get_guid( dv_unit unit ) +{ + return unit->guid; +} + +/** Get the node id associated to this unit. +*/ + +int dv_unit_get_nodeid( dv_unit unit ) +{ + return (unit->node_id & 0x3f); +} + +/** Get the channel associated to this unit. +*/ + +int dv_unit_get_channel( dv_unit unit ) +{ + return (unit->channel); +} + +/** Turn unit online. +*/ + +int dv_unit_online( dv_unit unit ) +{ + int result = 0; + int port, node_id; + + if ( unit->raw1394 != NULL ) + raw1394_close( unit->raw1394 ); + + node_id = raw1394_find_node( &(unit->raw1394), unit->guid ); + if ( node_id != -1 ) + { + unit->node_id = 0xffc0 | node_id; + port = dv_unit_get_port( unit ); + + unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR ); + if ( unit->dv1394_fd < 0 ) + { + dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] ); + dv_unit_close( unit ); + } + else + { + device_count[ port ] ++; + if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) ) + { + avc1394_vcr_record( unit->raw1394, unit->node_id ); + unit->online = 1; + dv_unit_status_communicate( unit ); + result = 1; + } + } + } + + return result; +} + +/** Turn unit offline. +*/ + +void dv_unit_offline( dv_unit unit ) +{ + if ( unit->online == 1 ) + { + if ( unit->is_terminated == 0 ) + dv_unit_terminate( unit ); + unit->online = 0; + if ( unit->raw1394 != NULL ) + { + avc1394_vcr_stop( unit->raw1394, unit->node_id ); + break_p2p_connection( unit->raw1394, unit->node_id, unit->channel ); + } + if ( unit->dv1394_fd > -1 ) + { + close( unit->dv1394_fd ); + device_count[ dv_unit_get_port( unit ) ] --; + } + dv_unit_status_communicate( unit ); + dv1394d_log( LOG_DEBUG, "Unit with GUID %08x%08x is now offline.", + (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) ); + } +} + +/** Determine if unit is offline. +*/ + +int dv_unit_is_offline( dv_unit unit ) +{ + return (unit->online == 0); +} + +/** Obtain the status for a given unit +*/ + +int dv_unit_get_status( dv_unit unit, dv1394_status status ) +{ + int error = -1; + + memset( status, 0, sizeof( dv1394_status_t ) ); + + if ( unit != NULL ) + { + dv_player player = dv_unit_get_dv_player( unit ); + + error = 0; + + if ( player != NULL ) + { + dv_frame head = dv_pump_get_head( player->pump ); + dv_frame tail = dv_pump_get_tail( player->pump ); + + status->speed = (int)( dv_player_get_speed( player ) * 1000.0 ); + status->fps = dv_player_frames_per_second( player, 0 ); + + if ( head != NULL ) + { + dv_frame_id id = dv_frame_get_id( head ); + if ( id->resource != NULL ) + { + const char *resource = id->resource; + if ( resource != NULL && unit->root_dir != NULL ) + resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' ); + strncpy( status->clip, resource, sizeof( status->clip ) ); + } + else + { + char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir ); + if ( title != NULL ) + strncpy( status->clip, title, sizeof( status->clip ) ); + } + + status->position = id->relative; + status->in = id->in; + status->out = id->out; + status->length = id->length; + status->seek_flag = id->seek_flag; + } + else + { + char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir ); + if ( title != NULL ) + strncpy( status->clip, title, sizeof( status->clip ) ); + } + + if ( tail != NULL ) + { + dv_frame_id id = dv_frame_get_id( tail ); + const char *resource = id->resource; + if ( resource != NULL && unit->root_dir != NULL ) + resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' ); + if ( resource != NULL ) + strncpy( status->tail_clip, resource, sizeof( status->clip ) ); + status->tail_position = id->relative; + status->tail_in = id->in; + status->tail_out = id->out; + status->tail_length = id->length; + } + + status->generation = player->generation; + status->clip_index = dv_unit_get_current_clip( unit ); + } + + if ( dv_unit_is_offline( unit ) ) + status->status = unit_offline; + else if ( !strcmp( status->clip, "" ) ) + status->status = unit_not_loaded; + else if ( dv_unit_has_terminated( unit ) ) + status->status = unit_stopped; + else if ( status->speed == 0 ) + status->status = unit_paused; + else + status->status = unit_playing; + } + else + { + status->status = unit_undefined; + } + + status->unit = unit->unit; + + return error; +} + +/** Change position in the playlist. +*/ + +void dv_unit_change_position( dv_unit unit, int clip, long position ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_set_clip_position( player, clip, position ); + dv_unit_status_communicate( unit ); +} + +/** Change speed. +*/ + +void dv_unit_change_speed( dv_unit unit, int speed ) +{ + if ( dv_unit_has_terminated( unit ) ) + dv_unit_change_position( unit, 0, 0 ); + else + dv_unit_play( unit, speed ); +} + +int dv_unit_get_current_clip( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + unsigned long position = dv_player_get_position( player ); + return dv_player_get_clip_containing( player, position ); +} + +/** Set a clip's in point +*/ + +int dv_unit_set_clip_in( dv_unit unit, int index, long position ) +{ + int error = 0; + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_unit_change_speed( unit, 0 ); + if ( dv_player_set_in_point( player, index, (unsigned long) position ) == position ) + dv_player_set_clip_position( player, index, position ); + else + error = -2; + } + else + { + error = -1; + } + + dv_unit_status_communicate( unit ); + + return error; + +} + +/** Set a clip's out point. +*/ + +int dv_unit_set_clip_out( dv_unit unit, int index, long position ) +{ + int error = 0; + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_unit_change_speed( unit, 0 ); + if ( dv_player_set_out_point( player, index, position ) == position ) + dv_player_set_clip_position( player, index, position ); + else + error = -2; + } + else + { + error = -1; + } + + dv_unit_status_communicate( unit ); + + return error; +} + +/** Step by specified position. +*/ + +void dv_unit_step( dv_unit unit, int offset ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_change_position( player, dv_seek_relative, offset ); +} + +/** Set the unit's clip mode regarding in and out points. +*/ + +void dv_unit_set_mode( dv_unit unit, dv_player_clip_mode mode ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + if ( player != NULL ) + dv_player_set_clip_mode( player, mode ); + dv_unit_status_communicate( unit ); +} + +/** Get the unit's clip mode regarding in and out points. +*/ + +dv_player_clip_mode dv_unit_get_mode( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + return dv_player_get_clip_mode( player ); +} + +/** Set the unit's clip mode regarding eof handling. +*/ + +void dv_unit_set_eof_action( dv_unit unit, dv_player_eof_action action ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + dv_player_set_eof_action( player, action ); + dv_unit_status_communicate( unit ); +} + +/** Get the unit's clip mode regarding eof handling. +*/ + +dv_player_eof_action dv_unit_get_eof_action( dv_unit unit ) +{ + dv_player player = dv_unit_get_dv_player( unit ); + return dv_player_get_eof_action( player ); +} + +/** Release the unit + + \todo error handling + \param unit A dv_unit handle. +*/ + +void dv_unit_close( dv_unit unit ) +{ + if ( unit != NULL ) + { + dv1394d_log( LOG_DEBUG, "closing unit..." ); + dv_unit_offline( unit ); + if ( unit->pump != NULL ) + { + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + dv_pump_return_used_output( unit->pump ); + dv_input_join_thread( unit->in ); + if ( !unit->is_terminated ) + pthread_join( unit->out, NULL ); + dv_pump_close( unit->pump ); + unit->pump = NULL; + } + raw1394_close( unit->raw1394 ); + free( unit ); + dv1394d_log( LOG_DEBUG, "... unit closed." ); + } +} + +/** Get the raw1394 port associated to this unit. +*/ + +int dv_unit_get_port( dv_unit unit ) +{ + if ( unit->raw1394 != NULL ) + return (int) raw1394_get_userdata( unit->raw1394 ); + else + return -1; +} + +/** Set the dv1394 file descriptor for the unit. +*/ + +void dv_unit_set_dv1394_fd( dv_unit unit, int fd ) +{ + unit->dv1394_fd = fd; +} + +/** Get the dv1394 syt_offset (timestamp latency) property. +*/ + +unsigned int dv_unit_get_syt_offset( dv_unit unit ) +{ + return unit->dv1394_syt_offset; +} + +/** Get the dv1394 cip_n (timing numerator) property. +*/ + +unsigned int dv_unit_get_cip_n( dv_unit unit ) +{ + return unit->dv1394_cip_n; +} + +/** Get the dv1394 cip_d (timing denominator) property. +*/ + +unsigned int dv_unit_get_cip_d( dv_unit unit ) +{ + return unit->dv1394_cip_d; +} + +/** Set the dv1394 syt_offset (timestamp latency) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_syt_offset( dv_unit unit, unsigned int syt_offset ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_syt_offset = syt_offset; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Set the dv1394 cip_n (timing numerator) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_cip_n( dv_unit unit, unsigned int cip_n ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_cip_n = cip_n; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Set the dv1394 cip_d (timing denominator) property. + + Stops and restarts the unit if playing. +*/ + +void dv_unit_set_cip_d( dv_unit unit, unsigned int cip_d ) +{ + int restart = !unit->is_terminated; + int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 ); + + dv_unit_terminate( unit ); + unit->dv1394_cip_d = cip_d; + if ( restart ) + dv_unit_play( unit, speed ); +} + +/** Terminate, but only the output thread and close dv1394. +*/ + +void dv_unit_suspend( dv_unit unit ) +{ + if ( unit->is_terminated == 0 ) + { + unit->is_terminated = 1; + unit->is_suspended = 1; + dv_pump_terminate( unit->pump ); + dv_pump_flush( unit->pump ); + pthread_cancel( unit->out ); + } + if ( unit->dv1394_fd > -1 ) + { + close( unit->dv1394_fd ); + device_count[ dv_unit_get_port( unit ) ] --; + } + unit->dv1394_fd = -1; + dv_unit_status_communicate( unit ); +} + + +/** Restore unit on the bus, re-open dv1394, start playback if pump is running. +*/ + +void dv_unit_restore( dv_unit unit ) +{ + int result = 0; + int port, node_id; + + if ( unit->raw1394 != NULL ) + raw1394_close( unit->raw1394 ); + + node_id = raw1394_find_node( &(unit->raw1394), unit->guid ); + if ( node_id != -1 ) + { + unit->node_id = 0xffc0 | node_id; + port = dv_unit_get_port( unit ); + + unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR ); + if ( unit->dv1394_fd < 0 ) + { + dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] ); + dv_unit_close( unit ); + } + else + { + device_count[ port ] ++; + break_p2p_connection( unit->raw1394, unit->node_id, unit->channel ); + if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) ) + { + avc1394_vcr_record( unit->raw1394, unit->node_id ); + unit->online = 1; + result = 1; + } + } + } + if ( unit->is_suspended == 1 ) + { + int retval; + dv_frame frame = dv_pump_get_available_output( unit->pump ); + struct dv1394_init setup = + { + api_version: DV1394_API_VERSION, + channel: unit->channel, + /* this only sets the *requested* size of the ringbuffer, + in frames */ + n_frames: unit->n_frames, + format: dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC, + cip_n: unit->dv1394_cip_n, + cip_d: unit->dv1394_cip_d, + syt_offset: unit->dv1394_syt_offset + }; + pthread_attr_t attr; + + dv_input_join_thread( unit->in ); + unit->is_terminated = 0; + unit->is_suspended = 0; + dv_pump_restart( unit->pump ); + dv_input_start_thread( unit->in ); + + /* initialize dv1394 */ + retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup ); + if ( retval < 0 ) + return; + + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED ); + /* pthread_attr_setschedpolicy( &attr, SCHED_RR ); */ + pthread_create( &unit->out, &attr, output, unit ); + } + dv_unit_status_communicate( unit ); +} diff --git a/src/miracle/miracle_unit.h b/src/miracle/miracle_unit.h new file mode 100644 index 00000000..54338182 --- /dev/null +++ b/src/miracle/miracle_unit.h @@ -0,0 +1,122 @@ +/* + * dvunit.h -- DV Transmission Unit Header + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _DV_UNIT_H_ +#define _DV_UNIT_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct +{ + int unit; + dv_pump pump; + dv_player player; + dv_input in; + int dv1394_fd; + int is_terminated; + int is_suspended; + pthread_t out; + int channel; + nodeid_t node_id; + octlet_t guid; + raw1394handle_t raw1394; + int allow_stdin; + int buffer_size; + int online; + dv1394_notifier notifier; + char *root_dir; + unsigned int dv1394_syt_offset; + unsigned int dv1394_cip_n; + unsigned int dv1394_cip_d; + unsigned int n_frames; + unsigned int n_fill; + uint8_t *mmap; + int mmap_pos; + int mmap_length; +} dv_unit_t, *dv_unit; + +extern dv_unit dv_unit_init( octlet_t guid, int channel ); +extern void dv_unit_allow_stdin( dv_unit unit, int flag ); +extern void dv_unit_set_buffer_size( dv_unit unit, int size ); +extern int dv_unit_get_buffer_size( dv_unit unit ); +extern void dv_unit_set_n_frames( dv_unit unit, int size ); +extern int dv_unit_get_n_frames( dv_unit unit ); +extern void dv_unit_set_n_fill( dv_unit unit, int size ); +extern int dv_unit_get_n_fill( dv_unit unit ); +extern dv_error_code dv_unit_load( dv_unit unit, const char *clip, long in, long out, int flush ); +extern dv_error_code dv_unit_insert( dv_unit unit, const char *clip, int index, long in, long out ); +extern dv_error_code dv_unit_append( dv_unit unit, const char *clip, long in, long out ); +extern dv_error_code dv_unit_remove( dv_unit unit, int index ); +extern dv_error_code dv_unit_clean( dv_unit unit ); +extern dv_error_code dv_unit_move( dv_unit unit, int src, int dest ); +extern int dv_unit_transfer( dv_unit dest_unit, dv_unit src_unit ); +extern void dv_unit_play( dv_unit_t *unit, int speed ); +extern void dv_unit_terminate( dv_unit ); +extern int dv_unit_has_terminated( dv_unit ); +extern octlet_t dv_unit_get_guid( dv_unit unit ); +extern int dv_unit_get_nodeid( dv_unit unit ); +extern int dv_unit_get_channel( dv_unit unit ); +extern int dv_unit_online( dv_unit unit ); +extern void dv_unit_offline( dv_unit unit ); +extern int dv_unit_is_offline( dv_unit unit ); +extern void dv_unit_set_notifier( dv_unit, dv1394_notifier, char * ); +extern int dv_unit_get_status( dv_unit, dv1394_status ); +extern void dv_unit_change_position( dv_unit, int, long position ); +extern void dv_unit_change_speed( dv_unit unit, int speed ); +extern int dv_unit_set_clip_in( dv_unit unit, int index, long position ); +extern int dv_unit_set_clip_out( dv_unit unit, int index, long position ); +extern void dv_unit_set_mode( dv_unit unit, dv_player_clip_mode mode ); +extern dv_player_clip_mode dv_unit_get_mode( dv_unit unit ); +extern void dv_unit_set_eof_action( dv_unit unit, dv_player_eof_action mode ); +extern dv_player_eof_action dv_unit_get_eof_action( dv_unit unit ); +extern void dv_unit_step( dv_unit unit, int offset ); +extern void dv_unit_close( dv_unit unit ); +extern int dv_unit_get_port( dv_unit unit ); +extern void dv_unit_set_dv1394_fd( dv_unit unit, int fd ); +extern unsigned int dv_unit_get_syt_offset( dv_unit unit ); +extern unsigned int dv_unit_get_cip_n( dv_unit unit ); +extern unsigned int dv_unit_get_cip_d( dv_unit unit ); +extern void dv_unit_set_syt_offset( dv_unit unit, unsigned int ); +extern void dv_unit_set_cip_n( dv_unit unit, unsigned int ); +extern void dv_unit_set_cip_d( dv_unit unit, unsigned int ); +extern void dv_unit_suspend( dv_unit ); +extern void dv_unit_restore( dv_unit ); +extern dv_player dv_unit_get_dv_player( dv_unit ); +extern int dv_unit_get_current_clip( dv_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 index 00000000..b12f2c72 --- /dev/null +++ b/src/miracle/miracle_unit_commands.c @@ -0,0 +1,612 @@ +/* + * unit_commands.c + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "dvunit.h" +#include "global_commands.h" +#include "dverror.h" +#include "dvframepool.h" +#include "log.h" + +int dv1394d_load( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + int flush = 1; + + if ( filename[0] == '!' ) + { + flush = 0; + filename ++; + } + + if ( 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; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 5 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 3 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + } + if ( dv_unit_load( unit, fullname, in, out, flush ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_list( command_argument cmd_arg ) +{ + int i = 0; + dv_unit unit = dv1394d_get_unit( cmd_arg->unit ); + dv_player player = dv_unit_get_dv_player( unit ); + + if ( player != NULL ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", player->generation ); + + for ( i = 0; i < dv_player_get_clip_count( player ); i ++ ) + { + dv_clip clip = dv_player_get_clip( player, i ); + + dv_response_printf( cmd_arg->response, 10240, + "%d \"%s\" %d %d %d %d %.2f\n", + i, + dv_clip_get_resource( clip, cmd_arg->root_dir ), + dv_clip_get_in( clip ), + ( !dv_clip_is_seekable( clip ) && clip->out_frame == -1 ? -1 : dv_clip_get_out( clip ) ), + dv_clip_get_max_frames( clip ), + ( !dv_clip_is_seekable( clip ) && clip->out_frame == -1 ? -1 : dv_player_get_length_of_clip( player, i ) ), + dv_clip_frames_per_second( clip ) ); + } + + dv_response_printf( cmd_arg->response, 2, "\n" ); + + return RESPONSE_SUCCESS; + } + return RESPONSE_INVALID_UNIT; +} + +static int parse_clip( command_argument cmd_arg, int arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = dv_unit_get_current_clip( unit ); + + if ( dv_tokeniser_count( cmd_arg->tokeniser ) > arg ) + { + dv_player player = dv_unit_get_dv_player( unit ); + char *token = dv_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 ); + if ( clip < 0 ) + clip = 0; + if ( clip >= player->size ) + clip = player->size - 1; + } + + return clip; +} + +int dv1394d_insert( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + + if ( 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 ( dv_tokeniser_count( cmd_arg->tokeniser ) == 6 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 5 ) ); + } + + switch( dv_unit_insert( unit, fullname, index, in, out ) ) + { + case dv_pump_ok: + return RESPONSE_SUCCESS; + case dv_pump_too_many_files_open: + return RESPONSE_TOO_MANY_FILES; + default: + return RESPONSE_BAD_FILE; + } + } + return RESPONSE_SUCCESS; +} + +int dv1394d_remove( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + int index = parse_clip( cmd_arg, 2 ); + + if ( dv_unit_remove( unit, index ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_clean( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + if ( dv_unit_clean( unit ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + return RESPONSE_SUCCESS; +} + +int dv1394d_move( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if ( unit != NULL ) + { + if ( dv_tokeniser_count( cmd_arg->tokeniser ) > 2 ) + { + int src = parse_clip( cmd_arg, 2 ); + int dest = parse_clip( cmd_arg, 3 ); + + if ( dv_unit_move( unit, src, dest ) != dv_pump_ok ) + return RESPONSE_BAD_FILE; + } + else + { + return RESPONSE_MISSING_ARG; + } + } + else + { + return RESPONSE_INVALID_UNIT; + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_append( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + char *filename = (char*) cmd_arg->argument; + char fullname[1024]; + + if ( 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; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 5 ) + { + in = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 3 ) ); + out = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 4 ) ); + } + switch ( dv_unit_append( unit, fullname, in, out ) ) + { + case dv_pump_ok: + return RESPONSE_SUCCESS; + case dv_pump_too_many_files_open: + return RESPONSE_TOO_MANY_FILES; + default: + return RESPONSE_BAD_FILE; + } + } + return RESPONSE_SUCCESS; +} + +int dv1394d_play( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int speed = 1000; + if ( dv_tokeniser_count( cmd_arg->tokeniser ) == 3 ) + speed = atoi( dv_tokeniser_get_string( cmd_arg->tokeniser, 2 ) ); + dv_unit_play( unit, speed ); + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_stop( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_terminate( unit ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_pause( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_play( unit, 0 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_rewind( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_speed( unit, -2000 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_step( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + dv_unit_play( unit, 0 ); + dv_unit_step( unit, *(int*) cmd_arg->argument ); + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_goto( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_position( unit, clip, *(int*) cmd_arg->argument ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_ff( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + dv_unit_change_speed( unit, 2000 ); + + return RESPONSE_SUCCESS; +} + +int dv1394d_set_in_point( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int position = *(int *) cmd_arg->argument; + + switch( dv_unit_set_clip_in( unit, clip, position ) ) + { + case -1: + return RESPONSE_BAD_FILE; + case -2: + return RESPONSE_OUT_OF_RANGE; + } + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_set_out_point( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + int clip = parse_clip( cmd_arg, 3 ); + + if (unit == NULL || dv_unit_is_offline(unit)) + return RESPONSE_INVALID_UNIT; + else + { + int position = *(int *) cmd_arg->argument; + + switch( dv_unit_set_clip_out( unit, clip, position ) ) + { + case -1: + return RESPONSE_BAD_FILE; + case -2: + return RESPONSE_OUT_OF_RANGE; + } + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_get_unit_status( command_argument cmd_arg ) +{ + dv1394_status_t status; + int error = dv_unit_get_status( dv1394d_get_unit( cmd_arg->unit ), &status ); + + if ( error == -1 ) + return RESPONSE_INVALID_UNIT; + else + { + char text[ 10240 ]; + + dv_response_printf( cmd_arg->response, + sizeof( text ), + dv1394_status_serialise( &status, text, sizeof( text ) ) ); + + return RESPONSE_SUCCESS_1; + } + + return 0; +} + + +int dv1394d_set_unit_property( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + char *key = (char*) cmd_arg->argument; + char *value = NULL; + + value = strchr( key, '=' ); + if (value == NULL) + return RESPONSE_OUT_OF_RANGE; + value[0] = 0; + value++; + dv1394d_log( LOG_DEBUG, "USET %s = %s", key, value ); + if ( strncasecmp( key, "eof", 1024) == 0 ) + { + if ( strncasecmp( value, "pause", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_pause ); + else if ( strncasecmp( value, "loop", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_loop ); + else if ( strncasecmp( value, "stop", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_terminate ); + else if ( strncasecmp( value, "clean", 1024) == 0) + dv_unit_set_eof_action( unit, dv_player_clean_loop ); + else + return RESPONSE_OUT_OF_RANGE; + } + else if ( strncasecmp( key, "points", 1024) == 0 ) + { + if ( strncasecmp( value, "use", 1024) == 0) + dv_unit_set_mode( unit, dv_clip_mode_restricted ); + else if ( strncasecmp( value, "ignore", 1024) == 0) + dv_unit_set_mode( unit, dv_clip_mode_unrestricted ); + else + return RESPONSE_OUT_OF_RANGE; + } + else if ( strncasecmp( key, "syt_offset", 1024) == 0 ) + { + dv_unit_set_syt_offset( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "cip_n", 1024) == 0 ) + { + dv_unit_set_cip_n( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "cip_d", 1024) == 0 ) + { + dv_unit_set_cip_d( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "size", 1024) == 0 ) + { + dv_unit_set_buffer_size( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "n_frames", 1024) == 0 ) + { + dv_unit_set_n_frames( unit, atoi( value ) ); + } + else if ( strncasecmp( key, "n_fill", 1024) == 0 ) + { + dv_unit_set_n_fill( unit, atoi( value ) ); + } + else + return RESPONSE_OUT_OF_RANGE; + } + + return RESPONSE_SUCCESS; +} + +int dv1394d_get_unit_property( command_argument cmd_arg ) +{ + dv_unit unit = dv1394d_get_unit(cmd_arg->unit); + + if (unit == NULL) + return RESPONSE_INVALID_UNIT; + else + { + char *key = (char*) cmd_arg->argument; + + if ( strncasecmp( key, "eof", 1024) == 0 ) + { + switch ( dv_unit_get_eof_action( unit ) ) + { + case dv_player_pause: + dv_response_write( cmd_arg->response, "pause", strlen("pause") ); + break; + case dv_player_loop: + dv_response_write( cmd_arg->response, "loop", strlen("loop") ); + break; + case dv_player_terminate: + dv_response_write( cmd_arg->response, "stop", strlen("stop") ); + break; + case dv_player_clean_loop: + dv_response_write( cmd_arg->response, "clean", strlen("clean") ); + break; + } + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "points", 1024) == 0 ) + { + if ( dv_unit_get_mode( unit ) == dv_clip_mode_restricted ) + dv_response_write( cmd_arg->response, "use", strlen("use") ); + else + dv_response_write( cmd_arg->response, "ignore", strlen("ignore") ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "syt_offset", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_syt_offset( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "cip_n", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_cip_n( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "cip_d", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_cip_d( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "size", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_buffer_size( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "n_frames", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_n_frames( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "n_fill", 1024) == 0 ) + { + dv_response_printf( cmd_arg->response, 1024, "%d\n", + dv_unit_get_n_fill( unit ) ); + return RESPONSE_SUCCESS_1; + } + else if ( strncasecmp( key, "all", 1024 ) == 0 ) + { + switch ( dv_unit_get_eof_action( unit ) ) + { + case dv_player_pause: + dv_response_write( cmd_arg->response, "eof=pause\n", strlen("pause") ); + break; + case dv_player_loop: + dv_response_write( cmd_arg->response, "eof=loop\n", strlen("loop") ); + break; + case dv_player_terminate: + dv_response_write( cmd_arg->response, "eof=stop\n", strlen("stop") ); + break; + case dv_player_clean_loop: + dv_response_write( cmd_arg->response, "eof=clean\n", strlen("clean") ); + break; + } + if ( dv_unit_get_mode( unit ) == dv_clip_mode_restricted ) + dv_response_write( cmd_arg->response, "points=use\n", strlen("use") ); + else + dv_response_write( cmd_arg->response, "points=ignore\n", strlen("ignore") ); + dv_response_printf( cmd_arg->response, 1024, "syt_offset=%d\n", dv_unit_get_syt_offset( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "cip_n=%d\n", dv_unit_get_cip_n( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "cip_d=%d\n", dv_unit_get_cip_d( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "size=%d\n", dv_unit_get_buffer_size( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "n_frames=%d\n", dv_unit_get_n_frames( unit ) ); + dv_response_printf( cmd_arg->response, 1024, "n_fill=%d\n", dv_unit_get_n_fill( unit ) ); + } + } + + return RESPONSE_SUCCESS; +} + + +int dv1394d_transfer( command_argument cmd_arg ) +{ + dv_unit src_unit = dv1394d_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 ) + { + dv_unit dest_unit = dv1394d_get_unit( dest_unit_id ); + if ( dest_unit != NULL && !dv_unit_is_offline(dest_unit) && dest_unit != src_unit ) + { + dv_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 index 00000000..a7163bdc --- /dev/null +++ b/src/miracle/miracle_unit_commands.h @@ -0,0 +1,57 @@ +/* + * unit_commands.h + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _UNIT_COMMANDS_H_ +#define _UNIT_COMMANDS_H_ + +#include "dvconnection.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern response_codes dv1394d_list( command_argument ); +extern response_codes dv1394d_load( command_argument ); +extern response_codes dv1394d_insert( command_argument ); +extern response_codes dv1394d_remove( command_argument ); +extern response_codes dv1394d_clean( command_argument ); +extern response_codes dv1394d_move( command_argument ); +extern response_codes dv1394d_append( command_argument ); +extern response_codes dv1394d_play( command_argument ); +extern response_codes dv1394d_stop( command_argument ); +extern response_codes dv1394d_pause( command_argument ); +extern response_codes dv1394d_rewind( command_argument ); +extern response_codes dv1394d_step( command_argument ); +extern response_codes dv1394d_goto( command_argument ); +extern response_codes dv1394d_ff( command_argument ); +extern response_codes dv1394d_set_in_point( command_argument ); +extern response_codes dv1394d_set_out_point( command_argument ); +extern response_codes dv1394d_get_unit_status( command_argument ); +extern response_codes dv1394d_set_unit_property( command_argument ); +extern response_codes dv1394d_get_unit_property( command_argument ); +extern response_codes dv1394d_transfer( command_argument ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/Makefile b/src/modules/Makefile new file mode 100644 index 00000000..0a4c7172 --- /dev/null +++ b/src/modules/Makefile @@ -0,0 +1,15 @@ +SUBDIRS = core gtk2 dv sdl # bluefish mcmpeg + +all clean depend install: + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + [ ! -f disable-$$subdir ] && $(MAKE) -C $$subdir $@; \ + done + +dist-clean: + rm -f consumers.dat filters.dat producers.dat transitions.dat; \ + list='$(SUBDIRS)'; \ + for subdir in $$list; do \ + [ ! -f disable-$$subdir ] && $(MAKE) -C $$subdir $@; \ + done + diff --git a/src/modules/configure b/src/modules/configure new file mode 100755 index 00000000..365fe433 --- /dev/null +++ b/src/modules/configure @@ -0,0 +1,26 @@ +#!/bin/bash + +# Clean up disables if not in help mode +[ "$help" != "1" ] && rm -f disable-* producers.dat filters.dat transitions.dat consumers.dat + +# 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 [ -x $i/configure -a \( "$help" = "1" -o ! -f disable-$i \) ] + then + echo "Configuring $i:" + pushd $i > /dev/null + ./configure $@ + [ $? != 0 ] && exit 1 + popd > /dev/null + fi +done + diff --git a/src/modules/dv/Makefile b/src/modules/dv/Makefile new file mode 100644 index 00000000..22d999ee --- /dev/null +++ b/src/modules/dv/Makefile @@ -0,0 +1,29 @@ + +TARGET = factory.o \ + libmltdv.so + +OBJS = producer_libdv.o + +CFLAGS=-I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS=-ldv -lpthread + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/dv/configure b/src/modules/dv/configure new file mode 100755 index 00000000..876897c2 --- /dev/null +++ b/src/modules/dv/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +libdv libmltdv.so +EOF + +fi + diff --git a/src/modules/dv/factory.c b/src/modules/dv/factory.c new file mode 100644 index 00000000..e27510a2 --- /dev/null +++ b/src/modules/dv/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "producer_libdv.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "libdv" ) ) + return producer_libdv_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/src/modules/dv/producer_libdv.c b/src/modules/dv/producer_libdv.c new file mode 100644 index 00000000..5d2c768e --- /dev/null +++ b/src/modules/dv/producer_libdv.c @@ -0,0 +1,370 @@ +/* + * producer_libdv.c -- simple libdv test case + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "producer_libdv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct producer_libdv_s *producer_libdv; + +struct producer_libdv_s +{ + struct mlt_producer_s parent; + int fd; + dv_decoder_t *dv_decoder; + int is_pal; + uint64_t file_size; + int frame_size; + long frames_in_file; +}; + +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_producer producer_libdv_init( char *filename ) +{ + producer_libdv this = calloc( sizeof( struct producer_libdv_s ), 1 ); + + if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) + { + mlt_producer producer = &this->parent; + + // Register transport implementation with the producer + producer->close = producer_close; + + // Register our get_frame implementation with the producer + producer->get_frame = producer_get_frame; + + // Create the dv_decoder + this->dv_decoder = dv_decoder_new( FALSE, FALSE, FALSE ); + this->dv_decoder->quality = DV_QUALITY_BEST; + this->dv_decoder->audio->arg_audio_emphasis = 2; + dv_set_audio_correction( this->dv_decoder, DV_AUDIO_CORRECT_AVERAGE ); + + // Open the file if specified + if ( filename != NULL ) + { + this->fd = open( filename, O_RDONLY ); + producer_collect_info( this ); + } + + // 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 ) +{ + int valid = 0; + uint8_t *dv_data = malloc( 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 ); + + // 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 + double fps = this->is_pal ? 25 : 30000 / 1001; + mlt_timecode length = ( mlt_timecode )( this->frames_in_file ) / fps; + mlt_properties_set_double( properties, "fps", fps ); + mlt_properties_set_timecode( properties, "playtime", length ); + mlt_properties_set_timecode( properties, "length", length ); + mlt_properties_set_timecode( properties, "in", 0.0 ); + mlt_properties_set_timecode( properties, "out", length ); + + // Set the speed to normal + mlt_properties_set_double( properties, "speed", 1 ); + } + + free( dv_data ); + } + + return valid; +} + +#if 0 +static int mc_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 ); + + // Get the dv data + uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL ); + + // Determine bytes in frame + int bytes_in_frame = dv_data[ 3 ] & 0x80 ? frame_size_625_50 : frame_size_525_60; + + // Assign width and height from properties + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + if ( *format == mlt_image_yuv422 ) + { + // Allocate image + *buffer = malloc( *width * *height * 2 ); + + // Decompress + DecompressBuffer_DV( dv_data, bytes_in_frame, *buffer, *width * 2, *width, *height, 0, FOURCC_YUYV, 0, NULL ); + + // Set the image on the properties + mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, free, NULL ); + } + else + { + // Allocate image + *buffer = malloc( *width * *height * 3 ); + + // Decompress + DecompressBuffer_DV( dv_data, bytes_in_frame, *buffer, *width * 3, *width, *height, 0, FOURCC_R24C, 0, NULL ); + + // Set the image on the properties + mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, free, NULL ); + } + + return 0; +} +#endif + +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 the dv decoder + dv_decoder_t *decoder = mlt_properties_get_data( properties, "dv_decoder", NULL ); + + // Get the dv data + uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL ); + + // Assign width and height from properties + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + // Parse the header + dv_parse_header( decoder, dv_data ); + + // Extract an image of the format requested + if ( *format == mlt_image_yuv422 ) + { + // Allocate an image + uint8_t *image = malloc( *width * *height * 2 ); + + // Pass to properties for clean up + mlt_properties_set_data( properties, "image", image, *width * *height * 2, free, 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; + } + else if ( *format == mlt_image_rgb24 ) + { + // Allocate an image + uint8_t *image = malloc( *width * *height * 3 ); + + // Pass to properties for clean up + mlt_properties_set_data( properties, "image", image, *width * *height * 3, free, 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; + } + + 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 the dv decoder + dv_decoder_t *decoder = mlt_properties_get_data( properties, "dv_decoder", NULL ); + + // 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 ); + + // 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 ] = malloc( DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) ); + + // Create a workspace for the result + *buffer = malloc( *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 ), free, 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++ ) + free( audio_channels[ i ] ); + + return 0; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + producer_libdv this = producer->child; + uint8_t *data = malloc( frame_size_625_50 ); + + // Obtain the current frame number + uint64_t position = mlt_producer_frame( producer ); + + // Convert timecode to a file position (ensuring that we're on a frame boundary) + uint64_t offset = position * this->frame_size; + + // Create an empty frame + *frame = mlt_frame_init( ); + + // Seek and fetch + if ( this->fd != 0 && + lseek( this->fd, offset, SEEK_SET ) == offset && + read_frame( this->fd, data, &this->is_pal ) ) + { + // Get the frames properties + mlt_properties properties = mlt_frame_properties( *frame ); + + // Pass the dv decoder + mlt_properties_set_data( properties, "dv_decoder", this->dv_decoder, 0, NULL, NULL ); + + // Pass the dv data + mlt_properties_set_data( properties, "dv_data", data, frame_size_625_50, free, NULL ); + + // Update other info on the frame + mlt_properties_set_int( properties, "width", 720 ); + mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 ); + + // Hmm - register audio callback + ( *frame )->get_audio = producer_get_audio; + + // Push the get_image method on to the stack + mlt_frame_push_get_image( *frame, producer_get_image ); + } + else + { + free( data ); + } + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *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; + + // Free the dv deconder + dv_decoder_free( this->dv_decoder ); + + // Close the file + if ( this->fd != 0 ) + close( this->fd ); + + // Close the parent + parent->close = NULL; + mlt_producer_close( parent ); + + // Free the memory + free( this ); +} + diff --git a/src/modules/dv/producer_libdv.h b/src/modules/dv/producer_libdv.h new file mode 100644 index 00000000..c493eaef --- /dev/null +++ b/src/modules/dv/producer_libdv.h @@ -0,0 +1,28 @@ +/* + * producer_libdv.h -- a DV decoder based on libdv + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _PRODUCER_LIBDV_H_ +#define _PRODUCER_LIBDV_H_ + +#include + +extern mlt_producer producer_libdv_init( char *filename ); + +#endif diff --git a/src/modules/gtk2/Makefile b/src/modules/gtk2/Makefile new file mode 100644 index 00000000..0136b31f --- /dev/null +++ b/src/modules/gtk2/Makefile @@ -0,0 +1,29 @@ + +TARGET = libmltgtk2.so + +OBJS = factory.o \ + producer_pixbuf.o + +CFLAGS=`pkg-config gdk-pixbuf-2.0 --cflags` -I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS=`pkg-config gdk-pixbuf-2.0 --libs` + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/gtk2/configure b/src/modules/gtk2/configure new file mode 100755 index 00000000..21123363 --- /dev/null +++ b/src/modules/gtk2/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +pixbuf libmltgtk2.so +EOF + +fi + diff --git a/src/modules/gtk2/factory.c b/src/modules/gtk2/factory.c new file mode 100644 index 00000000..fc3a7fa9 --- /dev/null +++ b/src/modules/gtk2/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "producer_pixbuf.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "pixbuf" ) ) + return producer_pixbuf_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/src/modules/gtk2/producer_pixbuf.c b/src/modules/gtk2/producer_pixbuf.c new file mode 100644 index 00000000..8a74a468 --- /dev/null +++ b/src/modules/gtk2/producer_pixbuf.c @@ -0,0 +1,231 @@ +/* + * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "producer_pixbuf.h" +#include +#include +#include +#include +#include + +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( const 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; + + producer->get_frame = producer_get_frame; + producer->close = producer_close; + + this->filename = strdup( filename ); + this->counter = -1; + g_type_init(); + + 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 ) +{ + // Obtain properties of frame + mlt_properties properties = mlt_frame_properties( this ); + + // May need to know the size of the image to clone it + int size = 0; + + // Get the image + uint8_t *image = mlt_properties_get_data( properties, "image", &size ); + + // Get width and height + *width = mlt_properties_get_int( properties, "width" ); + *height = mlt_properties_get_int( properties, "height" ); + + // Clone if necessary + if ( writable ) + { + // Clone our image + uint8_t *copy = malloc( size ); + memcpy( copy, image, size ); + + // We're going to pass the copy on + image = copy; + + // Now update properties so we free the copy after + mlt_properties_set_data( properties, "image", copy, size, free, NULL ); + } + + // Pass on the image + *buffer = image; + + return 0; +} + +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_pixbuf this = producer->child; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + + // Generate a frame + *frame = mlt_frame_init( ); + + // Obtain properties of frame + mlt_properties properties = mlt_frame_properties( *frame ); + + // optimization for subsequent iterations on single picture + if ( this->image != NULL ) + { + // Set width/height + mlt_properties_set_int( properties, "width", this->width ); + mlt_properties_set_int( properties, "height", this->height ); + + // if picture sequence pass the image and alpha data without destructor + mlt_properties_set_data( properties, "image", this->image, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", this->alpha, 0, NULL, NULL ); + + // Set alpha mask call back + ( *frame )->get_alpha_mask = producer_get_alpha_mask; + + // Stack the get image callback + mlt_frame_push_get_image( *frame, producer_get_image ); + + } + else if ( strchr( this->filename, '%' ) != NULL ) + { + // handle picture sequences + char filename[1024]; + filename[1023] = 0; + int current = this->counter; + do + { + ++this->counter; + snprintf( filename, 1023, this->filename, this->counter ); + pixbuf = gdk_pixbuf_new_from_file( filename, &error ); + // allow discontinuity in frame numbers up to 99 + error = NULL; + } while ( pixbuf == NULL && ( this->counter - current ) < 100 ); + } + else + { + pixbuf = gdk_pixbuf_new_from_file( this->filename, &error ); + } + + // If we have a pixbuf + if ( pixbuf ) + { + // Store width and height + this->width = gdk_pixbuf_get_width( pixbuf ); + this->height = gdk_pixbuf_get_height( pixbuf ); + + // Allocate/define image and alpha + uint8_t *image = malloc( this->width * this->height * 2 ); + uint8_t *alpha = NULL; + + // Extract YUV422 and alpha + if ( gdk_pixbuf_get_has_alpha( pixbuf ) ) + { + // Allocate the alpha mask + alpha = malloc( 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 ), + image, 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 ), + image ); + } + + // Finished with pixbuf now + g_object_unref( pixbuf ); + + // Set width/height of frame + mlt_properties_set_int( properties, "width", this->width ); + mlt_properties_set_int( properties, "height", this->height ); + + // Pass alpha and image on properties with or without destructor + if ( this->counter >= 0 ) + { + // if picture sequence pass the image and alpha data with destructor + mlt_properties_set_data( properties, "image", image, 0, free, NULL ); + mlt_properties_set_data( properties, "alpha", alpha, 0, free, NULL ); + } + else + { + // if single picture, reference the image and alpha in the producer + this->image = image; + this->alpha = alpha; + + // pass the image and alpha data without destructor + mlt_properties_set_data( properties, "image", image, 0, NULL, NULL ); + mlt_properties_set_data( properties, "alpha", alpha, 0, NULL, NULL ); + } + + // 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 ); + } + + // Update timecode on the frame we're creating + mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer parent ) +{ + producer_pixbuf this = parent->child; + if ( this->filename ) + free( this->filename ); + if ( this->image ) + free( this->image ); + if ( this->alpha ) + free( this->alpha ); + parent->close = NULL; + mlt_producer_close( parent ); + free( this ); +} + diff --git a/src/modules/gtk2/producer_pixbuf.h b/src/modules/gtk2/producer_pixbuf.h new file mode 100644 index 00000000..c7f76ef3 --- /dev/null +++ b/src/modules/gtk2/producer_pixbuf.h @@ -0,0 +1,41 @@ +/* + * producer_pixbuf.h -- raster image loader based upon gdk-pixbuf + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _PRODUCER_PIXBUF_H_ +#define _PRODUCER_PIXBUF_H_ + +#include + +typedef struct producer_pixbuf_s *producer_pixbuf; + +struct producer_pixbuf_s +{ + struct mlt_producer_s parent; + char *filename; + int counter; + int width; + int height; + uint8_t *image; + uint8_t *alpha; +}; + +extern mlt_producer producer_pixbuf_init( const char *filename ); + +#endif diff --git a/src/modules/sdl/Makefile b/src/modules/sdl/Makefile new file mode 100644 index 00000000..51074412 --- /dev/null +++ b/src/modules/sdl/Makefile @@ -0,0 +1,29 @@ + +TARGET = libmltsdl.so + +OBJS = factory.o \ + consumer_sdl.o + +CFLAGS=-I../../ `sdl-config --cflags` -Wall -g -D_FILE_OFFSET_BITS=64 -pthread + +LDFLAGS= `sdl-config --libs` + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/sdl/configure b/src/modules/sdl/configure new file mode 100755 index 00000000..1c40faca --- /dev/null +++ b/src/modules/sdl/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../consumers.dat +sdl libmltsdl.so +EOF + +fi + diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c new file mode 100644 index 00000000..59196d2e --- /dev/null +++ b/src/modules/sdl/consumer_sdl.c @@ -0,0 +1,366 @@ +/* + * consumer_sdl.c -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "consumer_sdl.h" +#include +#include +#include +#include +#include +#include +#include + +/** This classes definition. +*/ + +typedef struct consumer_sdl_s *consumer_sdl; + +struct consumer_sdl_s +{ + struct mlt_consumer_s parent; + mlt_properties properties; + int format; + int video; + pthread_t thread; + int running; + uint8_t audio_buffer[ 4096 * 6 ]; + int audio_avail; + pthread_mutex_t audio_mutex; + pthread_cond_t audio_cond; + int window_width; + int window_height; +}; + +/** Forward references to static functions. +*/ + +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); +static void consumer_get_dimensions( int *width, int *height ); + +/** 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( void *dummy ) +{ + // 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 ) == 0 ) + { + // 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 + this->running = 1; + pthread_mutex_init( &this->audio_mutex, NULL ); + pthread_cond_init( &this->audio_cond, NULL); + + // TODO: process actual param + + // Create the the thread + pthread_create( &this->thread, NULL, consumer_thread, this ); + + // Return the consumer produced + return parent; + } + + // malloc or consumer init failed + free( this ); + + // Indicate failure + return NULL; +} + +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 ); +} + +void sdl_fill_audio( void *udata, Uint8 *stream, int len ) +{ + consumer_sdl this = udata; + + // Get the volume + float volume = mlt_properties_get_double( this->properties, "volume" ); + + pthread_mutex_lock( &this->audio_mutex ); + + // Experimental - 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 + SDL_MixAudio( stream, this->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); + + // 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 ); + + // Copy what we have into the stream + memcpy( stream, this->audio_buffer, this->audio_avail ); + + // Mix the audio + SDL_MixAudio( stream, stream, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); + + // No audio left + this->audio_avail = 0; + } + pthread_cond_broadcast( &this->audio_cond ); + pthread_mutex_unlock( &this->audio_mutex ); +} + +/** 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; + + // Get the service assoicated to the consumer + mlt_service service = mlt_consumer_service( consumer ); + + // Define a frame pointer + mlt_frame frame; + + // internal intialization + int sdl_flags = SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL | SDL_RESIZABLE; + SDL_Surface *sdl_screen = NULL; + SDL_Overlay *sdl_overlay = NULL; + uint8_t *buffer = NULL; + int init_audio = 1; + int bytes; + + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); + return NULL; + } + + // Loop until told not to + while( this->running ) + { + // Get a frame from the service (should never return anything other than 0) + if ( mlt_service_get_frame( service, &frame, 0 ) == 0 ) + { + mlt_image_format vfmt = mlt_image_yuv422; + int width, height; + uint8_t *image; + + mlt_audio_format afmt = mlt_audio_pcm; + int channels; + int samples; + int frequency; + int16_t *pcm; + + mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples ); + if ( init_audio == 1 ) + { + SDL_AudioSpec request; + + // specify audio format + request.freq = frequency; + request.format = AUDIO_S16; + request.channels = channels; + request.samples = 1024; + request.callback = sdl_fill_audio; + request.userdata = (void *)this; + if ( SDL_OpenAudio( &request, NULL ) < 0 ) + { + fprintf( stderr, "SDL failed to open audio: %s\n", SDL_GetError() ); + break; + } + SDL_PauseAudio( 0 ); + init_audio = 0; + } + bytes = ( samples * channels * 2 ); + pthread_mutex_lock( &this->audio_mutex ); + while ( bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) ) + pthread_cond_wait( &this->audio_cond, &this->audio_mutex ); + memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes ); + this->audio_avail += bytes; + pthread_cond_broadcast( &this->audio_cond ); + pthread_mutex_unlock( &this->audio_mutex ); + + // Get the image, width and height + mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); + if ( width != this->window_width || height != this->window_height ) + { + SDL_Rect rect; + this->window_width = rect.w = width; + this->window_height = rect.h = height; + + // open SDL window with video overlay, if possible + if ( sdl_screen == NULL ) + sdl_screen = SDL_SetVideoMode( width, height, 0, sdl_flags ); + if ( sdl_screen != NULL ) + { + rect.x = rect.y = 0; + + + // XXX: this is a little hack until we have some sort of aspect + // ratio property on images. + if ( rect.h <= 486 ) + { + rect.w = 640; + rect.x = ( 720 - 640 ) / 2; + } + else + { + rect.h = 540; + rect.y = ( 576 - 540 ) / 2; + } + + SDL_SetClipRect( sdl_screen, &rect ); + + // Force an overlay recreation + if ( sdl_overlay != NULL ) + SDL_FreeYUVOverlay( sdl_overlay ); + sdl_lock_display(); + sdl_overlay = SDL_CreateYUVOverlay( width, height, SDL_YUY2_OVERLAY, sdl_screen ); + sdl_unlock_display(); + } + } + + if ( sdl_screen != NULL && sdl_overlay != NULL ) + { + buffer = sdl_overlay->pixels[ 0 ]; + if ( sdl_lock_display() ) + { + if ( SDL_LockYUVOverlay( sdl_overlay ) >= 0 ) + { + memcpy( buffer, image, width * height * 2 ); + SDL_UnlockYUVOverlay( sdl_overlay ); + SDL_DisplayYUVOverlay( sdl_overlay, &sdl_screen->clip_rect ); + } + sdl_unlock_display(); + } + } + else + { + // TODO: allocate buffer? + } + + // Close the frame + mlt_frame_close( frame ); + } + else + { + break; + } + } // while + + // internal cleanup + if ( init_audio == 0 ) + SDL_AudioQuit( ); + if ( sdl_overlay != NULL ) + SDL_FreeYUVOverlay( sdl_overlay ); + SDL_Quit( ); + + return NULL; +} + +static void consumer_get_dimensions( int *width, int *height ) +{ + // SDL windows manager structure + SDL_SysWMinfo wm; + + // Specify the SDL Version + SDL_VERSION( &wm.version ); + + // 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 ); + + // Return width and height + *width = attr.width; + *height = attr.height; + } + } +} + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + // Kill the thread and clean up + this->running = 0; + pthread_join( this->thread, NULL ); + + pthread_mutex_destroy( &this->audio_mutex ); + pthread_cond_destroy( &this->audio_cond ); + + // Now clean up the rest (the close = NULL is a bit nasty but needed for now) + parent->close = NULL; + mlt_consumer_close( parent ); + + // Finally clean up this + free( this ); +} + diff --git a/src/modules/sdl/consumer_sdl.h b/src/modules/sdl/consumer_sdl.h new file mode 100644 index 00000000..6c8114e4 --- /dev/null +++ b/src/modules/sdl/consumer_sdl.h @@ -0,0 +1,28 @@ +/* + * consumer_sdl.h -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _CONSUMER_SDL_H_ +#define _CONSUMER_SDL_H_ + +#include + +extern mlt_consumer consumer_sdl_init( void * ); + +#endif diff --git a/src/modules/sdl/factory.c b/src/modules/sdl/factory.c new file mode 100644 index 00000000..9e5f56ea --- /dev/null +++ b/src/modules/sdl/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "consumer_sdl.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + if ( !strcmp( id, "sdl" ) ) + return consumer_sdl_init( arg ); + return NULL; +} + diff --git a/src/tests/charlie.c b/src/tests/charlie.c new file mode 100644 index 00000000..174cb20c --- /dev/null +++ b/src/tests/charlie.c @@ -0,0 +1,79 @@ +#include "mlt_producer.h" +#include "mlt_consumer.h" +#include "mlt_filter.h" +#include "mlt_tractor.h" +#include "mlt_transition.h" +#include "mlt_multitrack.h" + +#include "producer_libdv.h" +#include "producer_ppm.h" +#include "filter_deinterlace.h" +#include "filter_greyscale.h" +#include "consumer_sdl.h" + +#include + +int main( int argc, char **argv ) +{ + char temp[ 132 ]; + char *file1 = NULL; + char *file2 = NULL; + + // Start the consumer... + mlt_consumer sdl_out = consumer_sdl_init( NULL ); + + fprintf( stderr, "Press return to continue\n" ); + fgets( temp, 132, stdin ); + + if ( argc >= 2 ) + file1 = argv[ 1 ]; + if ( argc >= 3 ) + file2 = argv[ 2 ]; + + // Create the producer(s) + //mlt_producer dv1 = producer_ppm_init( NULL ); + //mlt_producer dv2 = producer_ppm_init( NULL ); + mlt_producer dv1 = producer_libdv_init( file1 ); + mlt_producer dv2 = producer_libdv_init( file2 ); + + mlt_consumer_connect( sdl_out, mlt_producer_service( dv1 ) ); + + fprintf( stderr, "Press return to continue\n" ); + fgets( temp, 132, stdin ); + + // Register producers(s) with a multitrack object + mlt_multitrack multitrack = mlt_multitrack_init( ); + mlt_multitrack_connect( multitrack, dv1, 0 ); + mlt_multitrack_connect( multitrack, dv2, 1 ); + + // Create a filter and associate it to track 0 + mlt_filter filter = filter_deinterlace_init( NULL ); + mlt_filter_connect( filter, mlt_multitrack_service( multitrack ), 0 ); + mlt_filter_set_in_and_out( filter, 0, 5 ); + + // Create another + mlt_filter greyscale = filter_greyscale_init( NULL ); + mlt_filter_connect( greyscale, mlt_filter_service( filter ), 0 ); + mlt_filter_set_in_and_out( greyscale, 0, 10 ); + + // Buy a tractor and connect it to the filter + mlt_tractor tractor = mlt_tractor_init( ); + mlt_tractor_connect( tractor, mlt_filter_service( greyscale ) ); + + // Connect the tractor to the consumer + mlt_consumer_connect( sdl_out, 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( sdl_out ); + mlt_tractor_close( tractor ); + mlt_filter_close( filter ); + mlt_multitrack_close( multitrack ); + mlt_producer_close( dv1 ); + mlt_producer_close( dv2 ); + + return 0; +} diff --git a/src/tests/dan.c b/src/tests/dan.c new file mode 100644 index 00000000..ee76bb01 --- /dev/null +++ b/src/tests/dan.c @@ -0,0 +1,72 @@ +#include "mlt_producer.h" +#include "mlt_consumer.h" +#include "mlt_filter.h" +#include "mlt_tractor.h" +#include "mlt_transition.h" +#include "mlt_multitrack.h" + +#include "producer_libdv.h" +#include "filter_deinterlace.h" +#include "consumer_sdl.h" +#include "producer_ppm.h" +#include "producer_pixbuf.h" +#include "transition_composite.h" + +#include + +int main( int argc, char **argv ) +{ + char temp[ 132 ]; + char *file1 = NULL; + char *file2 = NULL; + + if ( argc >= 2 ) + file1 = argv[ 1 ]; + if ( argc >= 3 ) + file2 = argv[ 2 ]; + + // Start the consumer... + mlt_consumer sdl_out = consumer_sdl_init( NULL ); + + // Create the producer(s) + mlt_producer dv1 = producer_libdv_init( file1 ); + //mlt_producer dv1 = producer_pixbuf_init( file1 ); + //mlt_producer dv2 = producer_libdv_init( file2 ); + mlt_producer dv2 = producer_pixbuf_init( file2 ); + + // Register producers(s) with a multitrack object + mlt_multitrack multitrack = mlt_multitrack_init( ); + mlt_multitrack_connect( multitrack, dv1, 0 ); + mlt_multitrack_connect( multitrack, dv2, 1 ); + + // Create a filter and associate it to track 0 + mlt_filter filter = filter_deinterlace_init( NULL ); + mlt_filter_connect( filter, mlt_multitrack_service( multitrack ), 0 ); + mlt_filter_set_in_and_out( filter, 0, 1000 ); + + // Define a transition + mlt_transition transition = transition_composite_init( NULL ); + mlt_transition_connect( transition, mlt_filter_service( filter ), 0, 1 ); + mlt_transition_set_in_and_out( transition, 0, 1000 ); + + // 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( sdl_out, 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( sdl_out ); + mlt_tractor_close( tractor ); + mlt_filter_close( filter ); + mlt_multitrack_close( multitrack ); + mlt_producer_close( dv1 ); + mlt_producer_close( dv2 ); + + return 0; +} diff --git a/src/tests/test.png b/src/tests/test.png new file mode 100644 index 0000000000000000000000000000000000000000..b3fca64519876231ae9a060fc194a0ae48244fd8 GIT binary patch literal 1352 zcmV-O1-JT%P)Q2Ue;Z@vP-uW*q~F{#Qkvx3{iucMMBURf(ro( z64WDz%3wsI1WiZ?IwmqfE=>#|LBSY)C;}3QD2(82BPa;o3Yo5hZNR#99qZa2f1I_H ztzGq(@2_*d%lmxq_4@>AL{Ystz*E3%(8@tLfC8W$@PYO#i2cA(x63(@PM;VBR0zBZ zsDLq@BESzc0Gr${XE4` z1|ntJ0SGg&TT#7n-^VOm1LP#zVloOEUoK*PO)iZ~?3fIKZ+`3pz?LmDX}oDv8-aU) zg>j&wdadZGpWHfwAkV6w>%po_8W!8RaAkmwZr!a5X4uFyCr$Q5SsnWjit2q9*fZMd z9E-%}x?(0zFayy1bCC8MVV<6D1t1cIGrt5mcsjs$=YoX7S~4|NZkO}C?5JM08kjs9 zFcj7}?&(9Ruwb_tC@nHlV3Wzp&@Gb$l-iBdRA*89h+Zx?15rA=BZ+GM_Ia94Nx-DU z(X0%KbuZZY^sTXc{HBAN>MR7%+aKlCO;@>aMekmAo1O=n2FE>pM54M?)+~wIN386A z!@<`ZO3BHLyL%FV1ezl@Qc-H=;5*}ad`cE~jWtnOW@f!=XG>i%7PB-03LpgRJkrgK zm8~@Hzs_%eh2nF2kNN2eMq|GY&|??@B#T+_$(pf@D>UNoi*oREFSDj)W6PES@O6cG zzQISAKSH)eVoO3yTQ2rfx8)jxL$RwUH8HWo#NqD)3BJ?ZZddsf3AC#Ki)PvwS7_vV zPlQ>kTG{mZ3g)eCC{RK3XAoU0l3r}8U_Op9YZb^ z?EobOdXH}F3^OpOpR~&#;idQ5Is3;g0LqGtY+F%+HA@-+eW)sv4_+%Ctr4^j0XYY7 zvscfk`zD&Hz9%ETYG6=f>HAk{KCgF|3B^V}tSceIBt}3_pAVuXtq6#5WJmQffO&tm z3NmN96{A5=qhxd9Y#-eLJ>Zcj9P${1up%f)^|VT~g#0ouC4e0HKI z1xwRFEb)1o{1R{k=;`PVvwm+E02wC1j`|YH9k<(7IHFO%^%|`mg83iL;5+;<%SgDg_rhe<@N zjeW(caw9`Q-F)B%2Tu0h0Ud9@ed)`Ro)sFm@%;ckPgB@Yz03nV1IVX- z3Q~}3pt8)2#Vk?#s1;cf6pb= zFFs*IeR9kQEtgscSoCgtywFof2zc1-a<(K6j;NZUx5y5_3^1j_LjBx4${i-c5sh7k z{d{z&n{Z@QSSK*Y?Q)(RK9RIc71cWmv@b#AgWfOAMnO12IuO9Oz-+h6c{ADMXzx-P zSOlyB%F|JXKpPSh.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) libvalerie.a + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/valerie/configure b/src/valerie/configure new file mode 100755 index 00000000..1a248525 --- /dev/null +++ b/src/valerie/configure @@ -0,0 +1 @@ +#!/bin/sh diff --git a/src/valerie/valerie.c b/src/valerie/valerie.c new file mode 100644 index 00000000..2e1f2895 --- /dev/null +++ b/src/valerie/valerie.c @@ -0,0 +1,875 @@ +/* + * valerie.c -- High Level Client API for miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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, 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; +} + +/** 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 - 2 ); + 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, double in, double out ) +{ + return valerie_execute( this, 10240, "LOAD U%d \"%s\" %e %e", 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, double in, double out ) +{ + return valerie_execute( this, 10240, "LOAD U%d \"!%s\" %e %e", unit, file, in, out ); +} + +/** Append a file on the specified unit. +*/ + +valerie_error_code valerie_unit_append( valerie this, int unit, char *file, double in, double out ) +{ + return valerie_execute( this, 10240, "APND U%d \"%s\" %e %e", unit, file, in, out ); +} + +/** 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 ); +} + +/** 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, double in, double out ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "INSERT U%d %s %s %e %e", 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, double speed ) +{ + return valerie_execute( this, 10240, "PLAY U%d %e", 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, double step ) +{ + return valerie_execute( this, 1024, "STEP U%d %e", unit, step ); +} + +/** Goto the specified frame on the specified unit. +*/ + +valerie_error_code valerie_unit_goto( valerie this, int unit, double position ) +{ + return valerie_execute( this, 1024, "GOTO U%d %e", 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, double position ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "GOTO U%d %e %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, double in ) +{ + return valerie_execute( this, 1024, "SIN U%d %e", 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, double in ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "SIN U%d %e %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, double out ) +{ + return valerie_execute( this, 1024, "SOUT U%d %e", 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, double in ) +{ + char temp[ 100 ]; + valerie_interpret_clip_offset( temp, offset, clip ); + return valerie_execute( this, 1024, "SOUT U%d %e %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, char *name, 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, 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 = atof( valerie_tokeniser_get_string( tokeniser, 2 ) ); + entry->out = atof( valerie_tokeniser_get_string( tokeniser, 3 ) ); + entry->max = atof( valerie_tokeniser_get_string( tokeniser, 4 ) ); + entry->size = atof( 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. +*/ + +char *valerie_error_description( valerie_error_code error ) +{ + 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 index 00000000..91c09878 --- /dev/null +++ b/src/valerie/valerie.h @@ -0,0 +1,256 @@ +/* + * valerie.h -- High Level Client API for miracle + * Copyright (C) 2002-2003 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _VALERIE_H_ +#define _VALERIE_H_ + +/* System header files */ +#include + +/* 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 *, double, double ); +extern valerie_error_code valerie_unit_load_back( valerie, int, char * ); +extern valerie_error_code valerie_unit_load_back_clipped( valerie, int, char *, double, double ); +extern valerie_error_code valerie_unit_append( valerie, int, char *, double, double ); +extern valerie_error_code valerie_unit_clean( 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 *, double, double ); +extern valerie_error_code valerie_unit_play( valerie, int ); +extern valerie_error_code valerie_unit_play_at_speed( valerie, int, double ); +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, double ); +extern valerie_error_code valerie_unit_goto( valerie, int, double ); +extern valerie_error_code valerie_unit_clip_goto( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_clip_set_in( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_clip_set_out( valerie, int, valerie_clip_offset, int, double ); +extern valerie_error_code valerie_unit_set_in( valerie, int, double ); +extern valerie_error_code valerie_unit_set_out( valerie, int, double ); +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, char *, 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, 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 ]; + double in; + double out; + double max; + double size; + double 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[ 17 ]; + 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 char *valerie_error_description( valerie_error_code ); + +/* Courtesy functions. */ +extern valerie_error_code valerie_execute( valerie, size_t, 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 index 00000000..5e374a85 --- /dev/null +++ b/src/valerie/valerie_notifier.c @@ -0,0 +1,142 @@ +/* + * valerie_notifier.c -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include + +/* Application header files */ +#include "valerie_notifier.h" + +/** Notifier initialisation. +*/ + +valerie_notifier valerie_notifier_init( ) +{ + valerie_notifier this = malloc( sizeof( valerie_notifier_t ) ); + if ( this != NULL ) + { + int index = 0; + memset( this, 0, sizeof( valerie_notifier_t ) ); + pthread_mutex_init( &this->mutex, NULL ); + pthread_cond_init( &this->cond, NULL ); + pthread_mutex_init( &this->cond_mutex, 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 ) ); + + pthread_mutex_lock( &this->cond_mutex ); + gettimeofday( &now, NULL ); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + if ( pthread_cond_timedwait( &this->cond, &this->cond_mutex, &timeout ) != ETIMEDOUT ) + { + pthread_mutex_lock( &this->mutex ); + valerie_status_copy( status, &this->last ); + pthread_mutex_unlock( &this->mutex ); + } + else + { + error = -1; + } + pthread_mutex_unlock( &this->cond_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_mutex_destroy( &this->cond_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 index 00000000..bc123ed1 --- /dev/null +++ b/src/valerie/valerie_notifier.h @@ -0,0 +1,61 @@ +/* + * valerie_notifier.h -- Unit Status Notifier Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_NOTIFIER_H_ +#define _VALERIE_NOTIFIER_H_ + +/* System header files */ +#include + +/* 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_mutex_t cond_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 index 00000000..4ead1c04 --- /dev/null +++ b/src/valerie/valerie_parser.c @@ -0,0 +1,131 @@ +/* + * valerie_parser.c -- Valerie Parser for Miracle + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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 ); +} + +/** Execute a formatted command via the parser. +*/ + +valerie_response valerie_parser_executef( valerie_parser parser, 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 index 00000000..71446d90 --- /dev/null +++ b/src/valerie/valerie_parser.h @@ -0,0 +1,67 @@ +/* + * valerie_parser.h -- Valerie Parser for Miracle Server + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_PARSER_H_ +#define _VALERIE_PARSER_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 void (*parser_close)( void * ); + +/** Structure for the valerie parser. +*/ + +typedef struct +{ + parser_connect connect; + parser_execute execute; + 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_execute( valerie_parser, char * ); +extern valerie_response valerie_parser_executef( valerie_parser, 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 index 00000000..4b5023e5 --- /dev/null +++ b/src/valerie/valerie_remote.c @@ -0,0 +1,260 @@ +/* + * valerie_remote.c -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include +#include +#include + +/* Application header files */ +#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 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 = malloc( sizeof( valerie_parser_t ) ); + valerie_remote remote = malloc( sizeof( valerie_remote_t ) ); + + if ( parser != NULL ) + { + memset( parser, 0, sizeof( valerie_parser_t ) ); + + parser->connect = (parser_connect)valerie_remote_connect; + parser->execute = (parser_execute)valerie_remote_execute; + parser->close = (parser_close)valerie_remote_close; + parser->real = remote; + + if ( remote != NULL ) + { + memset( remote, 0, sizeof( valerie_remote_t ) ); + 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; +} + +/** Disconnect. +*/ + +static void valerie_remote_disconnect( valerie_remote remote ) +{ + if ( remote != NULL && remote->terminated ) + { + 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 index 00000000..291184a2 --- /dev/null +++ b/src/valerie/valerie_remote.h @@ -0,0 +1,41 @@ +/* + * valerie_remote.h -- Remote Parser + * Copyright (C) 2002-2003 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 + */ + +#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 index 00000000..5b9491ca --- /dev/null +++ b/src/valerie/valerie_response.c @@ -0,0 +1,244 @@ +/* + * valerie_response.c -- Response + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include +#include + +/* 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. +*/ + +char *valerie_response_get_error_string( valerie_response response ) +{ + 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, 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, 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, char *text, int size ) +{ + int ret = 0; + char *ptr = text; + + while ( size > 0 ) + { + int index = response->count - 1; + 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 index 00000000..1e8f1fa6 --- /dev/null +++ b/src/valerie/valerie_response.h @@ -0,0 +1,59 @@ +/* + * valerie_response.h -- Response + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_RESPONSE_H_ +#define _VALERIE_RESPONSE_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 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, char * ); +extern int valerie_response_printf( valerie_response, size_t, char *, ... ); +extern int valerie_response_write( valerie_response, 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 index 00000000..74e84834 --- /dev/null +++ b/src/valerie/valerie_socket.c @@ -0,0 +1,175 @@ +/* + * valerie_socket.c -- Client Socket + * Copyright (C) 2002-2003 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* System header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, 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 index 00000000..f016ca1a --- /dev/null +++ b/src/valerie/valerie_socket.h @@ -0,0 +1,56 @@ +/* + * valerie_socket.h -- Client Socket + * Copyright (C) 2002-2003 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 + * 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, 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 index 00000000..5725b0c4 --- /dev/null +++ b/src/valerie/valerie_status.c @@ -0,0 +1,160 @@ +/* + * valerie_status.c -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include +#include + +/* 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 = atof( 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 = atof( valerie_tokeniser_get_string( tokeniser, 6 ) ); + status->out = atof( valerie_tokeniser_get_string( tokeniser, 7 ) ); + status->length = atof( 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 = atof( valerie_tokeniser_get_string( tokeniser, 10 ) ); + status->tail_in = atof( valerie_tokeniser_get_string( tokeniser, 11 ) ); + status->tail_out = atof( valerie_tokeniser_get_string( tokeniser, 12 ) ); + status->tail_length = atof( valerie_tokeniser_get_string( tokeniser, 13 ) ); + status->seek_flag = atof( valerie_tokeniser_get_string( tokeniser, 14 ) ); + status->generation = atof( valerie_tokeniser_get_string( tokeniser, 15 ) ); + status->clip_index = atof( 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 ) +{ + 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\" %e %e %.2f %e %e %e \"%s\" %e %e %e %e %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 index 00000000..d0e45a94 --- /dev/null +++ b/src/valerie/valerie_status.h @@ -0,0 +1,83 @@ +/* + * valerie_status.h -- Unit Status Handling + * Copyright (C) 2002-2003 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 + */ + +#ifndef _VALERIE_STATUS_H_ +#define _VALERIE_STATUS_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 ]; + double position; + double speed; + double fps; + double in; + double out; + double length; + char tail_clip[ 2048 ]; + double tail_position; + double tail_in; + double tail_out; + double 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 index 00000000..7acaf853 --- /dev/null +++ b/src/valerie/valerie_tokeniser.c @@ -0,0 +1,172 @@ +/* + * valerie_tokeniser.c -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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, 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 index 00000000..9d0838cc --- /dev/null +++ b/src/valerie/valerie_tokeniser.h @@ -0,0 +1,55 @@ +/* + * valerie_tokeniser.h -- String tokeniser + * Copyright (C) 2002-2003 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 + */ + +#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 *, 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 index 00000000..168f6a74 --- /dev/null +++ b/src/valerie/valerie_util.c @@ -0,0 +1,77 @@ +/* + * valerie_util.c -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +/* System header files */ +#include +#include + +/* 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 index 00000000..492eba04 --- /dev/null +++ b/src/valerie/valerie_util.h @@ -0,0 +1,37 @@ +/* + * valerie_util.h -- General Purpose Client Utilities + * Copyright (C) 2002-2003 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 + */ + +#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 -- 2.39.2