--- /dev/null
+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
+
--- /dev/null
+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 <framework/file.h>
+
+ All external modules have {prefix}/include/mlt on the include path. All
+ headers should also be included as:
+
+ #include <framework/file.h>
+
+ This allows migration of source between external and internal
+ modules. The configuration and Makefile template requirements will require
+ attention though.
+
+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.
+
--- /dev/null
+#!/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
+
--- /dev/null
+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
+
--- /dev/null
+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 <framework/file.h>
+
+ All external modules have {prefix}/include/mlt on the include path. All
+ headers should also be included as:
+
+ #include <framework/file.h>
+
+ This allows migration of source between external and internal
+ modules. The configuration and Makefile template requirements will require
+ attention though.
+
+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.
+
--- /dev/null
+#!/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
+
--- /dev/null
+
+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
--- /dev/null
+/** GENERATED FILE - DON'T EDIT */
+
+#ifndef _MLT_CONFIG_H_
+#define _MLT_CONFIG_H_
+
+#define PREFIX "/usr/local"
+#define PREFIX_DATA PREFIX "/share"
+
+#endif
+
--- /dev/null
+#!/bin/bash
--- /dev/null
+/*
+ * mlt_consumer.c -- abstraction for all consumer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_consumer.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_consumer.h -- abstraction for all consumer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_factory.h"
+#include "mlt_repository.h"
+
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_factory.h -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_filter.c -- abstraction for all filter services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_filter.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_filter.h -- abstraction for all filter services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_frame.c -- interface for all frame classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}
+
--- /dev/null
+/*
+ * mlt_frame.h -- interface for all frame classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_manager.h -- manager service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MLT_MANAGER_H_
+#define _MLT_MANAGER_H_
+
+mlt_producer mlt_manager_init( char **config );
+
+#endif
--- /dev/null
+/*
+ * mlt_multitrack.c -- multitrack service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_multitrack.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_multitrack.h -- multitrack service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_playlist.c -- playlist service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "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 );
+}
--- /dev/null
+/*
+ * mlt_playlist.h -- playlist service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_producer.c -- abstraction for all producer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_producer.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Forward references.
+*/
+
+static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_producer.h -- abstraction for all producer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_properties.c -- base properties class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_properties.h"
+#include "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* ---------------- // 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 );
+}
+
--- /dev/null
+/*
+ * mlt_properties.h -- base properties class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_property.c -- property class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** 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 );
+}
+
+
--- /dev/null
+/*
+ * mlt_property.h -- property class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * repository.c -- provides a map between service and shared objects
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mlt_repository.h"
+#include <stdlib.h>
+
+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 )
+{
+}
+
+
--- /dev/null
+/*
+ * repository.h -- provides a map between service and shared objects
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_service.c -- interface for all service classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_service.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** IMPORTANT NOTES
+
+ The base service implements a null frame producing service - as such,
+ it is functional without extension and will produce test cards frames
+ and PAL sized audio frames.
+
+ PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
+ CONTROL THIS IN EXTENDING CLASSES.
+*/
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_service.h -- interface for all service classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_tractor.c -- tractor service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_tractor.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_tractor.h -- tractor service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_transition.c -- abstraction for all transition services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_transition.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Forward references.
+*/
+
+static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_transition.h -- abstraction for all transition services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_types.h -- provides forward definitions of all public types
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MLT_TYPES_H_
+#define _MLT_TYPES_H_
+
+#include <stdint.h>
+
+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
--- /dev/null
+/*
+ * dv1394d.c -- A DV over IEEE 1394 TCP Server
+ *
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Authors:
+ * Dan Dennedy <dan@dennedy.org>
+ * Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+
+/* 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;
+}
--- /dev/null
+/*
+ * global_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <pthread.h>
+
+#include "dvunit.h"
+#include "global_commands.h"
+#include "raw1394util.h"
+#include <libavc1394/rom1394.h>
+#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] );
+ }
+ }
+}
+
+
--- /dev/null
+/*
+ * global_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _GLOBAL_COMMANDS_H_
+#define _GLOBAL_COMMANDS_H_
+
+#include <dv1394status.h>
+#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
--- /dev/null
+/*
+ * dvconnection.c -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+/* 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;
+}
--- /dev/null
+/*
+ * dvconnection.h -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_CONNECTION_H_
+#define _DV_CONNECTION_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <dvparser.h>
+#include <dvtokeniser.h>
+
+#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
--- /dev/null
+/*
+ * dvlocal.c -- Local dv1394d Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+/* Library header files */
+#include <dvutil.h>
+
+/* Application header files */
+#include <dvclipfactory.h>
+#include <dvframepool.h>
+#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 <dan@dennedy.org>\n"
+ " Charles Yates <charles.yates@pandora.be>\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( );
+}
--- /dev/null
+/*
+ * dvlocal.h -- Local dv1394d Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DV_LOCAL_H_
+#define _DV_LOCAL_H_
+
+/* Application header files */
+#include <dvparser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Local parser API.
+*/
+
+extern dv_parser dv_parser_init_local( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * log.h -- logging facility implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdio.h>
+
+#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 );
+}
--- /dev/null
+/*
+ * log.h -- logging facility header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <syslog.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum log_output {
+ log_stderr,
+ log_syslog
+};
+
+void dv1394d_log_init( enum log_output method, int threshold );
+void dv1394d_log( int priority, char *format, ... );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * dvserver.c -- DV Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include "log.h"
+#include <netdb.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+/* Application header files */
+#include "dvserver.h"
+#include "dvconnection.h"
+#include "dvlocal.h"
+#include "log.h"
+#include <dvremote.h>
+#include <dvtokeniser.h>
+
+/** 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 );
+ }
+}
--- /dev/null
+/*
+ * dvserver.h -- DV Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_SERVER_H_
+#define _DV_SERVER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include <dvparser.h>
+
+#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
--- /dev/null
+/*
+ * dvunit.c -- DV Transmission Unit Implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <libdv/dv1394.h>
+#include <libraw1394/raw1394.h>
+#include <libavc1394/avc1394_vcr.h>
+#include <sys/mman.h>
+
+#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 );
+}
--- /dev/null
+/*
+ * dvunit.h -- DV Transmission Unit Header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_UNIT_H_
+#define _DV_UNIT_H_
+
+#include <pthread.h>
+#include <libraw1394/raw1394.h>
+
+#include <dv1394notifier.h>
+#include <dv1394status.h>
+#include <dvpump.h>
+#include <dvplayer.h>
+#include <dvinput.h>
+#include <dverror.h>
+
+#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
--- /dev/null
+/*
+ * unit_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "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;
+}
--- /dev/null
+/*
+ * unit_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _UNIT_COMMANDS_H_
+#define _UNIT_COMMANDS_H_
+
+#include "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
--- /dev/null
+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
+
--- /dev/null
+#!/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
+
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../producers.dat
+libdv libmltdv.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+/*
+ * producer_libdv.c -- simple libdv test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "producer_libdv.h"
+#include <framework/mlt_frame.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libdv/dv.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+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 );
+}
+
--- /dev/null
+/*
+ * producer_libdv.h -- a DV decoder based on libdv
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PRODUCER_LIBDV_H_
+#define _PRODUCER_LIBDV_H_
+
+#include <framework/mlt_producer.h>
+
+extern mlt_producer producer_libdv_init( char *filename );
+
+#endif
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../producers.dat
+pixbuf libmltgtk2.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+/*
+ * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "producer_pixbuf.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+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 );
+}
+
--- /dev/null
+/*
+ * producer_pixbuf.h -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PRODUCER_PIXBUF_H_
+#define _PRODUCER_PIXBUF_H_
+
+#include <framework/mlt_producer.h>
+
+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
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../consumers.dat
+sdl libmltsdl.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * consumer_sdl.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "consumer_sdl.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * consumer_sdl.h -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONSUMER_SDL_H_
+#define _CONSUMER_SDL_H_
+
+#include <framework/mlt_consumer.h>
+
+extern mlt_consumer consumer_sdl_init( void * );
+
+#endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+#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 <stdio.h>
+
+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;
+}
--- /dev/null
+#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 <stdio.h>
+
+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;
+}
--- /dev/null
+
+AR = ar
+
+OBJS = valerie.o \
+ valerie_notifier.o \
+ valerie_parser.o \
+ valerie_response.o \
+ valerie_status.o \
+ valerie_tokeniser.o \
+ valerie_util.o \
+ valerie_remote.o \
+ valerie_socket.o
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS=-Wall -g -D_FILE_OFFSET_BITS=64 -pthread
+
+LDFLAGS=-ldv -lpthread
+
+all: libvalerie.a
+
+libvalerie.a: $(OBJS)
+ $(AR) rvu $@ $(OBJS)
+ ranlib $@
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+dist-clean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) libvalerie.a
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * valerie.c -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Application header files */
+#include "valerie.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Initialise the valerie structure.
+*/
+
+valerie valerie_init( valerie_parser parser )
+{
+ valerie this = malloc( sizeof( valerie_t ) );
+ if ( this != NULL )
+ {
+ memset( this, 0, sizeof( valerie_t ) );
+ this->parser = parser;
+ }
+ return this;
+}
+
+/** Set the response structure associated to the last command.
+*/
+
+static void valerie_set_last_response( valerie this, valerie_response response )
+{
+ if ( this != NULL )
+ {
+ if ( this->last_response != NULL )
+ valerie_response_close( this->last_response );
+ this->last_response = response;
+ }
+}
+
+/** Connect to the parser.
+*/
+
+valerie_error_code valerie_connect( valerie this )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ valerie_response response = valerie_parser_connect( this->parser );
+ if ( response != NULL )
+ {
+ valerie_set_last_response( this, response );
+ if ( valerie_response_get_error_code( response ) == 100 )
+ error = valerie_ok;
+ }
+ return error;
+}
+
+/** Interpret a non-context sensitive error code.
+*/
+
+static valerie_error_code valerie_get_error_code( valerie this, valerie_response response )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ switch( valerie_response_get_error_code( response ) )
+ {
+ case -1:
+ error = valerie_server_unavailable;
+ break;
+ case -2:
+ error = valerie_no_response;
+ break;
+ case 200:
+ case 201:
+ case 202:
+ error = valerie_ok;
+ break;
+ case 400:
+ error = valerie_invalid_command;
+ break;
+ case 401:
+ error = valerie_server_timeout;
+ break;
+ case 402:
+ error = valerie_missing_argument;
+ break;
+ case 403:
+ error = valerie_unit_unavailable;
+ break;
+ case 404:
+ error = valerie_invalid_file;
+ break;
+ default:
+ case 500:
+ error = valerie_unknown_error;
+ break;
+ }
+ return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_execute( valerie this, size_t size, 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie.h -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_H_
+#define _VALERIE_H_
+
+/* System header files */
+#include <limits.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_status.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Client error conditions
+*/
+
+typedef enum
+{
+ valerie_ok = 0,
+ valerie_malloc_failed,
+ valerie_unknown_error,
+ valerie_no_response,
+ valerie_invalid_command,
+ valerie_server_timeout,
+ valerie_missing_argument,
+ valerie_server_unavailable,
+ valerie_unit_creation_failed,
+ valerie_unit_unavailable,
+ valerie_invalid_file,
+ valerie_invalid_position
+}
+valerie_error_code;
+
+/** Clip index specification.
+*/
+
+typedef enum
+{
+ valerie_absolute = 0,
+ valerie_relative
+}
+valerie_clip_offset;
+
+/** Client structure.
+*/
+
+typedef struct
+{
+ valerie_parser parser;
+ valerie_response last_response;
+}
+*valerie, valerie_t;
+
+/** Client API.
+*/
+
+extern valerie valerie_init( valerie_parser );
+
+/* Connect to the valerie parser instance */
+extern valerie_error_code valerie_connect( valerie );
+
+/* Global functions */
+extern valerie_error_code valerie_set( valerie, char *, char * );
+extern valerie_error_code valerie_get( valerie, char *, char *, int );
+extern valerie_error_code valerie_run( valerie, char * );
+
+/* Unit functions */
+extern valerie_error_code valerie_unit_add( valerie, char *, int * );
+extern valerie_error_code valerie_unit_load( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_clipped( valerie, int, char *, 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
--- /dev/null
+/*
+ * valerie_notifier.c -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_notifier.h"
+
+/** Notifier initialisation.
+*/
+
+valerie_notifier valerie_notifier_init( )
+{
+ valerie_notifier this = 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_notifier.h -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_NOTIFIER_H_
+#define _VALERIE_NOTIFIER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include "valerie_status.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MAX_UNITS 16
+
+/** Status notifier definition.
+*/
+
+typedef struct
+{
+ pthread_mutex_t mutex;
+ pthread_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
--- /dev/null
+/*
+ * valerie_parser.c -- Valerie Parser for Miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_util.h"
+
+/** Connect to the parser.
+*/
+
+valerie_response valerie_parser_connect( valerie_parser parser )
+{
+ return parser->connect( parser->real );
+}
+
+/** Execute a command via the parser.
+*/
+
+valerie_response valerie_parser_execute( valerie_parser parser, char *command )
+{
+ return parser->execute( parser->real, command );
+}
+
+/** 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_parser.h -- Valerie Parser for Miracle Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_PARSER_H_
+#define _VALERIE_PARSER_H_
+
+/* 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
--- /dev/null
+/*
+ * valerie_remote.c -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Application header files */
+#include "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;
+}
--- /dev/null
+/*
+ * valerie_remote.h -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_REMOTE_H_
+#define _VALERIE_REMOTE_H_
+
+/* Application header files */
+#include "valerie_parser.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Remote parser API.
+*/
+
+extern valerie_parser valerie_parser_init_remote( char *, int );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_response.c -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_response.h"
+
+/** Construct a new dv response.
+*/
+
+valerie_response valerie_response_init( )
+{
+ valerie_response response = malloc( sizeof( valerie_response_t ) );
+ if ( response != NULL )
+ memset( response, 0, sizeof( valerie_response_t ) );
+ return response;
+}
+
+/** Clone a dv response
+*/
+
+valerie_response valerie_response_clone( valerie_response response )
+{
+ valerie_response clone = valerie_response_init( );
+ if ( clone != NULL && response != NULL )
+ {
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ {
+ char *line = valerie_response_get_line( response, index );
+ valerie_response_printf( clone, strlen( line ) + 2, "%s\n", line );
+ }
+ }
+ return clone;
+}
+
+/** Get the error code associated to the response.
+*/
+
+int valerie_response_get_error_code( valerie_response response )
+{
+ int error_code = -1;
+ if ( response != NULL )
+ {
+ if ( response->count > 0 )
+ {
+ if ( sscanf( response->array[ 0 ], "%d", &error_code ) != 1 )
+ error_code = 0;
+ }
+ else
+ {
+ error_code = -2;
+ }
+ }
+ return error_code;
+}
+
+/** Get the error description associated to the response.
+*/
+
+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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_response.h -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_RESPONSE_H_
+#define _VALERIE_RESPONSE_H_
+
+#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
--- /dev/null
+/*
+ * valerie_socket.c -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* 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 );
+}
--- /dev/null
+/*
+ * valerie_socket.h -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_SOCKET_H_
+#define _VALERIE_SOCKET_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for socket.
+*/
+
+typedef struct
+{
+ char *server;
+ int port;
+ int fd;
+ int no_close;
+}
+*valerie_socket, valerie_socket_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_socket valerie_socket_init( char *, int );
+extern int valerie_socket_connect( valerie_socket );
+extern valerie_socket valerie_socket_init_fd( int );
+extern int valerie_socket_read_data( valerie_socket, char *, int );
+extern int valerie_socket_write_data( valerie_socket, char *, int );
+extern void valerie_socket_close( valerie_socket );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_status.c -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_status.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Parse a unit status string.
+*/
+
+void valerie_status_parse( valerie_status status, char *text )
+{
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ if ( valerie_tokeniser_parse_new( tokeniser, text, " " ) == 17 )
+ {
+ status->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+ strncpy( status->clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' ), sizeof( status->clip ) );
+ status->position = 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 ) );
+}
--- /dev/null
+/*
+ * valerie_status.h -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_STATUS_H_
+#define _VALERIE_STATUS_H_
+
+#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
--- /dev/null
+/*
+ * valerie_tokeniser.c -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+valerie_tokeniser valerie_tokeniser_init( )
+{
+ valerie_tokeniser tokeniser = malloc( sizeof( valerie_tokeniser_t ) );
+ if ( tokeniser != NULL )
+ memset( tokeniser, 0, sizeof( valerie_tokeniser_t ) );
+ return tokeniser;
+}
+
+/** Clear the tokeniser.
+*/
+
+static void valerie_tokeniser_clear( valerie_tokeniser tokeniser )
+{
+ int index = 0;
+ for ( index = 0; index < tokeniser->count; index ++ )
+ free( tokeniser->tokens[ index ] );
+ tokeniser->count = 0;
+ free( tokeniser->input );
+ tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int valerie_tokeniser_append( valerie_tokeniser tokeniser, char *token )
+{
+ int error = 0;
+
+ if ( tokeniser->count == tokeniser->size )
+ {
+ tokeniser->size += 20;
+ tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+ }
+
+ if ( tokeniser->tokens != NULL )
+ {
+ tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+ }
+ else
+ {
+ tokeniser->count = 0;
+ error = -1;
+ }
+ return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int valerie_tokeniser_parse_new( valerie_tokeniser tokeniser, char *string, 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 );
+}
--- /dev/null
+/*
+ * valerie_tokeniser.h -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_TOKENISER_H_
+#define _VALERIE_TOKENISER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for tokeniser.
+*/
+
+typedef struct
+{
+ char *input;
+ char **tokens;
+ int count;
+ int size;
+}
+*valerie_tokeniser, valerie_tokeniser_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_tokeniser valerie_tokeniser_init( );
+extern int valerie_tokeniser_parse_new( valerie_tokeniser, char *, 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
--- /dev/null
+/*
+ * valerie_util.c -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <string.h>
+#include <ctype.h>
+
+/* Application header files */
+#include "valerie_util.h"
+
+/** Remove LF or CR/LF terminations from the input string.
+*/
+
+char *valerie_util_chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+/** Remove leading and trailing spaces from the input string.
+*/
+
+char *valerie_util_trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+/** Strip the specified string of leading and trailing 'value' (ie: ").
+*/
+
+char *valerie_util_strip( char *input, char value )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, value );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == value )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
--- /dev/null
+/*
+ * valerie_util.h -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_UTIL_H_
+#define _VALERIE_UTIL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *valerie_util_chomp( char * );
+extern char *valerie_util_trim( char * );
+extern char *valerie_util_strip( char *, char );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+
+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
--- /dev/null
+/** GENERATED FILE - DON'T EDIT */
+
+#ifndef _MLT_CONFIG_H_
+#define _MLT_CONFIG_H_
+
+#define PREFIX "/usr/local"
+#define PREFIX_DATA PREFIX "/share"
+
+#endif
+
--- /dev/null
+#!/bin/bash
--- /dev/null
+/*
+ * mlt_consumer.c -- abstraction for all consumer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_consumer.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_consumer.h -- abstraction for all consumer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_factory.h"
+#include "mlt_repository.h"
+
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_factory.h -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_filter.c -- abstraction for all filter services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_filter.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int filter_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_filter.h -- abstraction for all filter services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_frame.c -- interface for all frame classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}
+
--- /dev/null
+/*
+ * mlt_frame.h -- interface for all frame classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_manager.h -- manager service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MLT_MANAGER_H_
+#define _MLT_MANAGER_H_
+
+mlt_producer mlt_manager_init( char **config );
+
+#endif
--- /dev/null
+/*
+ * mlt_multitrack.c -- multitrack service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_multitrack.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_multitrack.h -- multitrack service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_playlist.c -- playlist service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "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 );
+}
--- /dev/null
+/*
+ * mlt_playlist.h -- playlist service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_producer.c -- abstraction for all producer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_producer.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/** Forward references.
+*/
+
+static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_producer.h -- abstraction for all producer services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_properties.c -- base properties class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_properties.h"
+#include "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* ---------------- // 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 );
+}
+
--- /dev/null
+/*
+ * mlt_properties.h -- base properties class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_property.c -- property class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_property.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** 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 );
+}
+
+
--- /dev/null
+/*
+ * mlt_property.h -- property class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * repository.c -- provides a map between service and shared objects
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mlt_repository.h"
+#include <stdlib.h>
+
+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 )
+{
+}
+
+
--- /dev/null
+/*
+ * repository.h -- provides a map between service and shared objects
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_service.c -- interface for all service classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "mlt_service.h"
+#include "mlt_frame.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** IMPORTANT NOTES
+
+ The base service implements a null frame producing service - as such,
+ it is functional without extension and will produce test cards frames
+ and PAL sized audio frames.
+
+ PLEASE DO NOT CHANGE THIS BEHAVIOUR!!! OVERRIDE THE METHODS THAT
+ CONTROL THIS IN EXTENDING CLASSES.
+*/
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_service.h -- interface for all service classes
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
+
--- /dev/null
+/*
+ * mlt_tractor.c -- tractor service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_tractor.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * mlt_tractor.h -- tractor service class
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_transition.c -- abstraction for all transition services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "mlt_transition.h"
+#include "mlt_frame.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Forward references.
+*/
+
+static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
+
+/** 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 );
+}
--- /dev/null
+/*
+ * mlt_transition.h -- abstraction for all transition services
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#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
--- /dev/null
+/*
+ * mlt_types.h -- provides forward definitions of all public types
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MLT_TYPES_H_
+#define _MLT_TYPES_H_
+
+#include <stdint.h>
+
+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
--- /dev/null
+/*
+ * dv1394d.c -- A DV over IEEE 1394 TCP Server
+ *
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Authors:
+ * Dan Dennedy <dan@dennedy.org>
+ * Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+
+/* 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;
+}
--- /dev/null
+/*
+ * global_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <pthread.h>
+
+#include "dvunit.h"
+#include "global_commands.h"
+#include "raw1394util.h"
+#include <libavc1394/rom1394.h>
+#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] );
+ }
+ }
+}
+
+
--- /dev/null
+/*
+ * global_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _GLOBAL_COMMANDS_H_
+#define _GLOBAL_COMMANDS_H_
+
+#include <dv1394status.h>
+#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
--- /dev/null
+/*
+ * dvconnection.c -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+/* 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;
+}
--- /dev/null
+/*
+ * dvconnection.h -- DV Connection Handler
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_CONNECTION_H_
+#define _DV_CONNECTION_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <dvparser.h>
+#include <dvtokeniser.h>
+
+#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
--- /dev/null
+/*
+ * dvlocal.c -- Local dv1394d Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+/* Library header files */
+#include <dvutil.h>
+
+/* Application header files */
+#include <dvclipfactory.h>
+#include <dvframepool.h>
+#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 <dan@dennedy.org>\n"
+ " Charles Yates <charles.yates@pandora.be>\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( );
+}
--- /dev/null
+/*
+ * dvlocal.h -- Local dv1394d Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DV_LOCAL_H_
+#define _DV_LOCAL_H_
+
+/* Application header files */
+#include <dvparser.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Local parser API.
+*/
+
+extern dv_parser dv_parser_init_local( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * log.h -- logging facility implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdio.h>
+
+#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 );
+}
--- /dev/null
+/*
+ * log.h -- logging facility header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <syslog.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum log_output {
+ log_stderr,
+ log_syslog
+};
+
+void dv1394d_log_init( enum log_output method, int threshold );
+void dv1394d_log( int priority, char *format, ... );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * dvserver.c -- DV Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <netinet/in.h>
+#include "log.h"
+#include <netdb.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+/* Application header files */
+#include "dvserver.h"
+#include "dvconnection.h"
+#include "dvlocal.h"
+#include "log.h"
+#include <dvremote.h>
+#include <dvtokeniser.h>
+
+/** 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 );
+ }
+}
--- /dev/null
+/*
+ * dvserver.h -- DV Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_SERVER_H_
+#define _DV_SERVER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include <dvparser.h>
+
+#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
--- /dev/null
+/*
+ * dvunit.c -- DV Transmission Unit Implementation
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <libdv/dv1394.h>
+#include <libraw1394/raw1394.h>
+#include <libavc1394/avc1394_vcr.h>
+#include <sys/mman.h>
+
+#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 );
+}
--- /dev/null
+/*
+ * dvunit.h -- DV Transmission Unit Header
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_UNIT_H_
+#define _DV_UNIT_H_
+
+#include <pthread.h>
+#include <libraw1394/raw1394.h>
+
+#include <dv1394notifier.h>
+#include <dv1394status.h>
+#include <dvpump.h>
+#include <dvplayer.h>
+#include <dvinput.h>
+#include <dverror.h>
+
+#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
--- /dev/null
+/*
+ * unit_commands.c
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "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;
+}
--- /dev/null
+/*
+ * unit_commands.h
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _UNIT_COMMANDS_H_
+#define _UNIT_COMMANDS_H_
+
+#include "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
--- /dev/null
+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
+
--- /dev/null
+#!/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
+
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../producers.dat
+libdv libmltdv.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+/*
+ * producer_libdv.c -- simple libdv test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "producer_libdv.h"
+#include <framework/mlt_frame.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <libdv/dv.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+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 );
+}
+
--- /dev/null
+/*
+ * producer_libdv.h -- a DV decoder based on libdv
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PRODUCER_LIBDV_H_
+#define _PRODUCER_LIBDV_H_
+
+#include <framework/mlt_producer.h>
+
+extern mlt_producer producer_libdv_init( char *filename );
+
+#endif
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../producers.dat
+pixbuf libmltgtk2.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+/*
+ * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "producer_pixbuf.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+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 );
+}
+
--- /dev/null
+/*
+ * producer_pixbuf.h -- raster image loader based upon gdk-pixbuf
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PRODUCER_PIXBUF_H_
+#define _PRODUCER_PIXBUF_H_
+
+#include <framework/mlt_producer.h>
+
+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
--- /dev/null
+
+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
--- /dev/null
+#!/bin/bash
+
+if [ "$help" != "1" ]
+then
+
+cat << EOF >> ../consumers.dat
+sdl libmltsdl.so
+EOF
+
+fi
+
--- /dev/null
+/*
+ * consumer_sdl.c -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "consumer_sdl.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+
+/** 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 );
+}
+
--- /dev/null
+/*
+ * consumer_sdl.h -- A Simple DirectMedia Layer consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONSUMER_SDL_H_
+#define _CONSUMER_SDL_H_
+
+#include <framework/mlt_consumer.h>
+
+extern mlt_consumer consumer_sdl_init( void * );
+
+#endif
--- /dev/null
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "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;
+}
+
--- /dev/null
+#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 <stdio.h>
+
+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;
+}
--- /dev/null
+#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 <stdio.h>
+
+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;
+}
--- /dev/null
+
+AR = ar
+
+OBJS = valerie.o \
+ valerie_notifier.o \
+ valerie_parser.o \
+ valerie_response.o \
+ valerie_status.o \
+ valerie_tokeniser.o \
+ valerie_util.o \
+ valerie_remote.o \
+ valerie_socket.o
+
+SRCS := $(OBJS:.o=.c)
+
+CFLAGS=-Wall -g -D_FILE_OFFSET_BITS=64 -pthread
+
+LDFLAGS=-ldv -lpthread
+
+all: libvalerie.a
+
+libvalerie.a: $(OBJS)
+ $(AR) rvu $@ $(OBJS)
+ ranlib $@
+
+depend: $(SRCS)
+ $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+dist-clean: clean
+ rm -f .depend
+
+clean:
+ rm -f $(OBJS) libvalerie.a
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+/*
+ * valerie.c -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* Application header files */
+#include "valerie.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Initialise the valerie structure.
+*/
+
+valerie valerie_init( valerie_parser parser )
+{
+ valerie this = malloc( sizeof( valerie_t ) );
+ if ( this != NULL )
+ {
+ memset( this, 0, sizeof( valerie_t ) );
+ this->parser = parser;
+ }
+ return this;
+}
+
+/** Set the response structure associated to the last command.
+*/
+
+static void valerie_set_last_response( valerie this, valerie_response response )
+{
+ if ( this != NULL )
+ {
+ if ( this->last_response != NULL )
+ valerie_response_close( this->last_response );
+ this->last_response = response;
+ }
+}
+
+/** Connect to the parser.
+*/
+
+valerie_error_code valerie_connect( valerie this )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ valerie_response response = valerie_parser_connect( this->parser );
+ if ( response != NULL )
+ {
+ valerie_set_last_response( this, response );
+ if ( valerie_response_get_error_code( response ) == 100 )
+ error = valerie_ok;
+ }
+ return error;
+}
+
+/** Interpret a non-context sensitive error code.
+*/
+
+static valerie_error_code valerie_get_error_code( valerie this, valerie_response response )
+{
+ valerie_error_code error = valerie_server_unavailable;
+ switch( valerie_response_get_error_code( response ) )
+ {
+ case -1:
+ error = valerie_server_unavailable;
+ break;
+ case -2:
+ error = valerie_no_response;
+ break;
+ case 200:
+ case 201:
+ case 202:
+ error = valerie_ok;
+ break;
+ case 400:
+ error = valerie_invalid_command;
+ break;
+ case 401:
+ error = valerie_server_timeout;
+ break;
+ case 402:
+ error = valerie_missing_argument;
+ break;
+ case 403:
+ error = valerie_unit_unavailable;
+ break;
+ case 404:
+ error = valerie_invalid_file;
+ break;
+ default:
+ case 500:
+ error = valerie_unknown_error;
+ break;
+ }
+ return error;
+}
+
+/** Execute a command.
+*/
+
+valerie_error_code valerie_execute( valerie this, size_t size, 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie.h -- High Level Client API for miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_H_
+#define _VALERIE_H_
+
+/* System header files */
+#include <limits.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_status.h"
+#include "valerie_notifier.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Client error conditions
+*/
+
+typedef enum
+{
+ valerie_ok = 0,
+ valerie_malloc_failed,
+ valerie_unknown_error,
+ valerie_no_response,
+ valerie_invalid_command,
+ valerie_server_timeout,
+ valerie_missing_argument,
+ valerie_server_unavailable,
+ valerie_unit_creation_failed,
+ valerie_unit_unavailable,
+ valerie_invalid_file,
+ valerie_invalid_position
+}
+valerie_error_code;
+
+/** Clip index specification.
+*/
+
+typedef enum
+{
+ valerie_absolute = 0,
+ valerie_relative
+}
+valerie_clip_offset;
+
+/** Client structure.
+*/
+
+typedef struct
+{
+ valerie_parser parser;
+ valerie_response last_response;
+}
+*valerie, valerie_t;
+
+/** Client API.
+*/
+
+extern valerie valerie_init( valerie_parser );
+
+/* Connect to the valerie parser instance */
+extern valerie_error_code valerie_connect( valerie );
+
+/* Global functions */
+extern valerie_error_code valerie_set( valerie, char *, char * );
+extern valerie_error_code valerie_get( valerie, char *, char *, int );
+extern valerie_error_code valerie_run( valerie, char * );
+
+/* Unit functions */
+extern valerie_error_code valerie_unit_add( valerie, char *, int * );
+extern valerie_error_code valerie_unit_load( valerie, int, char * );
+extern valerie_error_code valerie_unit_load_clipped( valerie, int, char *, 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
--- /dev/null
+/*
+ * valerie_notifier.c -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/* Application header files */
+#include "valerie_notifier.h"
+
+/** Notifier initialisation.
+*/
+
+valerie_notifier valerie_notifier_init( )
+{
+ valerie_notifier this = 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_notifier.h -- Unit Status Notifier Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_NOTIFIER_H_
+#define _VALERIE_NOTIFIER_H_
+
+/* System header files */
+#include <pthread.h>
+
+/* Application header files */
+#include "valerie_status.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MAX_UNITS 16
+
+/** Status notifier definition.
+*/
+
+typedef struct
+{
+ pthread_mutex_t mutex;
+ pthread_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
--- /dev/null
+/*
+ * valerie_parser.c -- Valerie Parser for Miracle
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_parser.h"
+#include "valerie_util.h"
+
+/** Connect to the parser.
+*/
+
+valerie_response valerie_parser_connect( valerie_parser parser )
+{
+ return parser->connect( parser->real );
+}
+
+/** Execute a command via the parser.
+*/
+
+valerie_response valerie_parser_execute( valerie_parser parser, char *command )
+{
+ return parser->execute( parser->real, command );
+}
+
+/** 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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_parser.h -- Valerie Parser for Miracle Server
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_PARSER_H_
+#define _VALERIE_PARSER_H_
+
+/* 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
--- /dev/null
+/*
+ * valerie_remote.c -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Application header files */
+#include "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;
+}
--- /dev/null
+/*
+ * valerie_remote.h -- Remote Parser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_REMOTE_H_
+#define _VALERIE_REMOTE_H_
+
+/* Application header files */
+#include "valerie_parser.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Remote parser API.
+*/
+
+extern valerie_parser valerie_parser_init_remote( char *, int );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_response.c -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_response.h"
+
+/** Construct a new dv response.
+*/
+
+valerie_response valerie_response_init( )
+{
+ valerie_response response = malloc( sizeof( valerie_response_t ) );
+ if ( response != NULL )
+ memset( response, 0, sizeof( valerie_response_t ) );
+ return response;
+}
+
+/** Clone a dv response
+*/
+
+valerie_response valerie_response_clone( valerie_response response )
+{
+ valerie_response clone = valerie_response_init( );
+ if ( clone != NULL && response != NULL )
+ {
+ int index = 0;
+ for ( index = 0; index < valerie_response_count( response ); index ++ )
+ {
+ char *line = valerie_response_get_line( response, index );
+ valerie_response_printf( clone, strlen( line ) + 2, "%s\n", line );
+ }
+ }
+ return clone;
+}
+
+/** Get the error code associated to the response.
+*/
+
+int valerie_response_get_error_code( valerie_response response )
+{
+ int error_code = -1;
+ if ( response != NULL )
+ {
+ if ( response->count > 0 )
+ {
+ if ( sscanf( response->array[ 0 ], "%d", &error_code ) != 1 )
+ error_code = 0;
+ }
+ else
+ {
+ error_code = -2;
+ }
+ }
+ return error_code;
+}
+
+/** Get the error description associated to the response.
+*/
+
+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 );
+ }
+}
--- /dev/null
+/*
+ * valerie_response.h -- Response
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_RESPONSE_H_
+#define _VALERIE_RESPONSE_H_
+
+#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
--- /dev/null
+/*
+ * valerie_socket.c -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* 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 );
+}
--- /dev/null
+/*
+ * valerie_socket.h -- Client Socket
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VALERIE_SOCKET_H_
+#define _VALERIE_SOCKET_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for socket.
+*/
+
+typedef struct
+{
+ char *server;
+ int port;
+ int fd;
+ int no_close;
+}
+*valerie_socket, valerie_socket_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_socket valerie_socket_init( char *, int );
+extern int valerie_socket_connect( valerie_socket );
+extern valerie_socket valerie_socket_init_fd( int );
+extern int valerie_socket_read_data( valerie_socket, char *, int );
+extern int valerie_socket_write_data( valerie_socket, char *, int );
+extern void valerie_socket_close( valerie_socket );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * valerie_status.c -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_status.h"
+#include "valerie_tokeniser.h"
+#include "valerie_util.h"
+
+/** Parse a unit status string.
+*/
+
+void valerie_status_parse( valerie_status status, char *text )
+{
+ valerie_tokeniser tokeniser = valerie_tokeniser_init( );
+ if ( valerie_tokeniser_parse_new( tokeniser, text, " " ) == 17 )
+ {
+ status->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
+ strncpy( status->clip, valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' ), sizeof( status->clip ) );
+ status->position = 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 ) );
+}
--- /dev/null
+/*
+ * valerie_status.h -- Unit Status Handling
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_STATUS_H_
+#define _VALERIE_STATUS_H_
+
+#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
--- /dev/null
+/*
+ * valerie_tokeniser.c -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <stdlib.h>
+#include <string.h>
+
+/* Application header files */
+#include "valerie_tokeniser.h"
+
+/** Initialise a tokeniser.
+*/
+
+valerie_tokeniser valerie_tokeniser_init( )
+{
+ valerie_tokeniser tokeniser = malloc( sizeof( valerie_tokeniser_t ) );
+ if ( tokeniser != NULL )
+ memset( tokeniser, 0, sizeof( valerie_tokeniser_t ) );
+ return tokeniser;
+}
+
+/** Clear the tokeniser.
+*/
+
+static void valerie_tokeniser_clear( valerie_tokeniser tokeniser )
+{
+ int index = 0;
+ for ( index = 0; index < tokeniser->count; index ++ )
+ free( tokeniser->tokens[ index ] );
+ tokeniser->count = 0;
+ free( tokeniser->input );
+ tokeniser->input = NULL;
+}
+
+/** Append a string to the tokeniser.
+*/
+
+static int valerie_tokeniser_append( valerie_tokeniser tokeniser, char *token )
+{
+ int error = 0;
+
+ if ( tokeniser->count == tokeniser->size )
+ {
+ tokeniser->size += 20;
+ tokeniser->tokens = realloc( tokeniser->tokens, tokeniser->size * sizeof( char * ) );
+ }
+
+ if ( tokeniser->tokens != NULL )
+ {
+ tokeniser->tokens[ tokeniser->count ++ ] = strdup( token );
+ }
+ else
+ {
+ tokeniser->count = 0;
+ error = -1;
+ }
+ return error;
+}
+
+/** Parse a string by splitting on the delimiter provided.
+*/
+
+int valerie_tokeniser_parse_new( valerie_tokeniser tokeniser, char *string, 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 );
+}
--- /dev/null
+/*
+ * valerie_tokeniser.h -- String tokeniser
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_TOKENISER_H_
+#define _VALERIE_TOKENISER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Structure for tokeniser.
+*/
+
+typedef struct
+{
+ char *input;
+ char **tokens;
+ int count;
+ int size;
+}
+*valerie_tokeniser, valerie_tokeniser_t;
+
+/** Remote parser API.
+*/
+
+extern valerie_tokeniser valerie_tokeniser_init( );
+extern int valerie_tokeniser_parse_new( valerie_tokeniser, char *, 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
--- /dev/null
+/*
+ * valerie_util.c -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* System header files */
+#include <string.h>
+#include <ctype.h>
+
+/* Application header files */
+#include "valerie_util.h"
+
+/** Remove LF or CR/LF terminations from the input string.
+*/
+
+char *valerie_util_chomp( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ if ( length && input[ length - 1 ] == '\n' )
+ input[ length - 1 ] = '\0';
+ if ( length > 1 && input[ length - 2 ] == '\r' )
+ input[ length - 2 ] = '\0';
+ }
+ return input;
+}
+
+/** Remove leading and trailing spaces from the input string.
+*/
+
+char *valerie_util_trim( char *input )
+{
+ if ( input != NULL )
+ {
+ int length = strlen( input );
+ int first = 0;
+ while( first < length && isspace( input[ first ] ) )
+ first ++;
+ memmove( input, input + first, length - first + 1 );
+ length = length - first;
+ while ( length > 0 && isspace( input[ length - 1 ] ) )
+ input[ -- length ] = '\0';
+ }
+ return input;
+}
+
+/** Strip the specified string of leading and trailing 'value' (ie: ").
+*/
+
+char *valerie_util_strip( char *input, char value )
+{
+ if ( input != NULL )
+ {
+ char *ptr = strrchr( input, value );
+ if ( ptr != NULL )
+ *ptr = '\0';
+ if ( input[ 0 ] == value )
+ strcpy( input, input + 1 );
+ }
+ return input;
+}
--- /dev/null
+/*
+ * valerie_util.h -- General Purpose Client Utilities
+ * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VALERIE_UTIL_H_
+#define _VALERIE_UTIL_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern char *valerie_util_chomp( char * );
+extern char *valerie_util_trim( char * );
+extern char *valerie_util_strip( char *, char );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif