From 8dde1311719b29c63efb379566916cb1aa9a7cd7 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 18 Sep 2013 17:39:30 +0200 Subject: Work on FFMpegInvoker --- CMakeLists.txt | 23 +- apps/restart-on-term.sh | 9 + apps/samples/vrml/ffmpeg-server.scxml | 245 +++++++ apps/samples/vrml/img/close.png | Bin 0 -> 855 bytes apps/samples/vrml/img/pitchRoll-handle.png | Bin 0 -> 6142 bytes apps/samples/vrml/img/pitchRoll.pxm | Bin 0 -> 29076 bytes apps/samples/vrml/img/xy-handle.png | Bin 0 -> 4530 bytes apps/samples/vrml/img/xy.pxm | Bin 0 -> 29076 bytes apps/samples/vrml/img/yawZoom-handle.png | Bin 0 -> 5603 bytes apps/samples/vrml/img/yawZoom.pxm | Bin 0 -> 29076 bytes apps/samples/vrml/stress-vrml-server.pl | 35 + apps/samples/vrml/viewer.html | 21 +- apps/samples/vrml/viewer.js | 320 +++++++++- apps/samples/vrml/vrml-server.scxml | 258 +++----- apps/samples/vrml/vrml-server.scxml.old | 416 ++++++++++++ apps/w3c-mmi/im/MMISessionManager.cpp | 3 +- contrib/cmake/FindFFMPEG.cmake | 12 +- contrib/cmake/FindICU.cmake | 317 +++++++++ contrib/local/compress_and_upload_deps.sh | 2 +- src/uscxml/Interpreter.cpp | 47 +- src/uscxml/Interpreter.h | 9 +- src/uscxml/Message.cpp | 27 +- src/uscxml/Message.h | 66 +- src/uscxml/URL.cpp | 18 + src/uscxml/URL.h | 1 + src/uscxml/URL.mm | 4 +- src/uscxml/UUID.cpp | 14 + src/uscxml/UUID.h | 20 + src/uscxml/interpreter/InterpreterDraft6.cpp | 3 +- src/uscxml/interpreter/InterpreterDraft7.cpp | 3 +- .../ecmascript/JavaScriptCore/JSCDataModel.cpp | 4 +- .../datamodel/ecmascript/v8/V8DataModel.cpp | 6 +- .../plugins/datamodel/prolog/swi/SWIDataModel.cpp | 2 + .../plugins/datamodel/xpath/XPathDataModel.cpp | 10 +- src/uscxml/plugins/element/file/FileElement.cpp | 2 +- .../plugins/element/respond/RespondElement.cpp | 20 +- src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp | 16 +- src/uscxml/plugins/invoker/audio/OpenALInvoker.h | 2 +- .../plugins/invoker/ffmpeg/FFMPEGInvoker.cpp | 706 +++++++++------------ src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h | 49 +- .../invoker/filesystem/dirmon/DirMonInvoker.cpp | 18 +- .../invoker/graphics/openscenegraph/OSGInvoker.cpp | 3 +- .../openscenegraph/converter/OSGConverter.cpp | 261 +++++--- .../openscenegraph/converter/OSGConverter.h | 4 +- .../plugins/invoker/heartbeat/HeartbeatInvoker.cpp | 6 +- .../plugins/invoker/http/HTTPServletInvoker.cpp | 12 +- src/uscxml/plugins/invoker/miles/SpatialAudio.cpp | 4 +- src/uscxml/plugins/invoker/miles/SpatialAudio.h | 2 +- .../plugins/invoker/umundo/UmundoInvoker.cpp | 12 +- .../ioprocessor/basichttp/BasicHTTPIOProcessor.cpp | 10 +- .../ioprocessor/modality/MMIHTTPIOProcessor.cpp | 10 +- src/uscxml/server/HTTPServer.cpp | 39 +- src/uscxml/server/InterpreterServlet.cpp | 17 +- test/CMakeLists.txt | 7 + test/src/test-ffmpeg.cpp | 371 +++++++++++ test/src/test-url.cpp | 2 +- 56 files changed, 2613 insertions(+), 855 deletions(-) create mode 100644 apps/restart-on-term.sh create mode 100644 apps/samples/vrml/ffmpeg-server.scxml create mode 100644 apps/samples/vrml/img/close.png create mode 100644 apps/samples/vrml/img/pitchRoll-handle.png create mode 100644 apps/samples/vrml/img/pitchRoll.pxm create mode 100644 apps/samples/vrml/img/xy-handle.png create mode 100644 apps/samples/vrml/img/xy.pxm create mode 100644 apps/samples/vrml/img/yawZoom-handle.png create mode 100644 apps/samples/vrml/img/yawZoom.pxm create mode 100755 apps/samples/vrml/stress-vrml-server.pl create mode 100644 apps/samples/vrml/vrml-server.scxml.old create mode 100644 contrib/cmake/FindICU.cmake create mode 100644 src/uscxml/UUID.cpp create mode 100644 src/uscxml/UUID.h create mode 100644 test/src/test-ffmpeg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 96096ec..8a940d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,7 @@ if (WIN32) include_directories(${PROJECT_SOURCE_DIR}/contrib/src/inttypes) endif() include_directories(${PROJECT_SOURCE_DIR}/contrib/src/jsmn) +#include_directories(${PROJECT_SOURCE_DIR}/contrib/src/google-url) ############################################################ # General setup @@ -352,6 +353,13 @@ else() add_definitions("-DUSCXML_STATIC") endif() +# file(GLOB +# GURL_SOURCE ${PROJECT_SOURCE_DIR}/contrib/src/google-url/googleurl/src/*.cc +# GURL_SOURCE ${PROJECT_SOURCE_DIR}/contrib/src/google-url/googleurl/src/*.h) +# list(APPEND USCXML_FILES ${GURL_SOURCE}) +# list(APPEND USCXML_FILES ${PROJECT_SOURCE_DIR}/contrib/src/google-url/base/string16.cc) + + # library suffix order if (IOS) LIST(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".dylib") @@ -447,6 +455,11 @@ elseif(WIN32) list (APPEND USCXML_CORE_LIBS ${XML_LIBRARIES}) endif() +# ICU +# find_package(ICU REQUIRED) +# include_directories(${ICU_INCLUDE_DIRS}) +# list (APPEND USCXML_CORE_LIBS ${ICU_LIBRARIES}) + # CURL find_package(CURL REQUIRED) include_directories(${CURL_INCLUDE_DIRS}) @@ -685,9 +698,13 @@ foreach( FILE ${USCXML_FILES} ) STRING(REGEX MATCH "[^\\/]*$" COMP_NAME ${PATH}) source_group("Element\\${COMP_NAME}" FILES ${FILE}) - elseif (${PATH} MATCHES ".*\\/ioprocessor\\/.*") - STRING(REGEX MATCH "[^\\/]*$" COMP_NAME ${PATH}) - source_group("IOProcessor\\${COMP_NAME}" FILES ${FILE}) + elseif (${PATH} MATCHES ".*\\/ioprocessor\\/.*") + STRING(REGEX MATCH "[^\\/]*$" COMP_NAME ${PATH}) + source_group("IOProcessor\\${COMP_NAME}" FILES ${FILE}) + + # elseif (${PATH} MATCHES ".*\\/google-url\\/.*") + # STRING(REGEX MATCH "[^\\/]*$" COMP_NAME ${PATH}) + # source_group("Interpreter\\URL" FILES ${FILE}) else () source_group(Interpreter FILES ${FILE}) diff --git a/apps/restart-on-term.sh b/apps/restart-on-term.sh new file mode 100644 index 0000000..37bb4fa --- /dev/null +++ b/apps/restart-on-term.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# see http://superuser.com/questions/223449/auto-restart-process-on-crash +PROGRAM=$1 + +RC=1 +while [ $RC -ne 0 ]; do + ./${PROGRAM} + RC=$? +done diff --git a/apps/samples/vrml/ffmpeg-server.scxml b/apps/samples/vrml/ffmpeg-server.scxml new file mode 100644 index 0000000..1fd4038 --- /dev/null +++ b/apps/samples/vrml/ffmpeg-server.scxml @@ -0,0 +1,245 @@ + + + + + + {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/samples/vrml/img/close.png b/apps/samples/vrml/img/close.png new file mode 100644 index 0000000..7233cbe Binary files /dev/null and b/apps/samples/vrml/img/close.png differ diff --git a/apps/samples/vrml/img/pitchRoll-handle.png b/apps/samples/vrml/img/pitchRoll-handle.png new file mode 100644 index 0000000..fcff0dd Binary files /dev/null and b/apps/samples/vrml/img/pitchRoll-handle.png differ diff --git a/apps/samples/vrml/img/pitchRoll.pxm b/apps/samples/vrml/img/pitchRoll.pxm new file mode 100644 index 0000000..1dbc3e2 Binary files /dev/null and b/apps/samples/vrml/img/pitchRoll.pxm differ diff --git a/apps/samples/vrml/img/xy-handle.png b/apps/samples/vrml/img/xy-handle.png new file mode 100644 index 0000000..0edb8cc Binary files /dev/null and b/apps/samples/vrml/img/xy-handle.png differ diff --git a/apps/samples/vrml/img/xy.pxm b/apps/samples/vrml/img/xy.pxm new file mode 100644 index 0000000..5e077ef Binary files /dev/null and b/apps/samples/vrml/img/xy.pxm differ diff --git a/apps/samples/vrml/img/yawZoom-handle.png b/apps/samples/vrml/img/yawZoom-handle.png new file mode 100644 index 0000000..f6a54ee Binary files /dev/null and b/apps/samples/vrml/img/yawZoom-handle.png differ diff --git a/apps/samples/vrml/img/yawZoom.pxm b/apps/samples/vrml/img/yawZoom.pxm new file mode 100644 index 0000000..ec00b18 Binary files /dev/null and b/apps/samples/vrml/img/yawZoom.pxm differ diff --git a/apps/samples/vrml/stress-vrml-server.pl b/apps/samples/vrml/stress-vrml-server.pl new file mode 100755 index 0000000..4145f29 --- /dev/null +++ b/apps/samples/vrml/stress-vrml-server.pl @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w + +use Math::Round; + +my $pi = 3.14159; + +# make one thousand requests with random parameters +for (my $i = 0; $i < 1000; $i++) { + my $pitch = rand(2*$pi); + my $roll = rand(2*$pi); + my $yaw = rand(2*$pi); + my $width = rand(400) + 200; + my $height = rand(400) + 200; + my $url = "http://epikur.local:8080/vrml/HARD_MP_VAL_002.png". + "?pitch=".sprintf("%.3f",$pitch). + "&roll=".sprintf("%.3f",$roll). + "&yaw=".sprintf("%.3f",$yaw). + "&width=".sprintf("%.0f",$width). + "&height=".sprintf("%.0f",$height); + print $url."\n"; + `wget '$url'`; +} + +# for (my $pitch = 0; $pitch < 2*$pi; $pitch += 0.01) { +# for (my $roll = 0; $roll < 2*$pi; $roll += 0.01) { +# for (my $yaw = 0; $yaw < 2*$pi; $yaw += 0.01) { +# my $url = "http://epikur.local:8081/vrml/HARD_MP_VAL_002.png". +# "?pitch=".sprintf("%.3f",$pitch). +# "&roll=".sprintf("%.3f",$roll). +# "&yaw=".sprintf("%.3f",$yaw); +# print $url."\n"; +# `wget '$url'`; +# } +# } +# } \ No newline at end of file diff --git a/apps/samples/vrml/viewer.html b/apps/samples/vrml/viewer.html index 9838c33..6e139e7 100644 --- a/apps/samples/vrml/viewer.html +++ b/apps/samples/vrml/viewer.html @@ -22,7 +22,22 @@ height:200px; padding:7px; } - + .tundra .dijitTooltipContainer { + background-color:rgba(200,200,200,0.5); + background:rgba(200,200,200,0.5); + } +/* .removeThumb { + background-image: url(img/close.png); + background-repeat: no-repeat; + background-size: 100%; + text-align: center; + border: 0px; + width: 20px; + height: 20px; + vertical-align: top; + margin: -3px 0px 0px -8px; + } +*/ @@ -220,23 +152,21 @@ + (!(_event.key in models) || wrls[_event.key].mtime > models[_event.key].mtime)"> - + @@ -244,83 +174,59 @@ - - + + + + +
+
+
+ + + + +
+ + + + + + + + + - - + - + - - - -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -346,18 +252,6 @@ - - -
-
-
- - - - - @@ -367,7 +261,7 @@ - + @@ -382,14 +276,34 @@ - +
- + + + + +
+
+
+ + + + + + + +
+
+
+ + + + diff --git a/apps/samples/vrml/vrml-server.scxml.old b/apps/samples/vrml/vrml-server.scxml.old new file mode 100644 index 0000000..70a7c3a --- /dev/null +++ b/apps/samples/vrml/vrml-server.scxml.old @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + + +
+
+
+ + + + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/apps/w3c-mmi/im/MMISessionManager.cpp b/apps/w3c-mmi/im/MMISessionManager.cpp index 83d4dea..2e68ff4 100644 --- a/apps/w3c-mmi/im/MMISessionManager.cpp +++ b/apps/w3c-mmi/im/MMISessionManager.cpp @@ -1,6 +1,7 @@ #include "MMISessionManager.h" #include #include +#include #include #include @@ -135,7 +136,7 @@ void MMISessionManager::received(const NewContextRequest& mmiEvent, const std::s newDOM.appendChild(newDOM.importNode(_protoInterpreter.getDocument().getDocumentElement(), true)); // instantiate new interpreter and name it after the context - std::string contextId = Interpreter::getUUID(); + std::string contextId = UUID::getUUID(); Interpreter interpreter = Interpreter::fromDOM(newDOM); interpreter.setFactory(_factory); interpreter.setName(contextId); diff --git a/contrib/cmake/FindFFMPEG.cmake b/contrib/cmake/FindFFMPEG.cmake index e01e1f9..397bcba 100644 --- a/contrib/cmake/FindFFMPEG.cmake +++ b/contrib/cmake/FindFFMPEG.cmake @@ -28,7 +28,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) pkg_check_modules(_FFMPEG_AVFORMAT libavformat) pkg_check_modules(_FFMPEG_AVUTIL libavutil) pkg_check_modules(_FFMPEG_SWSCALE libswscale) - pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample) +# pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample) endif (PKG_CONFIG_FOUND) find_path(FFMPEG_AVCODEC_INCLUDE_DIR @@ -57,10 +57,10 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) - find_library(FFMPEG_SWRESAMPLE - NAMES swresample - PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - ) + # find_library(FFMPEG_SWRESAMPLE + # NAMES swresample + # PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib + # ) if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT) set(FFMPEG_FOUND TRUE) @@ -74,7 +74,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) ${FFMPEG_LIBAVFORMAT} ${FFMPEG_LIBAVUTIL} ${FFMPEG_LIBSWSCALE} - ${FFMPEG_SWRESAMPLE} +# ${FFMPEG_SWRESAMPLE} ) endif (FFMPEG_FOUND) diff --git a/contrib/cmake/FindICU.cmake b/contrib/cmake/FindICU.cmake new file mode 100644 index 0000000..c48a9a0 --- /dev/null +++ b/contrib/cmake/FindICU.cmake @@ -0,0 +1,317 @@ +# +# Taken from: +# https://github.com/julp/FindICU.cmake +# +# This module can find the International Components for Unicode (ICU) Library +# +# Requirements: +# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args) +# +# The following variables will be defined for your use: +# - ICU_FOUND : were all of your specified components found (include dependencies)? +# - ICU_INCLUDE_DIRS : ICU include directory +# - ICU_LIBRARIES : ICU libraries +# - ICU_VERSION : complete version of ICU (x.y.z) +# - ICU_MAJOR_VERSION : major version of ICU +# - ICU_MINOR_VERSION : minor version of ICU +# - ICU_PATCH_VERSION : patch version of ICU +# - ICU__FOUND : were found? (FALSE for non specified component if it is not a dependency) +# +# For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways: +# - run cmake with -DICU_ROOT= +# - define an environment variable with the same name before running cmake +# With cmake-gui, before pressing "Configure": +# 1) Press "Add Entry" button +# 2) Add a new entry defined as: +# - Name: ICU_ROOT +# - Type: choose PATH in the selection list +# - Press "..." button and select the root installation of ICU +# +# Example Usage: +# +# 1. Copy this file in the root of your project source directory +# 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt: +# set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) +# 3. Finally call find_package() once, here are some examples to pick from +# +# Require ICU 4.4 or later +# find_package(ICU 4.4 REQUIRED) +# +# if(ICU_FOUND) +# include_directories(${ICU_INCLUDE_DIRS}) +# add_executable(myapp myapp.c) +# target_link_libraries(myapp ${ICU_LIBRARIES}) +# endif(ICU_FOUND) + +#============================================================================= +# Copyright (c) 2011-2013, julp +# +# Distributed under the OSI-approved BSD License +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#============================================================================= + +find_package(PkgConfig QUIET) + +########## Private ########## +if(NOT DEFINED ICU_PUBLIC_VAR_NS) + set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables +endif(NOT DEFINED ICU_PUBLIC_VAR_NS) +if(NOT DEFINED ICU_PRIVATE_VAR_NS) + set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables +endif(NOT DEFINED ICU_PRIVATE_VAR_NS) +if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables +endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + +function(icudebug _VARNAME) + if(${ICU_PUBLIC_VAR_NS}_DEBUG) + if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}") + else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ") + endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + endif(${ICU_PUBLIC_VAR_NS}_DEBUG) +endfunction(icudebug) + +set(${ICU_PRIVATE_VAR_NS}_ROOT "") +if(DEFINED ENV{ICU_ROOT}) + set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}") +endif(DEFINED ENV{ICU_ROOT}) +if (DEFINED ICU_ROOT) + set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}") +endif(DEFINED ICU_ROOT) + +set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES ) +set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES ) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64") + list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64") +endif(CMAKE_SIZEOF_VOID_P EQUAL 8) +list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin") +list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib") + +set(${ICU_PRIVATE_VAR_NS}_COMPONENTS ) +# ... +macro(icu_declare_component _NAME) + list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME}) + set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN}) +endmacro(icu_declare_component) + +icu_declare_component(data icudata) +icu_declare_component(uc icuuc) # Common and Data libraries +icu_declare_component(i18n icui18n icuin) # Internationalization library +icu_declare_component(io icuio ustdio) # Stream and I/O Library +icu_declare_component(le icule) # Layout library +icu_declare_component(lx iculx) # Paragraph Layout library + +########## Public ########## +set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE) +set(${ICU_PUBLIC_VAR_NS}_LIBRARIES ) +set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS ) +set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "") +foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS}) + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro +endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + +# Check components +if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least + set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) +else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}") + endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) +endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + +# Includes +find_path( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + NAMES unicode/utypes.h utypes.h + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES "include" + DOC "Include directories for ICU" +) + +if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + ########## ########## + if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + else() + message(FATAL_ERROR "ICU version header not found") + endif() + + if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3 + # [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0) + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*") + # + # Since version 4.9.1, ICU release version numbering was totaly changed, see: + # - http://site.icu-project.org/download/49 + # - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU + # + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[ + # [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0 + set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if + if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}") + endif() + else() + message(FATAL_ERROR "failed to detect ICU version") + endif() + set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}") + ########## ########## + + # Check dependencies (implies pkg-config) + if(PKG_CONFIG_FOUND) + set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP}) + pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET) + + if(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES}) + string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY}) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY}) + endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY) + endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + endif(PKG_CONFIG_FOUND) + + # Check libraries + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES ) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES ) + foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d") + endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME) + + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Release libraries for ICU" + ) + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Debug libraries for ICU" + ) + + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) + set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE) + else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + else() # both found + set( + ${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + ) + endif() + list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + + # Try to find out compiler flags + find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}) + if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + + # Check find_package arguments + include(FindPackageHandleStandardArgs) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args( + ${ICU_PUBLIC_VAR_NS} + REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION + ) + else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + message(FATAL_ERROR "Could not find ICU include directory") + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + +mark_as_advanced( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + ${ICU_PUBLIC_VAR_NS}_LIBRARIES +) + +# IN (args) +icudebug("FIND_COMPONENTS") +icudebug("FIND_REQUIRED") +icudebug("FIND_QUIETLY") +icudebug("FIND_VERSION") +# OUT +# Found +icudebug("FOUND") +icudebug("UC_FOUND") +icudebug("I18N_FOUND") +icudebug("IO_FOUND") +icudebug("LE_FOUND") +icudebug("LX_FOUND") +icudebug("DATA_FOUND") +# Flags +icudebug("C_FLAGS") +icudebug("CPP_FLAGS") +icudebug("CXX_FLAGS") +icudebug("C_SHARED_FLAGS") +icudebug("CPP_SHARED_FLAGS") +icudebug("CXX_SHARED_FLAGS") +# Linking +icudebug("INCLUDE_DIRS") +icudebug("LIBRARIES") +# Version +icudebug("MAJOR_VERSION") +icudebug("MINOR_VERSION") +icudebug("PATCH_VERSION") +icudebug("VERSION") \ No newline at end of file diff --git a/contrib/local/compress_and_upload_deps.sh b/contrib/local/compress_and_upload_deps.sh index 81df0f4..49b3949 100755 --- a/contrib/local/compress_and_upload_deps.sh +++ b/contrib/local/compress_and_upload_deps.sh @@ -31,7 +31,7 @@ cd ../prebuilt ssh ${USCXML_PREBUILT_HOST} mkdir -p ${USCXML_PREBUILT_PATH}/${VERSION} PLATFORMS=`find . -maxdepth 1 -type d -regex ./[^\.].*` -PLATFORMS="include" +PLATFORMS="windows-x86_64" for FILE in ${PLATFORMS}; do PLATFORM=`basename $FILE` if [ "$PLATFORM" != "include" ]; then diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 4e9dff0..3946bef 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1,6 +1,7 @@ #include "uscxml/Common.h" #include "uscxml/Interpreter.h" #include "uscxml/URL.h" +#include "uscxml/UUID.h" #include "uscxml/NameSpacingParser.h" #include "uscxml/debug/SCXMLDotWriter.h" @@ -9,9 +10,6 @@ #include #include -#include -#include -#include #include #include @@ -45,15 +43,6 @@ using namespace Arabica::DOM; std::map > Interpreter::_instances; tthread::recursive_mutex Interpreter::_instanceMutex; -boost::uuids::random_generator InterpreterImpl::uuidGen; - -std::string InterpreterImpl::getUUID() { - boost::uuids::uuid uuid = uuidGen(); - std::ostringstream os; - os << uuid; - return os.str(); -} - InterpreterImpl::InterpreterImpl() { _lastRunOnMainThread = 0; _nsURL = "*"; @@ -271,7 +260,7 @@ void InterpreterImpl::init() { _xpath.setNamespaceContext(_nsContext); if (_name.length() == 0) - _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : getUUID()); + _name = (HAS_ATTR(_scxml, "name") ? ATTR(_scxml, "name") : UUID::getUUID()); normalize(_scxml); @@ -293,7 +282,7 @@ void InterpreterImpl::init() { } if (_sessionId.length() == 0) - _sessionId = getUUID(); + _sessionId = UUID::getUUID(); _isInitialized = true; } @@ -347,7 +336,7 @@ void InterpreterImpl::normalize(Arabica::DOM::Element& scxml) { Arabica::DOM::Element stateElem = Arabica::DOM::Element(states[i]); stateElem.setAttribute("isFirstEntry", "true"); if (!stateElem.hasAttribute("id")) { - stateElem.setAttribute("id", getUUID()); + stateElem.setAttribute("id", UUID::getUUID()); } } @@ -356,7 +345,7 @@ void InterpreterImpl::normalize(Arabica::DOM::Element& scxml) { for (int i = 0; i < invokes.size(); i++) { Arabica::DOM::Element invokeElem = Arabica::DOM::Element(invokes[i]); if (!invokeElem.hasAttribute("id") && !invokeElem.hasAttribute("idlocation")) { - invokeElem.setAttribute("id", getUUID()); + invokeElem.setAttribute("id", UUID::getUUID()); } // // make sure every finalize element contained has the invoke id as an attribute // Arabica::XPath::NodeSet finalizes = _xpath.evaluate("" + _xpathPrefix + "finalize", invokeElem).asNodeSet(); @@ -371,7 +360,7 @@ void InterpreterImpl::normalize(Arabica::DOM::Element& scxml) { Arabica::DOM::Element finalElem = Arabica::DOM::Element(finals[i]); finalElem.setAttribute("isFirstEntry", "true"); if (!finalElem.hasAttribute("id")) { - finalElem.setAttribute("id", getUUID()); + finalElem.setAttribute("id", UUID::getUUID()); } } @@ -379,12 +368,12 @@ void InterpreterImpl::normalize(Arabica::DOM::Element& scxml) { for (int i = 0; i < histories.size(); i++) { Arabica::DOM::Element historyElem = Arabica::DOM::Element(histories[i]); if (!historyElem.hasAttribute("id")) { - historyElem.setAttribute("id", getUUID()); + historyElem.setAttribute("id", UUID::getUUID()); } } if (!scxml.hasAttribute("id")) { - scxml.setAttribute("id", getUUID()); + scxml.setAttribute("id", UUID::getUUID()); } // create a pseudo initial and transition element @@ -570,7 +559,7 @@ void InterpreterImpl::processDOMorText(const Arabica::DOM::Node& el } } -void InterpreterImpl::processParamChilds(const Arabica::DOM::Node& element, std::multimap& params) { +void InterpreterImpl::processParamChilds(const Arabica::DOM::Node& element, std::multimap& params) { NodeSet paramElems = filterChildElements(_xmlNSPrefix + "param", element); try { for (int i = 0; i < paramElems.size(); i++) { @@ -578,11 +567,11 @@ void InterpreterImpl::processParamChilds(const Arabica::DOM::Node& LOG(ERROR) << "param element is missing name attribute"; continue; } - std::string paramValue; + Data paramValue; if (HAS_ATTR(paramElems[i], "expr") && _dataModel) { - paramValue = _dataModel.evalAsString(ATTR(paramElems[i], "expr")); + paramValue = _dataModel.getStringAsData(ATTR(paramElems[i], "expr")); } else if(HAS_ATTR(paramElems[i], "location") && _dataModel) { - paramValue = _dataModel.evalAsString(ATTR(paramElems[i], "location")); + paramValue = _dataModel.getStringAsData(ATTR(paramElems[i], "location")); } else { LOG(ERROR) << "param element is missing expr or location or no datamodel is specified"; continue; @@ -593,7 +582,7 @@ void InterpreterImpl::processParamChilds(const Arabica::DOM::Node& } catch(Event e) { LOG(ERROR) << "Syntax error while processing params:" << std::endl << e << std::endl; // test 343 - std::multimap::iterator paramIter = params.begin(); + std::multimap::iterator paramIter = params.begin(); while(paramIter != params.end()) { params.erase(paramIter++); } @@ -663,7 +652,7 @@ void InterpreterImpl::send(const Arabica::DOM::Node& element) { * See 3.14 IDs for details. * */ - sendReq.sendid = ATTR(getParentState(element), "id") + "." + getUUID(); + sendReq.sendid = ATTR(getParentState(element), "id") + "." + UUID::getUUID(); if (HAS_ATTR(element, "idlocation") && _dataModel) { _dataModel.assign(ATTR(element, "idlocation"), "'" + sendReq.sendid + "'"); } else { @@ -708,9 +697,9 @@ void InterpreterImpl::send(const Arabica::DOM::Node& element) { if (_dataModel) { std::vector names = tokenizeIdRefs(ATTR(element, "namelist")); for (int i = 0; i < names.size(); i++) { - std::string namelistValue = _dataModel.evalAsString(names[i]); + Data namelistValue = _dataModel.getStringAsData(names[i]); sendReq.namelist[names[i]] = namelistValue; - sendReq.data.compound[names[i]] = Data(namelistValue, Data::VERBATIM); + sendReq.data.compound[names[i]] = namelistValue; } } else { LOG(ERROR) << "Namelist attribute at send requires datamodel to be defined"; @@ -834,7 +823,7 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node& element) { if (HAS_ATTR(element, "id")) { invokeReq.invokeid = ATTR(element, "id"); } else { - invokeReq.invokeid = ATTR(getParentState(element), "id") + "." + getUUID(); + invokeReq.invokeid = ATTR(getParentState(element), "id") + "." + UUID::getUUID(); if (HAS_ATTR(element, "idlocation") && _dataModel) { _dataModel.assign(ATTR(element, "idlocation"), "'" + invokeReq.invokeid + "'"); } @@ -954,7 +943,7 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node& elemen } _invokers.erase(invokeId); } else { - LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no soch invokation"; + LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no such invokation"; } //receiveInternal(Event("done.invoke." + invokeId, Event::PLATFORM)); } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index d2c9025..aadef72 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -4,7 +4,6 @@ #include "uscxml/Common.h" #include "uscxml/URL.h" -#include #include #include @@ -238,7 +237,6 @@ public: static Arabica::XPath::NodeSet filterChildElements(const std::string& tagName, const Arabica::XPath::NodeSet& nodeSet); Arabica::DOM::Node findLCCA(const Arabica::XPath::NodeSet& states); Arabica::XPath::NodeSet getProperAncestors(const Arabica::DOM::Node& s1, const Arabica::DOM::Node& s2); - static std::string getUUID(); protected: InterpreterImpl(); @@ -296,7 +294,7 @@ protected: std::string& text, std::string& expr); void processParamChilds(const Arabica::DOM::Node& element, - std::multimap& params); + std::multimap& params); void processDOMorText(const Arabica::DOM::Node& element, Arabica::DOM::Node& dom, std::string& text); @@ -313,8 +311,6 @@ protected: bool isInFinalState(const Arabica::DOM::Node& state); bool parentIsScxmlState(const Arabica::DOM::Node& state); - static boost::uuids::random_generator uuidGen; - long _lastRunOnMainThread; std::string _name; std::string _sessionId; @@ -598,9 +594,6 @@ public: Arabica::XPath::NodeSet getProperAncestors(const Arabica::DOM::Node& s1, const Arabica::DOM::Node& s2) { return _impl->getProperAncestors(s1, s2); } - static std::string getUUID() { - return InterpreterImpl::getUUID(); - } boost::shared_ptr getImpl() { return _impl; diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index 6388f9f..ef542d7 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -193,22 +193,24 @@ Arabica::DOM::Document SendRequest::toDocument() { Arabica::DOM::Node payloadElem = scxmlMsg.getElementsByTagName("scxml:payload").item(0); // add parameters - std::multimap::iterator paramIter = params.begin(); + std::multimap::iterator paramIter = params.begin(); while(paramIter != params.end()) { Arabica::DOM::Element propertyElem = document.createElementNS("http://www.w3.org/2005/07/scxml", "scxml:property"); propertyElem.setAttribute("name", paramIter->first); - Arabica::DOM::Text textElem = document.createTextNode(paramIter->second); + // this is simplified - Data might be more elaborate than a simple string atom + Arabica::DOM::Text textElem = document.createTextNode(paramIter->second.atom); propertyElem.appendChild(textElem); payloadElem.appendChild(propertyElem); paramIter++; } // add namelist elements - std::map::iterator namelistIter = namelist.begin(); + std::map::iterator namelistIter = namelist.begin(); while(namelistIter != namelist.end()) { Arabica::DOM::Element propertyElem = document.createElementNS("http://www.w3.org/2005/07/scxml", "scxml:property"); propertyElem.setAttribute("name", namelistIter->first); - Arabica::DOM::Text textElem = document.createTextNode(namelistIter->second); + // this is simplified - Data might be more elaborate than a simple string atom + Arabica::DOM::Text textElem = document.createTextNode(namelistIter->second.atom); propertyElem.appendChild(textElem); payloadElem.appendChild(propertyElem); namelistIter++; @@ -294,14 +296,21 @@ Data Data::fromJSON(const std::string& jsonString) { if (t[0].end != trimmed.length()) return data; +// jsmntok_t* token = t; +// while(token->end) { +// std::cout << trimmed.substr(token->start, token->end - token->start) << std::endl; +// std::cout << "------" << std::endl; +// token++; +// } + std::list dataStack; std::list tokenStack; dataStack.push_back(&data); size_t currTok = 0; do { - jsmntok_t t2 = t[currTok]; - std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start); +// jsmntok_t t2 = t[currTok]; +// std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start); switch (t[currTok].type) { case JSMN_STRING: dataStack.back()->type = Data::VERBATIM; @@ -317,15 +326,15 @@ Data Data::fromJSON(const std::string& jsonString) { break; } - t2 = t[currTok]; - value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start); +// t2 = t[currTok]; +// value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start); // there are no more tokens if (t[currTok].end == 0 || tokenStack.empty()) break; // next token starts after current one => pop - if (t[currTok].end > tokenStack.back().end) { + while (t[currTok].end > tokenStack.back().end) { tokenStack.pop_back(); dataStack.pop_back(); } diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index e561c59..9ae6ea1 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -11,6 +11,7 @@ #include #include +#include #include #define TAGNAME(elem) ((Arabica::DOM::Element)elem).getTagName() @@ -43,7 +44,7 @@ public: virtual ~Data() {} operator bool() const { - return (atom.length() > 0 || !compound.empty() || !array.empty()); + return (atom.length() > 0 || !compound.empty() || !array.empty() || binary); } bool hasKey(const std::string& key) const { @@ -60,11 +61,11 @@ public: Data data; return data; } - - operator std::string() { + + operator std::string() const { return atom; } - + operator std::map() { return compound; } @@ -72,7 +73,7 @@ public: operator std::list() { return array; } - + static Data fromJSON(const std::string& jsonString); static std::string toJSON(const Data& data); static Data fromXML(const std::string& xmlString); @@ -235,13 +236,57 @@ public: return ss.str(); } - std::map& getNameList() { + std::map& getNameList() { return namelist; } - std::multimap& getParams() { + std::multimap& getParams() { return params; } + typedef std::multimap params_t; + typedef std::map namelist_t; + + static bool getParam(params_t params, const std::string& name, Data& target) { + if (params.find(name) != params.end()) { + target = params.find(name)->second; + return true; + } + return false; + } + + static bool getParam(params_t params, const std::string& name, std::list& target) { + if (params.find(name) != params.end()) { + std::pair rangeIter = params.equal_range(name); + while(rangeIter.first != rangeIter.second) { + target.push_back(rangeIter.first->second); + rangeIter.first++; + } + return true; + } + return false; + } + + template static bool getParam(params_t params, const std::string& name, T& target) { + if (params.find(name) != params.end()) { + target = boost::lexical_cast(params.find(name)->second.atom); + return true; + } + return false; + } + + template static bool getParam(params_t params, const std::string& name, std::list& target) { + if (params.find(name) != params.end()) { + std::pair rangeIter = params.equal_range(name); + while(rangeIter.first != rangeIter.second) { + target.push_back(boost::lexical_cast(rangeIter.first->second.atom)); + rangeIter.first++; + } + return true; + } + return false; + } + + #ifdef SWIGIMPORTED protected: #endif @@ -258,11 +303,8 @@ protected: std::string invokeid; Data data; std::string content; - std::map namelist; - std::multimap params; - - typedef std::multimap params_t; - typedef std::map namelist_t; + std::map namelist; + std::multimap params; friend std::ostream& operator<< (std::ostream& os, const Event& event); }; diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 24a64ac..1b15dee 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -1,5 +1,6 @@ #include #include "URL.h" +#include "UUID.h" #include "uscxml/config.h" #include @@ -42,6 +43,7 @@ std::string URL::tmpDir() { if (tmpDir == NULL) tmpDir = "/tmp/"; +#if 0 char* tmpl = (char*)malloc(strlen(tmpDir) + 11); char* writePtr = tmpl; memcpy(writePtr, tmpDir, strlen(tmpDir)); @@ -50,6 +52,22 @@ std::string URL::tmpDir() { writePtr += 11; tmpl[writePtr - tmpl] = 0; return tmpl; +#endif + return tmpDir; +} + + +std::string URL::getTmpFilename(const std::string& suffix) { + std::string tmpFilename = tmpDir(); + if (tmpFilename.find_last_of(PATH_SEPERATOR) != tmpFilename.length() - 1) + tmpFilename += PATH_SEPERATOR; + + tmpFilename += UUID::getUUID(); + if (suffix.length() > 0) { + tmpFilename += "."; + tmpFilename += suffix; + } + return tmpFilename; } #if (!defined APPLE && !defined IOS) diff --git a/src/uscxml/URL.h b/src/uscxml/URL.h index 16c29c7..c00d24a 100644 --- a/src/uscxml/URL.h +++ b/src/uscxml/URL.h @@ -205,6 +205,7 @@ public: static void toBaseURL(URL& uri); static std::string getResourceDir(); + static std::string getTmpFilename(const std::string& suffix = ""); static URL toLocalFile(const std::string& content, const std::string& suffix) { boost::shared_ptr impl = URLImpl::toLocalFile(content, suffix); diff --git a/src/uscxml/URL.mm b/src/uscxml/URL.mm index 3344601..e6dc9c5 100644 --- a/src/uscxml/URL.mm +++ b/src/uscxml/URL.mm @@ -10,19 +10,21 @@ namespace uscxml { std::string URL::getResourceDir() { + std::string path; #if HAS_AUTORELEASE_POOL @autoreleasepool { #else NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #endif NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - return std::string([resourcePath cStringUsingEncoding:NSUTF8StringEncoding]); + path = [resourcePath cStringUsingEncoding:NSUTF8StringEncoding]; #if HAS_AUTORELEASE_POOL } #else [pool drain]; #endif + return path; } } diff --git a/src/uscxml/UUID.cpp b/src/uscxml/UUID.cpp new file mode 100644 index 0000000..eac4316 --- /dev/null +++ b/src/uscxml/UUID.cpp @@ -0,0 +1,14 @@ +#include "UUID.h" +#include + +namespace uscxml { +boost::uuids::random_generator UUID::uuidGen; + +std::string UUID::getUUID() { + boost::uuids::uuid uuid = uuidGen(); + std::ostringstream os; + os << uuid; + return os.str(); +} + +} \ No newline at end of file diff --git a/src/uscxml/UUID.h b/src/uscxml/UUID.h new file mode 100644 index 0000000..4a11285 --- /dev/null +++ b/src/uscxml/UUID.h @@ -0,0 +1,20 @@ +#ifndef UUID_H_8X65R2EI +#define UUID_H_8X65R2EI + +#include +#include +#include +#include + +namespace uscxml { + +class UUID { +public: + static std::string getUUID(); + static boost::uuids::random_generator uuidGen; +}; + +} + + +#endif /* end of include guard: UUID_H_8X65R2EI */ diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index e044fcf..62c61e0 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -1,6 +1,7 @@ #include "InterpreterDraft6.h" #include +#include "uscxml/UUID.h" namespace uscxml { @@ -80,7 +81,7 @@ void InterpreterDraft6::interpret() { if (_userDefinedStartConfiguration.size() > 0) { // we emulate entering a given configuration by creating a pseudo deep history Element initHistory = _document.createElementNS(_nsURL, "history"); - initHistory.setAttribute("id", getUUID()); + initHistory.setAttribute("id", UUID::getUUID()); initHistory.setAttribute("type", "deep"); _scxml.insertBefore(initHistory, _scxml.getFirstChild()); diff --git a/src/uscxml/interpreter/InterpreterDraft7.cpp b/src/uscxml/interpreter/InterpreterDraft7.cpp index 7f16b1f..fa578b3 100644 --- a/src/uscxml/interpreter/InterpreterDraft7.cpp +++ b/src/uscxml/interpreter/InterpreterDraft7.cpp @@ -1,6 +1,7 @@ #include "InterpreterDraft7.h" #include +#include "uscxml/UUID.h" namespace uscxml { @@ -31,7 +32,7 @@ void InterpreterDraft7::interpret() { if (!_scxml) return; - _sessionId = getUUID(); + _sessionId = UUID::getUUID(); std::string datamodelName; if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp index 142f3ec..b2f66b4 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp @@ -223,14 +223,14 @@ void JSCDataModel::setEvent(const Event& event) { if (!eventCopy.params.empty()) { Event::params_t::iterator paramIter = eventCopy.params.begin(); while(paramIter != eventCopy.params.end()) { - eventCopy.data.compound[paramIter->first] = Data(paramIter->second, Data::VERBATIM); + eventCopy.data.compound[paramIter->first] = paramIter->second; paramIter++; } } if (!eventCopy.namelist.empty()) { Event::namelist_t::iterator nameListIter = eventCopy.namelist.begin(); while(nameListIter != eventCopy.namelist.end()) { - eventCopy.data.compound[nameListIter->first] = Data(nameListIter->second, Data::VERBATIM); + eventCopy.data.compound[nameListIter->first] = nameListIter->second; nameListIter++; } } diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index 77efe78..0e72c67 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -207,18 +207,19 @@ void V8DataModel::setEvent(const Event& event) { if (!eventCopy.params.empty()) { Event::params_t::iterator paramIter = eventCopy.params.begin(); while(paramIter != eventCopy.params.end()) { - eventCopy.data.compound[paramIter->first] = Data(paramIter->second, Data::VERBATIM); + eventCopy.data.compound[paramIter->first] = paramIter->second; paramIter++; } } if (!eventCopy.namelist.empty()) { Event::namelist_t::iterator nameListIter = eventCopy.namelist.begin(); while(nameListIter != eventCopy.namelist.end()) { - eventCopy.data.compound[nameListIter->first] = Data(nameListIter->second, Data::VERBATIM); + eventCopy.data.compound[nameListIter->first] = nameListIter->second; nameListIter++; } } if (eventCopy.data) { +// std::cout << Data::toJSON(eventCopy.data); eventObj->Set(v8::String::New("data"), getDataAsValue(eventCopy.data)); // set data part of _event } else { // test 343 / test 488 @@ -340,6 +341,7 @@ v8::Handle V8DataModel::getDataAsValue(const Data& data) { v8::Handle value = v8::Object::New(); std::map::const_iterator compoundIter = data.compound.begin(); while(compoundIter != data.compound.end()) { +// std::cout << compoundIter->first.c_str() << std::endl; value->Set(v8::String::New(compoundIter->first.c_str()), getDataAsValue(compoundIter->second)); compoundIter++; } diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp index 07cba96..aec044e 100644 --- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp +++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp @@ -230,6 +230,8 @@ void SWIDataModel::setEvent(const Event& event) { PlCall(dataInitStr.str().c_str()); } else if (event.content.size() > 0) { PlCall("assert", PlCompound("event", PlCompound("data", PlString(Interpreter::spaceNormalize(event.content).c_str())))); + } else if (event.data) { + LOG(ERROR) << "No support for structured data from events in prolog datamodel yet"; } // event.params diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp index cb40890..f863bb7 100644 --- a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp +++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp @@ -139,10 +139,11 @@ void XPathDataModel::setEvent(const Event& event) { } if (event.params.size() > 0) { - std::multimap::const_iterator paramIter = event.params.begin(); + std::multimap::const_iterator paramIter = event.params.begin(); while(paramIter != event.params.end()) { Element eventParamElem = _doc.createElement("data"); - Text eventParamText = _doc.createTextNode(paramIter->second); + // this is simplified - Data might be more elaborate than a simple string atom + Text eventParamText = _doc.createTextNode(paramIter->second.atom); eventParamElem.setAttribute("id", paramIter->first); eventParamElem.appendChild(eventParamText); @@ -151,10 +152,11 @@ void XPathDataModel::setEvent(const Event& event) { } } if (event.namelist.size() > 0) { - std::map::const_iterator namelistIter = event.namelist.begin(); + std::map::const_iterator namelistIter = event.namelist.begin(); while(namelistIter != event.namelist.end()) { Element eventNamelistElem = _doc.createElement("data"); - Text eventNamelistText = _doc.createTextNode(namelistIter->second); + // this is simplified - Data might be more elaborate than a simple string atom + Text eventNamelistText = _doc.createTextNode(namelistIter->second.atom); eventNamelistElem.setAttribute("id", namelistIter->first); eventNamelistElem.appendChild(eventNamelistText); diff --git a/src/uscxml/plugins/element/file/FileElement.cpp b/src/uscxml/plugins/element/file/FileElement.cpp index e51fa70..2b48aba 100644 --- a/src/uscxml/plugins/element/file/FileElement.cpp +++ b/src/uscxml/plugins/element/file/FileElement.cpp @@ -34,7 +34,7 @@ void FileElement::enterElement(const Arabica::DOM::Node& node) { } _givenUrl = (HAS_ATTR(node, "url") ? ATTR(node, "url") : _interpreter->getDataModel().evalAsString(ATTR(node, "urlexpr"))); - std::string sandBoxStr = (HAS_ATTR(node, "sandboxed") ? ATTR(node, "sandboxed") : "on"); + std::string sandBoxStr = (HAS_ATTR(node, "sandbox") ? ATTR(node, "sandbox") : "on"); if (boost::iequals(sandBoxStr, "off") || boost::iequals(sandBoxStr, "false") || boost::iequals(sandBoxStr, "no")) { _sandBoxed = false; } diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp index 3c47b83..ae81fed 100644 --- a/src/uscxml/plugins/element/respond/RespondElement.cpp +++ b/src/uscxml/plugins/element/respond/RespondElement.cpp @@ -63,8 +63,14 @@ void RespondElement::enterElement(const Arabica::DOM::Node& node) { if (HAS_ATTR(contents[0], "expr")) { // -- content is evaluated string from datamodel ------ if (_interpreter->getDataModel()) { try { - std::string contentValue = _interpreter->getDataModel().evalAsString(ATTR(contents[0], "expr")); - httpReply.content = contentValue; + Data contentData = _interpreter->getDataModel().getStringAsData(ATTR(contents[0], "expr")); + if (contentData.atom.length() > 0) { + httpReply.content = contentData.atom; + } else if (contentData.binary) { + httpReply.content = std::string(contentData.binary->_data, contentData.binary->_size); + } else { + httpReply.content = Data::toJSON(contentData); + } } catch (Event e) { LOG(ERROR) << "Syntax error with expr in content child of Respond element:" << std::endl << e << std::endl; return; @@ -132,20 +138,20 @@ void RespondElement::enterElement(const Arabica::DOM::Node& node) { std::string value; if (HAS_ATTR(headers[i], "value")) { value = ATTR(headers[i], "value"); - } else if(HAS_ATTR(headers[i], "expr")) { + } else if(HAS_ATTR(headers[i], "valueexpr")) { if (_interpreter->getDataModel()) { try { - value = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "expr")); + value = _interpreter->getDataModel().evalAsString(ATTR(headers[i], "valueexpr")); } catch (Event e) { - LOG(ERROR) << "Syntax error with expr in header child of Respond element:" << std::endl << e << std::endl; + LOG(ERROR) << "Syntax error with valueexpr in header child of Respond element:" << std::endl << e << std::endl; return; } } else { - LOG(ERROR) << "header element has expr attribute but no datamodel is specified."; + LOG(ERROR) << "header element has valueexpr attribute but no datamodel is specified."; return; } } else { - LOG(ERROR) << "header element has no value or expr attribute."; + LOG(ERROR) << "header element has no value or valueexpr attribute."; return; } diff --git a/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp b/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp index be6ffc7..73f44ea 100644 --- a/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp +++ b/src/uscxml/plugins/invoker/audio/OpenALInvoker.cpp @@ -72,14 +72,14 @@ void OpenALInvoker::send(const SendRequest& req) { LOG(ERROR) << "Sent event play with no src URL"; } - URL srcURL = req.params.find("src")->second; + URL srcURL = req.params.find("src")->second.atom; if (!srcURL.toAbsolute(_interpreter->getBaseURI())) { LOG(ERROR) << "src URL " << req.params.find("src")->second << " is relative with no base URI set for interpreter"; return; } _sources[req.sendid] = new OpenALSource(); - _sources[req.sendid]->loop = req.params.find("loop") != req.params.end() && boost::iequals(req.params.find("loop")->second, "true"); + _sources[req.sendid]->loop = req.params.find("loop") != req.params.end() && boost::iequals(req.params.find("loop")->second.atom, "true"); _sources[req.sendid]->file = srcURL; #ifdef LIBSNDFILE_FOUND _sources[req.sendid]->transform = new LibSoundFile(srcURL.asLocalFile(".audio")); @@ -127,7 +127,7 @@ void OpenALInvoker::send(const SendRequest& req) { LOG(WARNING) << "Cannot move source with no source given in parameters"; return; } - sourceId = req.params.find("source")->second; + sourceId = req.params.find("source")->second.atom; if (_sources.find(sourceId) == _sources.end()) { LOG(WARNING) << "Given source '" << sourceId << "' not active or not existing"; @@ -261,14 +261,14 @@ void OpenALInvoker::invoke(const InvokeRequest& req) { throw std::string("__FILE__ __LINE__ openal error opening device"); } - std::multimap::const_iterator paramIter = req.params.begin(); + std::multimap::const_iterator paramIter = req.params.begin(); while(paramIter != req.params.end()) { if (boost::iequals(paramIter->first, "maxX")) - _maxPos[0] = strTo(paramIter->second); + _maxPos[0] = strTo(paramIter->second.atom); if (boost::iequals(paramIter->first, "maxY")) - _maxPos[1] = strTo(paramIter->second); + _maxPos[1] = strTo(paramIter->second.atom); if (boost::iequals(paramIter->first, "maxZ")) - _maxPos[2] = strTo(paramIter->second); + _maxPos[2] = strTo(paramIter->second.atom); paramIter++; } @@ -310,7 +310,7 @@ void OpenALInvoker::notifyOfLoop(OpenALSource* src) { returnEvent(ev); } -void OpenALInvoker::getPosFromParams(const std::multimap& params, float* position) { +void OpenALInvoker::getPosFromParams(const std::multimap& params, float* position) { // vector explicitly given try { if (params.find("x") != params.end()) diff --git a/src/uscxml/plugins/invoker/audio/OpenALInvoker.h b/src/uscxml/plugins/invoker/audio/OpenALInvoker.h index 9b71d95..81ced9b 100644 --- a/src/uscxml/plugins/invoker/audio/OpenALInvoker.h +++ b/src/uscxml/plugins/invoker/audio/OpenALInvoker.h @@ -78,7 +78,7 @@ protected: void notifyOfLoop(OpenALSource*); float posToRadian(const std::string& pos); - void getPosFromParams(const std::multimap& params, float* position); + void getPosFromParams(const std::multimap& params, float* position); }; diff --git a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp index ccf65ce..e637f8b 100644 --- a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp +++ b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp @@ -1,15 +1,16 @@ #include "FFMPEGInvoker.h" #include +#include +#include +#include #ifdef BUILD_AS_PLUGINS #include #endif -#define STREAM_DURATION 200.0 #define STREAM_FRAME_RATE 25 /* 25 images/s */ -#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) -#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ +#define BMP_FORMAT PIX_FMT_BGR24 namespace uscxml { @@ -29,190 +30,363 @@ FFMPEGInvoker::~FFMPEGInvoker() { boost::shared_ptr FFMPEGInvoker::create(InterpreterImpl* interpreter) { boost::shared_ptr invoker = boost::shared_ptr(new FFMPEGInvoker()); - invoker->_interpreter = interpreter; + // Register all formats and codecs - this ought to be done just once + av_register_all(); return invoker; } Data FFMPEGInvoker::getDataModelVariables() { Data data; + + AVCodec* codec = NULL; + while((codec = av_codec_next(codec))) { + AVCodec* codecInst = avcodec_find_encoder(codec->id); + if (!codecInst) + continue; + + switch (codec->type) { + case AVMEDIA_TYPE_VIDEO: { + Data codecData; + codecData.compound["name"] = Data(codec->name, Data::VERBATIM); + codecData.compound["longName"] = Data(codec->long_name, Data::VERBATIM); + data.compound["video"].compound[codec->name] = codecData; + break; + } + case AVMEDIA_TYPE_AUDIO: { + Data codecData; + codecData.compound["name"] = Data(codec->name, Data::VERBATIM); + codecData.compound["longName"] = Data(codec->long_name, Data::VERBATIM); + data.compound["audio"].compound[codec->name] = codecData; + break; + } + default: + break; + } + } + return data; } +void FFMPEGInvoker::invoke(const InvokeRequest& req) { + int nrThreads = 1; + Event::getParam(req.params, "threads", nrThreads); + + _isRunning = true; + for (int i = 0; i < nrThreads; i++) { + _threads.insert(new tthread::thread(FFMPEGInvoker::run, this)); + } +} + void FFMPEGInvoker::send(const SendRequest& req) { - if (boost::iequals(req.name, "add")) { + SendRequest reqCopy = req; + + if (boost::iequals(req.name, "render.start")) { + // create a new encoding context + int ret; + EncodingContext* ctx = new EncodingContext(); + tthread::lock_guard lock(ctx->mutex); + + std::string context; + Event::getParam(req.params, "context", context); + + ctx->extension = "mpeg"; + Event::getParam(req.params, "format", ctx->extension); + + Event::getParam(req.params, "width", ctx->width); + Event::getParam(req.params, "height", ctx->height); + + if (!ctx->width || !ctx->height) + return; + + ctx->filename = URL::getTmpFilename(); + + /* allocate the output media context */ + avformat_alloc_output_context2(&ctx->formatCtx, NULL, ctx->extension.c_str(), ctx->filename.c_str()); + if (!ctx->formatCtx) { + printf("Could not deduce output format from file extension: using MPEG.\n"); + avformat_alloc_output_context2(&ctx->formatCtx, NULL, "mpeg", ctx->filename.c_str()); + } + if (!ctx->formatCtx) { + return; + } + ctx->format = ctx->formatCtx->oformat; + + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + ctx->videoStream = NULL; + + if (ctx->format->video_codec != AV_CODEC_ID_NONE) { + ctx->videoStream = addStream(ctx, ctx->formatCtx, &ctx->videoCodec, ctx->format->video_codec); + } + + /* Now that all the parameters are set, we can open the audio and + * video codecs and allocate the necessary encode buffers. */ + if (ctx->videoStream) + openVideo(ctx, ctx->formatCtx, ctx->videoCodec, ctx->videoStream); + + /* open the output file, if needed */ + if (!(ctx->format->flags & AVFMT_NOFILE)) { + ret = avio_open(&ctx->formatCtx->pb, ctx->filename.c_str(), AVIO_FLAG_WRITE); + if (ret < 0) { + // fprintf(stderr, "Could not open '%s': %s\n", ctx->filename.c_str(), + // av_err2str(ret)); + return; + } + } + + /* Write the stream header, if any. */ + ret = avformat_write_header(ctx->formatCtx, NULL); + if (ret < 0) { + // fprintf(stderr, "Error occurred when opening output file: %s\n", + // av_err2str(ret)); + return; + } + + if (ctx->frame) + ctx->frame->pts = 0; + + _encoders[context] = ctx; + } else if(boost::iequals(req.name, "render.frame")) { + _workQueue.push(req); + } else if(boost::iequals(req.name, "render.end")) { + _workQueue.push(req); + } +} - } else if(boost::iequals(req.name, "render")) { +void FFMPEGInvoker::cancel(const std::string sendId) { +} +void FFMPEGInvoker::run(void* instance) { + FFMPEGInvoker* INSTANCE = (FFMPEGInvoker*)instance; + while(true) { + SendRequest req = INSTANCE->_workQueue.pop(); + if (INSTANCE->_isRunning) { + INSTANCE->process(req); + } else { + return; + } } } -void FFMPEGInvoker::cancel(const std::string sendId) { +void FFMPEGInvoker::finish(EncodingContext* ctx, const SendRequest& req) { + av_write_trailer(ctx->formatCtx); + + /* Close each codec. */ + if (ctx->videoStream) + closeVideo(ctx, ctx->formatCtx, ctx->videoStream); + + if (!(ctx->formatCtx->oformat->flags & AVFMT_NOFILE)) + /* Close the output file. */ + avio_close(ctx->formatCtx->pb); + + /* free the stream */ + avformat_free_context(ctx->formatCtx); + + // read file + std::ifstream movieFile(ctx->filename.c_str()); + movieFile.seekg(0, std::ios::end); + size_t length = movieFile.tellg(); + movieFile.seekg(0, std::ios::beg); + + char* movieBuffer = (char*)malloc(length); + movieFile.read(movieBuffer, length); + + // move to desktop for checking +// int err = rename(ctx->filename.c_str(), "/Users/sradomski/Desktop/foo.mpg"); +// if (err) { +// printf("%s", strerror(errno)); +// } + + std::string context; + Event::getParam(req.params, "context", context); + + Event event; + event.name = "render.done"; + event.data.compound["context"] = context; + event.data.compound["movie"] = Data(movieBuffer, length, true); + event.data.compound["mimetype"] = Data("video/mpeg", Data::VERBATIM); + event.data.compound["filename"] = Data(std::string("movie.") + ctx->extension, Data::VERBATIM); + + returnEvent(event); } -static AVFrame *frame; -static AVPicture src_picture, dst_picture; -static int frame_count; -static int sws_flags = SWS_BICUBIC; +void FFMPEGInvoker::process(const SendRequest& req) { -static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, - enum AVCodecID codec_id) { + std::string context; + Event::getParam(req.params, "context", context); + if (_encoders.find(context) == _encoders.end()) { + return; + } + + EncodingContext* ctx = _encoders[context]; + tthread::lock_guard lock(ctx->mutex); + + // finish encoding and return + if(boost::iequals(req.name, "render.end")) { + finish(ctx, req); + delete _encoders[context]; + _encoders.erase(context); + } + + Data image; + Event::getParam(req.params, "frame", image); + if (!image) { + return; + } + + std::string format = "bmp"; + Event::getParam(req.params, "format", format); + + writeVideoFrame(ctx, ctx->formatCtx, ctx->videoStream, image.binary); + ctx->frame->pts += av_rescale_q(1, ctx->videoStream->codec->time_base, ctx->videoStream->time_base); + +} + +AVStream* FFMPEGInvoker::addStream(EncodingContext* ctx, AVFormatContext *oc, AVCodec **codec, + enum AVCodecID codec_id) { AVCodecContext *c; AVStream *st; - + /* find the encoder */ *codec = avcodec_find_encoder(codec_id); if (!(*codec)) { fprintf(stderr, "Could not find encoder for '%s'\n", - avcodec_get_name(codec_id)); - exit(1); + avcodec_get_name(codec_id)); + return NULL; } - + st = avformat_new_stream(oc, *codec); + ctx->videoPixFmt = (*codec)->pix_fmts[0]; if (!st) { fprintf(stderr, "Could not allocate stream\n"); - exit(1); + return NULL; } st->id = oc->nb_streams-1; c = st->codec; - + switch ((*codec)->type) { - case AVMEDIA_TYPE_AUDIO: - c->sample_fmt = AV_SAMPLE_FMT_FLTP; - c->bit_rate = 64000; - c->sample_rate = 44100; - c->channels = 2; - break; - - case AVMEDIA_TYPE_VIDEO: - c->codec_id = codec_id; - - c->bit_rate = 400000; - /* Resolution must be a multiple of two. */ - c->width = 352; - c->height = 288; - /* timebase: This is the fundamental unit of time (in seconds) in terms - * of which frame timestamps are represented. For fixed-fps content, - * timebase should be 1/framerate and timestamp increments should be - * identical to 1. */ - c->time_base.den = STREAM_FRAME_RATE; - c->time_base.num = 1; - c->gop_size = 12; /* emit one intra frame every twelve frames at most */ - c->pix_fmt = STREAM_PIX_FMT; - if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - c->max_b_frames = 2; - } - if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { - /* Needed to avoid using macroblocks in which some coeffs overflow. - * This does not happen with normal video, it just happens here as - * the motion of the chroma plane does not match the luma plane. */ - c->mb_decision = 2; - } - break; - - default: - break; + case AVMEDIA_TYPE_AUDIO: + c->sample_fmt = AV_SAMPLE_FMT_FLTP; + c->bit_rate = 64000; + c->sample_rate = 44100; + c->channels = 2; + break; + + case AVMEDIA_TYPE_VIDEO: + c->codec_id = codec_id; + + c->bit_rate = 800000; + /* Resolution must be a multiple of two. */ + c->width = ctx->width; + c->height = ctx->height; + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + c->time_base.den = STREAM_FRAME_RATE; + c->time_base.num = 1; + c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = ctx->videoPixFmt; + if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + c->max_b_frames = 2; + } + if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + c->mb_decision = 2; + } + break; + + default: + break; } - + /* Some formats want stream headers to be separate. */ if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; - + return st; } -static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st) { +void FFMPEGInvoker::openVideo(EncodingContext* ctx, AVFormatContext *oc, AVCodec *codec, AVStream *st) { int ret; AVCodecContext *c = st->codec; /* open the codec */ ret = avcodec_open2(c, codec, NULL); if (ret < 0) { - fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret)); - exit(1); + // fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret)); + return; } /* allocate and init a re-usable frame */ - frame = avcodec_alloc_frame(); - if (!frame) { + ctx->frame = avcodec_alloc_frame(); + if (!ctx->frame) { fprintf(stderr, "Could not allocate video frame\n"); - exit(1); + return; } /* Allocate the encoded raw picture. */ - ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height); + ret = avpicture_alloc(&ctx->dst_picture, c->pix_fmt, c->width, c->height); if (ret < 0) { - fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret)); - exit(1); +// fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret)); + return; } /* If the output format is not YUV420P, then a temporary YUV420P * picture is needed too. It is then converted to the required * output format. */ - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width, c->height); + if (c->pix_fmt != BMP_FORMAT) { + ret = avpicture_alloc(&ctx->src_picture, BMP_FORMAT, c->width, c->height); if (ret < 0) { - fprintf(stderr, "Could not allocate temporary picture: %s\n", - av_err2str(ret)); - exit(1); + // fprintf(stderr, "Could not allocate temporary picture: %s\n", + // av_err2str(ret)); + return; } } /* copy data and linesize picture pointers to frame */ - *((AVPicture *)frame) = dst_picture; + *((AVPicture *)ctx->frame) = ctx->dst_picture; } - -/* Prepare a dummy image. */ -static void fill_yuv_image(AVPicture *pict, int frame_index, - int width, int height) { - int x, y, i; - - i = frame_index; - - /* Y */ - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; - - /* Cb and Cr */ - for (y = 0; y < height / 2; y++) { - for (x = 0; x < width / 2; x++) { - pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; - pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; - } - } -} - -static void write_video_frame(AVFormatContext *oc, AVStream *st) { + +void FFMPEGInvoker::writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, AVStream *st, boost::shared_ptr image) { int ret; - static struct SwsContext *sws_ctx; AVCodecContext *c = st->codec; - if (frame_count >= STREAM_NB_FRAMES) { - /* No more frames to compress. The codec has a latency of a few - * frames if using B-frames, so we get the last frames by - * passing the same picture again. */ - } else { - if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - /* as we only generate a YUV420P picture, we must convert it - * to the codec pixel format if needed */ - if (!sws_ctx) { - sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P, - c->width, c->height, c->pix_fmt, - sws_flags, NULL, NULL, NULL); - if (!sws_ctx) { - fprintf(stderr, - "Could not initialize the conversion context\n"); - exit(1); - } + if (c->pix_fmt != BMP_FORMAT) { + /* as we only generate a YUV420P picture, we must convert it + * to the codec pixel format if needed */ + if (!ctx->sws_ctx) { + ctx->sws_ctx = sws_getContext(c->width, c->height, BMP_FORMAT, + c->width, c->height, c->pix_fmt, + ctx->sws_flags, NULL, NULL, NULL); + if (!ctx->sws_ctx) { + fprintf(stderr, + "Could not initialize the conversion context\n"); + return; } - fill_yuv_image(&src_picture, frame_count, c->width, c->height); - sws_scale(sws_ctx, - (const uint8_t * const *)src_picture.data, src_picture.linesize, - 0, c->height, dst_picture.data, dst_picture.linesize); - } else { - fill_yuv_image(&dst_picture, frame_count, c->width, c->height); } + + uint32_t headerOffset = 0; + headerOffset += image->_data[10] << 0; + headerOffset += image->_data[11] << 8; + headerOffset += image->_data[12] << 16; + headerOffset += image->_data[13] << 24; + +// std::cout << headerOffset + (c->width * c->height) << " / " << image->_size << std::endl; + + ret = avpicture_fill(&ctx->src_picture, (uint8_t*)(image->_data + headerOffset), BMP_FORMAT, c->width, c->height); + if (ret < 0) { + fprintf(stderr, + "Could not fill image from given bitmap\n"); + } + sws_scale(ctx->sws_ctx, + (const uint8_t * const *)ctx->src_picture.data, ctx->src_picture.linesize, + 0, c->height, ctx->dst_picture.data, ctx->dst_picture.linesize); + } else { + avpicture_fill(&ctx->dst_picture, (uint8_t*)image->_data, c->pix_fmt, c->width, c->height); } if (oc->oformat->flags & AVFMT_RAWPICTURE) { @@ -222,7 +396,7 @@ static void write_video_frame(AVFormatContext *oc, AVStream *st) { pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = st->index; - pkt.data = dst_picture.data[0]; + pkt.data = ctx->dst_picture.data[0]; pkt.size = sizeof(AVPicture); ret = av_interleaved_write_frame(oc, &pkt); @@ -232,10 +406,10 @@ static void write_video_frame(AVFormatContext *oc, AVStream *st) { av_init_packet(&pkt); /* encode the image */ - ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); + ret = avcodec_encode_video2(c, &pkt, ctx->frame, &got_packet); if (ret < 0) { - fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); - exit(1); + // fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); + return; } /* If size is zero, it means the image was buffered. */ @@ -243,297 +417,25 @@ static void write_video_frame(AVFormatContext *oc, AVStream *st) { pkt.stream_index = st->index; /* Write the compressed frame to the media file. */ +// ret = av_write_frame(oc, &pkt); ret = av_interleaved_write_frame(oc, &pkt); } else { ret = 0; } } if (ret != 0) { - fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret)); - exit(1); - } - frame_count++; -} - -static void close_video(AVFormatContext *oc, AVStream *st) { - avcodec_close(st->codec); - av_free(src_picture.data[0]); - av_free(dst_picture.data[0]); - av_free(frame); -} - -static float t, tincr, tincr2; - -static uint8_t **src_samples_data; -static int src_samples_linesize; -static int src_nb_samples; - -static int max_dst_nb_samples; -uint8_t **dst_samples_data; -int dst_samples_linesize; -int dst_samples_size; - -struct SwrContext *swr_ctx = NULL; - -static void open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st) { - AVCodecContext *c; - int ret; - - c = st->codec; - - /* open it */ - ret = avcodec_open2(c, codec, NULL); - if (ret < 0) { - fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret)); - exit(1); - } - - /* init signal generator */ - t = 0; - tincr = 2 * M_PI * 110.0 / c->sample_rate; - /* increment frequency by 110 Hz per second */ - tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; - - src_nb_samples = c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ? - 10000 : c->frame_size; - - ret = av_samples_alloc_array_and_samples(&src_samples_data, &src_samples_linesize, c->channels, - src_nb_samples, c->sample_fmt, 0); - if (ret < 0) { - fprintf(stderr, "Could not allocate source samples\n"); - exit(1); - } - - /* create resampler context */ - if (c->sample_fmt != AV_SAMPLE_FMT_S16) { - swr_ctx = swr_alloc(); - if (!swr_ctx) { - fprintf(stderr, "Could not allocate resampler context\n"); - exit(1); - } - - /* set options */ - av_opt_set_int (swr_ctx, "in_channel_count", c->channels, 0); - av_opt_set_int (swr_ctx, "in_sample_rate", c->sample_rate, 0); - av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); - av_opt_set_int (swr_ctx, "out_channel_count", c->channels, 0); - av_opt_set_int (swr_ctx, "out_sample_rate", c->sample_rate, 0); - av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", c->sample_fmt, 0); - - /* initialize the resampling context */ - if ((ret = swr_init(swr_ctx)) < 0) { - fprintf(stderr, "Failed to initialize the resampling context\n"); - exit(1); - } - } - - /* compute the number of converted samples: buffering is avoided - * ensuring that the output buffer will contain at least all the - * converted input samples */ - max_dst_nb_samples = src_nb_samples; - ret = av_samples_alloc_array_and_samples(&dst_samples_data, &dst_samples_linesize, c->channels, - max_dst_nb_samples, c->sample_fmt, 0); - if (ret < 0) { - fprintf(stderr, "Could not allocate destination samples\n"); - exit(1); - } - dst_samples_size = av_samples_get_buffer_size(NULL, c->channels, max_dst_nb_samples, - c->sample_fmt, 0); -} - -/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and - * 'nb_channels' channels. */ -static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) { - int j, i, v; - int16_t *q; - - q = samples; - for (j = 0; j < frame_size; j++) { - v = (int)(sin(t) * 10000); - for (i = 0; i < nb_channels; i++) - *q++ = v; - t += tincr; - tincr += tincr2; - } -} - -static void write_audio_frame(AVFormatContext *oc, AVStream *st) { - AVCodecContext *c; - AVPacket pkt = { 0 }; // data and size must be 0; - AVFrame *frame = avcodec_alloc_frame(); - int got_packet, ret, dst_nb_samples; - - av_init_packet(&pkt); - c = st->codec; - - get_audio_frame((int16_t *)src_samples_data[0], src_nb_samples, c->channels); - - /* convert samples from native format to destination codec format, using the resampler */ - if (swr_ctx) { - /* compute destination number of samples */ - dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, c->sample_rate) + src_nb_samples, - c->sample_rate, c->sample_rate, AV_ROUND_UP); - if (dst_nb_samples > max_dst_nb_samples) { - av_free(dst_samples_data[0]); - ret = av_samples_alloc(dst_samples_data, &dst_samples_linesize, c->channels, - dst_nb_samples, c->sample_fmt, 0); - if (ret < 0) - exit(1); - max_dst_nb_samples = dst_nb_samples; - dst_samples_size = av_samples_get_buffer_size(NULL, c->channels, dst_nb_samples, - c->sample_fmt, 0); - } - - /* convert to destination format */ - ret = swr_convert(swr_ctx, - dst_samples_data, dst_nb_samples, - (const uint8_t **)src_samples_data, src_nb_samples); - if (ret < 0) { - fprintf(stderr, "Error while converting\n"); - exit(1); - } - } else { - dst_samples_data[0] = src_samples_data[0]; - dst_nb_samples = src_nb_samples; - } - - frame->nb_samples = dst_nb_samples; - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, - dst_samples_data[0], dst_samples_size, 0); - - ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet); - if (ret < 0) { - fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret)); - exit(1); - } - - if (!got_packet) +// fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret)); return; - - pkt.stream_index = st->index; - - /* Write the compressed frame to the media file. */ - ret = av_interleaved_write_frame(oc, &pkt); - if (ret != 0) { - fprintf(stderr, "Error while writing audio frame: %s\n", - av_err2str(ret)); - exit(1); } - avcodec_free_frame(&frame); + ctx->frame_count++; } -static void close_audio(AVFormatContext *oc, AVStream *st) { +void FFMPEGInvoker::closeVideo(EncodingContext* ctx, AVFormatContext *oc, AVStream *st) { avcodec_close(st->codec); - av_free(src_samples_data[0]); - av_free(dst_samples_data[0]); +// av_free(ctx->src_picture.data[0]); + av_free(ctx->dst_picture.data[0]); + av_free(ctx->frame); } -void FFMPEGInvoker::invoke(const InvokeRequest& req) { - -#if 0 - const char *filename; - AVOutputFormat *fmt; - AVFormatContext *oc; - AVStream *audio_st, *video_st; - AVCodec *audio_codec, *video_codec; - double audio_time, video_time; - int ret; - - filename = "foo.avi"; - // Register all formats and codecs - av_register_all(); - - /* allocate the output media context */ - avformat_alloc_output_context2(&oc, NULL, NULL, filename); - if (!oc) { - printf("Could not deduce output format from file extension: using MPEG.\n"); - avformat_alloc_output_context2(&oc, NULL, "mpeg", filename); - } - if (!oc) { - return 1; - } - fmt = oc->oformat; - - /* Add the audio and video streams using the default format codecs - * and initialize the codecs. */ - video_st = NULL; - audio_st = NULL; - - if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_st = add_stream(oc, &video_codec, fmt->video_codec); - } - if (fmt->audio_codec != AV_CODEC_ID_NONE) { - audio_st = add_stream(oc, &audio_codec, fmt->audio_codec); - } - - /* Now that all the parameters are set, we can open the audio and - * video codecs and allocate the necessary encode buffers. */ - if (video_st) - open_video(oc, video_codec, video_st); - if (audio_st) - open_audio(oc, audio_codec, audio_st); - - av_dump_format(oc, 0, filename, 1); - - /* open the output file, if needed */ - if (!(fmt->flags & AVFMT_NOFILE)) { - ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); - if (ret < 0) { - fprintf(stderr, "Could not open '%s': %s\n", filename, - av_err2str(ret)); - return 1; - } - } - - /* Write the stream header, if any. */ - ret = avformat_write_header(oc, NULL); - if (ret < 0) { - fprintf(stderr, "Error occurred when opening output file: %s\n", - av_err2str(ret)); - return 1; - } - - if (frame) - frame->pts = 0; - for (;;) { - /* Compute current audio and video time. */ - audio_time = audio_st ? audio_st->pts.val * av_q2d(audio_st->time_base) : 0.0; - video_time = video_st ? video_st->pts.val * av_q2d(video_st->time_base) : 0.0; - - if ((!audio_st || audio_time >= STREAM_DURATION) && - (!video_st || video_time >= STREAM_DURATION)) - break; - - /* write interleaved audio and video frames */ - if (!video_st || (video_st && audio_st && audio_time < video_time)) { - write_audio_frame(oc, audio_st); - } else { - write_video_frame(oc, video_st); - frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base); - } - } - - /* Write the trailer, if any. The trailer must be written before you - * close the CodecContexts open when you wrote the header; otherwise - * av_write_trailer() may try to use memory that was freed on - * av_codec_close(). */ - av_write_trailer(oc); - - /* Close each codec. */ - if (video_st) - close_video(oc, video_st); - if (audio_st) - close_audio(oc, audio_st); - - if (!(fmt->flags & AVFMT_NOFILE)) - /* Close the output file. */ - avio_close(oc->pb); - - /* free the stream */ - avformat_free_context(oc); - - return 0; -#endif -} } \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h index 0337f25..e95b32e 100644 --- a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h +++ b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h @@ -4,11 +4,9 @@ #include extern "C" { -#include -#include +#include #include #include -#include } #ifdef BUILD_AS_PLUGINS @@ -37,14 +35,53 @@ public: protected: class EncodingContext { + public: + EncodingContext() : + format(NULL), + formatCtx(NULL), + audioStream(NULL), videoStream(NULL), + audioCodec(NULL), videoCodec(NULL), + audioTime(0), videoTime(0), + frame(NULL), + frame_count(0), + width(0), + height(0), + sws_flags(SWS_BICUBIC) {} + + virtual ~EncodingContext() { + if (sws_ctx) + sws_freeContext(sws_ctx); + } + + tthread::recursive_mutex mutex; + PixelFormat videoPixFmt; std::string filename; AVOutputFormat* format; AVFormatContext* formatCtx; - AVStream *audio_st, *video_st; - AVCodec *audio_codec, *video_codec; - double audio_time, video_time; + AVStream *audioStream, *videoStream; + AVCodec *audioCodec, *videoCodec, *imageCodec; + double audioTime, videoTime; + AVFrame *frame; + AVPicture src_picture, dst_picture; + int frame_count; + size_t width, height; + int sws_flags; + SwsContext *sws_ctx; + std::string extension; }; + AVStream* addStream(EncodingContext* ctx, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id); + void openVideo(EncodingContext* ctx, AVFormatContext *oc, AVCodec *codec, AVStream *st); + void writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, AVStream *st, boost::shared_ptr image); + void closeVideo(EncodingContext* ctx, AVFormatContext *oc, AVStream *st); + + static void run(void*); + void finish(EncodingContext* ctx, const SendRequest& req); + void process(const SendRequest& req); + + std::set _threads; + uscxml::concurrency::BlockingQueue _workQueue; + bool _isRunning; std::map _encoders; }; diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp index 0f6b776..1292e3a 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp @@ -65,20 +65,21 @@ void DirMonInvoker::invoke(const InvokeRequest& req) { return; } - if (boost::iequals(req.params.find("reportexisting")->second, "false")) + if (req.params.find("reportexisting") != req.params.end() && + boost::iequals(req.params.find("reportexisting")->second.atom, "false")) _reportExisting = false; if (req.params.find("recurse") != req.params.end() && - boost::iequals(req.params.find("recurse")->second, "true")) + boost::iequals(req.params.find("recurse")->second.atom, "true")) _recurse = true; if (req.params.find("reporthidden") != req.params.end() && - boost::iequals(req.params.find("reporthidden")->second, "true")) + boost::iequals(req.params.find("reporthidden")->second.atom, "true")) _reportHidden = true; std::string suffixList; if (req.params.find("suffix") != req.params.end()) { - suffixList = req.params.find("suffix")->second; + suffixList = req.params.find("suffix")->second.atom; } else if (req.params.find("suffixes") != req.params.end()) { - suffixList = req.params.find("suffixes")->second; + suffixList = req.params.find("suffixes")->second.atom; } if (suffixList.size() > 0) { @@ -92,9 +93,10 @@ void DirMonInvoker::invoke(const InvokeRequest& req) { } } - std::multimap::const_iterator dirIter = req.params.find("dir"); + std::multimap::const_iterator dirIter = req.params.find("dir"); while(dirIter != req.params.upper_bound("dir")) { - URL url(dirIter->second); + // this is simplified - Data might be more elaborate than a simple string atom + URL url(dirIter->second.atom); if (!url.toAbsolute(_interpreter->getBaseURI()) || !boost::iequals(url.scheme(), "file")) { LOG(ERROR) << "Given directory '" << dirIter->second << "' cannot be transformed to absolute path"; } else { @@ -266,6 +268,8 @@ void DirectoryWatch::updateEntries(bool reportAsExisting) { } if ((unsigned)dirStat.st_mtime >= (unsigned)_lastChecked) { +// std::cout << "dirStat.st_mtime: " << dirStat.st_mtime << " / _lastChecked: " << _lastChecked << std::endl; + // there are changes in the directory std::set currEntries; diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGInvoker.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGInvoker.cpp index 090f1b3..2b77baf 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGInvoker.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/OSGInvoker.cpp @@ -1,5 +1,6 @@ #include "OSGInvoker.h" #include "uscxml/URL.h" +#include "uscxml/UUID.h" #include #ifdef BUILD_AS_PLUGINS @@ -110,7 +111,7 @@ void OSGInvoker::processViewport(const Arabica::DOM::Node& element) _nodes[element] = group; sceneView->setSceneData(group); - std::string name = (HAS_ATTR(element, "id") ? ATTR(element, "id") : Interpreter::getUUID()); + std::string name = (HAS_ATTR(element, "id") ? ATTR(element, "id") : UUID::getUUID()); unsigned int actualX = 0; unsigned int actualY = 0; diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp index e377e5c..0e8430c 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp @@ -21,12 +21,12 @@ #define EVAL_PARAM_EXPR(param, expr, key) \ if (param.find(key) == param.end() && param.find(expr) != param.end() && _interpreter->getDataModel()) \ - param.insert(std::make_pair(key, _interpreter->getDataModel().evalAsString(param.find(expr)->second))); + param.insert(std::make_pair(key, _interpreter->getDataModel().evalAsString(param.find(expr)->second.atom))); #define CAST_PARAM(param, var, key, type) \ if (param.find(key) != param.end()) { \ - try { var = boost::lexical_cast(param.find(key)->second); } \ - catch(...) { LOG(ERROR) << "Attribute " key " of sendrequest to osgconverter is of invalid format: " << param.find(key)->second; } \ + try { var = boost::lexical_cast(param.find(key)->second.atom); } \ + catch(...) { LOG(ERROR) << "Attribute " key " of sendrequest to osgconverter is of invalid format: " << param.find(key)->second.atom; } \ } @@ -42,6 +42,7 @@ bool connect(pluma::Host& host) { OSGConverter::OSGConverter() : _isRunning(false) { // osg::setNotifyLevel(osg::DEBUG_FP); + osg::setNotifyLevel(osg::FATAL); } OSGConverter::~OSGConverter() { @@ -76,7 +77,7 @@ void OSGConverter::send(const SendRequest& req) { if (actualReq.params.find("source") == actualReq.params.end()) { // no explicit source if (actualReq.params.find("sourceexpr") != actualReq.params.end() && _interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("source", _interpreter->getDataModel().evalAsString(actualReq.params.find("sourceexpr")->second))); + actualReq.params.insert(std::make_pair("source", _interpreter->getDataModel().getStringAsData(actualReq.params.find("sourceexpr")->second))); } else { LOG(ERROR) << "SendRequests for osginvoker missing source or sourceExpr and datamodel"; return; @@ -86,20 +87,16 @@ void OSGConverter::send(const SendRequest& req) { if (actualReq.params.find("dest") == actualReq.params.end()) { // no explicit destination if (actualReq.params.find("destexpr") != actualReq.params.end() && _interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("dest", _interpreter->getDataModel().evalAsString(actualReq.params.find("destexpr")->second))); - } else { - LOG(ERROR) << "SendRequests for osginvoker missing dest or destExpr and datamodel"; - return; + actualReq.params.insert(std::make_pair("dest", _interpreter->getDataModel().getStringAsData(actualReq.params.find("destexpr")->second))); + boost::algorithm::replace_all(actualReq.params.find("dest")->second.atom, "//", "/"); + boost::algorithm::replace_all(actualReq.params.find("dest")->second.atom, "\\\\", "\\"); } } - boost::algorithm::replace_all(actualReq.params.find("dest")->second, "//", "/"); - boost::algorithm::replace_all(actualReq.params.find("dest")->second, "\\\\", "\\"); - if (actualReq.params.find("autorotate") == actualReq.params.end()) { if (actualReq.params.find("autorotateexpr") != actualReq.params.end()) { if (_interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("autorotate", _interpreter->getDataModel().evalAsString(actualReq.params.find("autorotateexpr")->second))); + actualReq.params.insert(std::make_pair("autorotate", _interpreter->getDataModel().getStringAsData(actualReq.params.find("autorotateexpr")->second))); } else { LOG(ERROR) << "SendRequests for osginvoker ncludes autorotateexpr but no datamodel is specified"; return; @@ -110,34 +107,25 @@ void OSGConverter::send(const SendRequest& req) { if (actualReq.params.find("format") == actualReq.params.end()) { // no explicit format if (actualReq.params.find("formatexpr") != actualReq.params.end() && _interpreter->getDataModel()) { - actualReq.params.insert(std::make_pair("format", _interpreter->getDataModel().evalAsString(actualReq.params.find("formatexpr")->second))); + actualReq.params.insert(std::make_pair("format", _interpreter->getDataModel().getStringAsData(actualReq.params.find("formatexpr")->second))); } else { std::string format; - size_t lastDot; - std::string dest = req.params.find("dest")->second; - if ((lastDot = dest.find_last_of(".")) != std::string::npos) { - lastDot++; - format = dest.substr(lastDot, dest.length() - lastDot); + std::string dest; + if (Event::getParam(actualReq.params, "dest", dest)) { + size_t lastDot; + if ((lastDot = dest.find_last_of(".")) != std::string::npos) { + lastDot++; + format = dest.substr(lastDot, dest.length() - lastDot); + } } if (format.length() == 0 || format.find_last_of(PATH_SEPERATOR) != std::string::npos) { // empty format or pathseperator in format format = "png"; } - actualReq.params.insert(std::make_pair("format", format)); + actualReq.params.insert(std::make_pair("format", Data(format, Data::VERBATIM))); } } -// assert(osgDB::Registry::instance()->getReaderWriterForExtension("png")); -// osgDB::Registry::ReaderWriterList formatList = osgDB::Registry::instance()->getReaderWriterList(); -// for (int i = 0; i < formatList.size(); i++) { -// std::map funcDesc = formatList[i]->supportedProtocols(); -// std::map::iterator funcDescIter = funcDesc.begin(); -// while(funcDescIter != funcDesc.end()) { -// std::cout << funcDescIter->first << ": " << funcDescIter->second << std::endl; -// funcDescIter++; -// } -// } - EVAL_PARAM_EXPR(actualReq.params, "heightexpr", "height"); EVAL_PARAM_EXPR(actualReq.params, "widthexpr", "width"); EVAL_PARAM_EXPR(actualReq.params, "pitchexpr", "pitch"); @@ -157,7 +145,8 @@ void OSGConverter::cancel(const std::string sendId) { void OSGConverter::invoke(const InvokeRequest& req) { int nrThreads = 1; - if (req.params.find("threads") != req.params.end() && isNumeric(req.params.find("threads")->second.c_str(), 10)) { + + if (req.params.find("threads") != req.params.end() && isNumeric(req.params.find("threads")->second.atom.c_str(), 10)) { nrThreads = strTo(req.params.find("threads")->second); } @@ -185,22 +174,28 @@ void OSGConverter::process(const SendRequest& req) { int width = 640; int height = 480; - CAST_PARAM(req.params, width, "width", int); - CAST_PARAM(req.params, height, "height", int); + Event::getParam(req.params, "width", width); + Event::getParam(req.params, "height", height); assert(req.params.find("source") != req.params.end()); - assert(req.params.find("dest") != req.params.end()); assert(req.params.find("format") != req.params.end()); - std::string source = req.params.find("source")->second; - std::string dest = req.params.find("dest")->second; - std::string format = req.params.find("format")->second; + std::string source; + if (!Event::getParam(req.params, "source", source)) + reportFailure(req); + + std::string dest; + Event::getParam(req.params, "dest", dest); + + std::string format; + if (!Event::getParam(req.params, "format", format)) + reportFailure(req); bool autoRotate = true; if (req.params.find("autorotate") != req.params.end()) { - if (boost::iequals(req.params.find("autorotate")->second, "off") || - boost::iequals(req.params.find("autorotate")->second, "0") || - boost::iequals(req.params.find("autorotate")->second, "false")) { + if (boost::iequals(req.params.find("autorotate")->second.atom, "off") || + boost::iequals(req.params.find("autorotate")->second.atom, "0") || + boost::iequals(req.params.find("autorotate")->second.atom, "false")) { autoRotate = false; } } @@ -215,16 +210,22 @@ void OSGConverter::process(const SendRequest& req) { sceneGraph->addChild(model); osgDB::ReaderWriter::WriteResult result; - if (osgDB::Registry::instance()->getReaderWriterForExtension(format) != NULL) { - // write as another 3D file - result = osgDB::Registry::instance()->writeNode(*sceneGraph, dest, osgDB::Registry::instance()->getOptions()); + + osg::ref_ptr writer = osgDB::Registry::instance()->getReaderWriterForExtension(format); + if(writer.valid()) { + std::stringstream ss; + result = writer->writeNode(*sceneGraph, ss); if (result.success()) { - // we can know about success right here - reportSuccess(req); + if (dest.length() > 0) { + std::ofstream outFile(dest.c_str()); + outFile << ss.str(); + } + Data content(ss.str().c_str(), ss.str().size(), false); + reportSuccess(req, content); return; } } - + /** * If we failed to interpret the extension as another 3D file, try to make a screenshot. */ @@ -245,53 +246,66 @@ void OSGConverter::process(const SendRequest& req) { osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1); - osgViewer::Viewer viewer; - viewer.setSceneData(sceneGraph); - viewer.setCameraManipulator(new osgGA::TrackballManipulator()); - viewer.addEventHandler(captureHandler); - captureHandler->startCapture(); - - osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); - osg::ref_ptr traits = new osg::GraphicsContext::Traits(ds); - traits->width = width; - traits->height = height; - traits->pbuffer = true; - osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); - - if (!gc.valid()) { - LOG(ERROR) << "Cannot create GraphicsContext!"; - return; - } + { +// tthread::lock_guard lock(_viewerMutex); + osgViewer::Viewer viewer; + osg::ref_ptr gc; + + viewer.setSceneData(sceneGraph); + viewer.addEventHandler(captureHandler); + captureHandler->startCapture(); + + osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); + osg::ref_ptr traits = new osg::GraphicsContext::Traits(ds); + traits->width = width; + traits->height = height; + traits->pbuffer = true; + gc = osg::GraphicsContext::createGraphicsContext(traits.get()); + + if (!gc.valid()) { + LOG(ERROR) << "Cannot create GraphicsContext!"; + return; + } + + GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; - GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + viewer.getCamera()->setGraphicsContext(gc.get()); + viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height)); + viewer.getCamera()->setDrawBuffer(pbuffer); + viewer.getCamera()->setReadBuffer(pbuffer); - viewer.getCamera()->setGraphicsContext(gc.get()); - viewer.getCamera()->setViewport(new osg::Viewport(0,0,traits->width,traits->height)); - viewer.getCamera()->setDrawBuffer(pbuffer); - viewer.getCamera()->setReadBuffer(pbuffer); + double zoom = 1; + CAST_PARAM(req.params, zoom, "zoom", double); - double zoom = 1; - CAST_PARAM(req.params, zoom, "zoom", double); + // set background color + viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f)); + viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + viewer.getCameraManipulator()->setByMatrix(osg::Matrix::lookAt(osg::Vec3d(0,0,bs.radius() * (-3.4 * zoom)), // eye + (osg::Vec3d)bs.center(), // center + osg::Vec3d(0,1,0))); // up - // set background color - viewer.getCamera()->setClearColor(osg::Vec4f(1.0f,1.0f,1.0f,1.0f)); - viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - viewer.getCameraManipulator()->setByMatrix(osg::Matrix::lookAt(osg::Vec3d(0,0,bs.radius() * (-3.4 * zoom)), // eye - (osg::Vec3d)bs.center(), // center - osg::Vec3d(0,1,0))); // up + // viewer.home(); -// viewer.home(); + // perform one viewer iteration + viewer.realize(); + viewer.frame(); - // perform one viewer iteration - viewer.realize(); - viewer.frame(); + viewer.removeEventHandler(captureHandler); + } + +// delete(cOp); +// delete(captureHandler); } -void OSGConverter::reportSuccess(const SendRequest& req) { +void OSGConverter::reportSuccess(const SendRequest& req, const Data& content) { Event event(req); + if (event.name.length() == 0) event.name = "convert"; event.name += ".success"; + if (content) + event.data.compound["content"] = content; returnEvent(event); } @@ -366,7 +380,7 @@ osg::ref_ptr OSGConverter::setupGraph(const std::string filename, boo std::map > >::iterator modelIter = _models.begin(); while(modelIter != _models.end()) { // delete every model unused for 1 minutes - if (now - modelIter->second.first > 60000) { + if (now - modelIter->second.first > 6000) { _models.erase(modelIter++); } else { modelIter++; @@ -504,30 +518,79 @@ void OSGConverter::dumpMatrix(const osg::Matrix& m) { } void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image, const unsigned int context_id) { + + // write to memory first + std::string format; + if (_req.params.find("format") != _req.params.end()) { + format = _req.params.find("format")->second.atom; + } else { + _converter->reportFailure(_req); + } -// URL fileURL(_filename); -// fileURL.path() + osg::ref_ptr op = new osgDB::ReaderWriter::Options(); + //op->setOptionString("JPEG_QUALITY 75"); + //op->setOptionString("PNG_COMPRESSION 9"); - std::string tmpName = _filename; - size_t pathSep = _filename.find_last_of(PATH_SEPERATOR); - if (pathSep != std::string::npos) { - tmpName = _filename.substr(0, pathSep) + PATH_SEPERATOR + ".tmp" + _filename.substr(pathSep + 1, _filename.length() - pathSep - 1); + // osgDB got confused when we write to a stringstream + std::string tempFile = URL::getTmpFilename(format); + osgDB::writeImageFile(image, tempFile, op); + + char* buffer = NULL; + size_t length = 0; + { + std::ifstream file(tempFile.c_str()); + + file.seekg(0, std::ios::end); + length = file.tellg(); + file.seekg(0, std::ios::beg); + buffer = (char*)malloc(length); + file.read(buffer, length); } + + remove(tempFile.c_str()); +// osg::ref_ptr writerFormat = osgDB::Registry::instance()->getReaderWriterForExtension(format); +// if(!writerFormat.valid()) +// _converter->reportFailure(_req); - bool success = osgDB::writeImageFile(image, tmpName); // <- no plugin to write to .tmp format - if (!success) { - _converter->reportFailure(_req); - return; - } - if (pathSep != std::string::npos) { - int err = rename(tmpName.c_str(), _filename.c_str()); - if (err) { - _converter->reportFailure(_req); +#if 0 + std::stringstream ssFormat; + + osgDB::ReaderWriter::WriteResult res = writerFormat->writeImage(image, ssFormat, op); + + if (_filename.length() > 0) { + std::string tmpName = _filename; + size_t pathSep = _filename.find_last_of(PATH_SEPERATOR); + if (pathSep != std::string::npos) { + tmpName = _filename.substr(0, pathSep) + PATH_SEPERATOR + ".tmp" + _filename.substr(pathSep + 1, _filename.length() - pathSep - 1); } - } - _converter->reportSuccess(_req); + { + std::ofstream outFile(tmpName.c_str()); + outFile << ssFormat.str(); + } + + if (pathSep != std::string::npos) { + int err = rename(tmpName.c_str(), _filename.c_str()); + if (err) { + _converter->reportFailure(_req); + } + } + } +#endif + + Data content; + content.compound[format] = Data(buffer, length, false); + + // save image as a raw rgba as well for ffmpeg - we are using the mpb format for now +// osg::ref_ptr writerRGBA = osgDB::Registry::instance()->getReaderWriterForExtension("rgba"); +// if(writerRGBA.valid()) { +// std::stringstream ssRGBA; +// osgDB::ReaderWriter::WriteResult res = writerRGBA->writeImage(image, ssRGBA, op); +// content.compound["rgba"] = Data(ssRGBA.str().c_str(), ssRGBA.str().size(), false); +// } + + _converter->reportSuccess(_req, content); } } \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.h b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.h index 78d6c6c..2bc84a1 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.h +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.h @@ -31,7 +31,7 @@ public: virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); - void reportSuccess(const SendRequest& req); + void reportSuccess(const SendRequest& req, const Data& content); void reportFailure(const SendRequest& req); osg::Matrix requestToModelPose(const SendRequest& req); @@ -61,6 +61,8 @@ protected: osg::ref_ptr setupGraph(const std::string filename, bool autoRotate = false); osg::ref_ptr getOrigin(); + tthread::recursive_mutex _viewerMutex; + std::map > > _models; tthread::recursive_mutex _cacheMutex; diff --git a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp index c7b91f6..5afbc66 100644 --- a/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp +++ b/src/uscxml/plugins/invoker/heartbeat/HeartbeatInvoker.cpp @@ -49,8 +49,8 @@ void HeartbeatInvoker::invoke(const InvokeRequest& req) { InvokeRequest::params_t::const_iterator paramIter = req.params.begin(); while(paramIter != req.params.end()) { if (boost::iequals(paramIter->first, "interval")) { - intervalStr = paramIter->second; - NumAttr intervalAttr(paramIter->second); + intervalStr = paramIter->second.atom; + NumAttr intervalAttr(paramIter->second.atom); interval = strTo(intervalAttr.value); if (false) { } else if (boost::iequals(intervalAttr.unit, "s")) { @@ -62,7 +62,7 @@ void HeartbeatInvoker::invoke(const InvokeRequest& req) { } } if (boost::iequals(paramIter->first, "eventname")) { - _event.name = paramIter->second; + _event.name = paramIter->second.atom; } paramIter++; } diff --git a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp index 2c395a8..1a95396 100644 --- a/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp +++ b/src/uscxml/plugins/invoker/http/HTTPServletInvoker.cpp @@ -52,15 +52,15 @@ void HTTPServletInvoker::send(const SendRequest& req) { HTTPServer::Reply httpReply(httpRequest); httpReply.content = req.content; - std::map::const_iterator nameListIter = req.namelist.begin(); + std::map::const_iterator nameListIter = req.namelist.begin(); while(nameListIter != req.namelist.end()) { - httpReply.headers[nameListIter->first] = nameListIter->second; + httpReply.headers[nameListIter->first] = nameListIter->second.atom; nameListIter++; } - std::multimap::const_iterator paramIter = req.params.begin(); + std::multimap::const_iterator paramIter = req.params.begin(); while(paramIter != req.params.end()) { - httpReply.headers[paramIter->first] = paramIter->second; + httpReply.headers[paramIter->first] = paramIter->second.atom; paramIter++; } @@ -78,10 +78,10 @@ void HTTPServletInvoker::invoke(const InvokeRequest& req) { if (req.params.find("path") == req.params.end()) { LOG(ERROR) << "Path parameter required with httpserver"; } - _path = (*req.params.find("path")).second; + _path = (*req.params.find("path")).second.atom; if (req.params.find("callback") != req.params.end()) { - _callback = (*req.params.find("callback")).second; + _callback = (*req.params.find("callback")).second.atom; } else { _callback = _path; std::replace(_callback.begin(), _callback.end(), '/', '.'); diff --git a/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp b/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp index fb91e40..9a26960 100644 --- a/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp +++ b/src/uscxml/plugins/invoker/miles/SpatialAudio.cpp @@ -125,7 +125,7 @@ void SpatialAudio::invoke(const InvokeRequest& req) { getPosFromParams(req.params, _pos); - std::multimap::const_iterator paramIter = req.params.begin(); + std::multimap::const_iterator paramIter = req.params.begin(); while(paramIter != req.params.end()) { if (boost::iequals(paramIter->first, "maxX")) _maxPos[0] = strTo(paramIter->second); @@ -150,7 +150,7 @@ void SpatialAudio::invoke(const InvokeRequest& req) { } } -void SpatialAudio::getPosFromParams(const std::multimap& params, float* position) { +void SpatialAudio::getPosFromParams(const std::multimap& params, float* position) { // vector explicitly given try { if (params.find("x") != params.end()) diff --git a/src/uscxml/plugins/invoker/miles/SpatialAudio.h b/src/uscxml/plugins/invoker/miles/SpatialAudio.h index 59ecfdb..e7ab830 100644 --- a/src/uscxml/plugins/invoker/miles/SpatialAudio.h +++ b/src/uscxml/plugins/invoker/miles/SpatialAudio.h @@ -34,7 +34,7 @@ public: virtual void invoke(const InvokeRequest& req); virtual void sendToParent(SendRequest& req); - void getPosFromParams(const std::multimap& params, float* position); + void getPosFromParams(const std::multimap& params, float* position); static float posToRadian(const std::string& position); protected: diff --git a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp index de5a580..6845280 100644 --- a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp +++ b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp @@ -63,7 +63,7 @@ void UmundoInvoker::send(const SendRequest& req) { std::string type; if (req.params.find("type") != req.params.end()) { // we are supposed to build a typed object - type = req.params.find("type")->second; + type = req.params.find("type")->second.atom; const google::protobuf::Message* protoMsg = umundo::PBSerializer::getProto(type); if (protoMsg == NULL) { @@ -139,17 +139,17 @@ void UmundoInvoker::invoke(const InvokeRequest& req) { std::string serviceName; if (req.params.find("channel") != req.params.end()) { - channelName = req.params.find("channel")->second; + channelName = req.params.find("channel")->second.atom; _isService = false; } else if (req.params.find("service") != req.params.end()) { - serviceName = req.params.find("service")->second; + serviceName = req.params.find("service")->second.atom; _isService = true; } else { LOG(ERROR) << "Invoking umundo needs a service or a channel param"; return; } if (req.params.find("domain") != req.params.end()) { - domain = req.params.find("domain")->second; + domain = req.params.find("domain")->second.atom; } _node = getNode(_interpreter, domain); @@ -157,7 +157,7 @@ void UmundoInvoker::invoke(const InvokeRequest& req) { if (req.params.find("type") != req.params.end()) { std::pair typeRange = req.params.equal_range("types"); for (InvokeRequest::params_t::const_iterator it = typeRange.first; it != typeRange.second; it++) { - URL typeURI(it->second); + URL typeURI(it->second.atom); if (typeURI.toAbsolute(_interpreter->getBaseURI())) { std::string filename = typeURI.asLocalFile(".proto"); umundo::PBSerializer::addProto(filename); @@ -171,7 +171,7 @@ void UmundoInvoker::invoke(const InvokeRequest& req) { if (req.params.find("types") != req.params.end()) { std::pair typeRange = req.params.equal_range("types"); for (InvokeRequest::params_t::const_iterator it = typeRange.first; it != typeRange.second; it++) { - URL typeURI(it->second); + URL typeURI(it->second.atom); if (typeURI.toAbsolute(_interpreter->getBaseURI()) && typeURI.scheme().compare("file") == 0) { umundo::PBSerializer::addProto(typeURI.path()); } else { diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp index 90cebc3..74e51d6 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp @@ -187,10 +187,11 @@ void BasicHTTPIOProcessor::send(const SendRequest& req) { // event namelist if (req.namelist.size() > 0) { - std::map::const_iterator namelistIter = req.namelist.begin(); + std::map::const_iterator namelistIter = req.namelist.begin(); while (namelistIter != req.namelist.end()) { char* keyCStr = evhttp_encode_uri(namelistIter->first.c_str()); - char* valueCStr = evhttp_encode_uri(namelistIter->second.c_str()); + // this is simplified - Data might be more elaborate than a simple string atom + char* valueCStr = evhttp_encode_uri(namelistIter->second.atom.c_str()); kvps << kvpSeperator << keyCStr << "=" << valueCStr; free(keyCStr); free(valueCStr); @@ -202,10 +203,11 @@ void BasicHTTPIOProcessor::send(const SendRequest& req) { // event params if (req.params.size() > 0) { - std::multimap::const_iterator paramIter = req.params.begin(); + std::multimap::const_iterator paramIter = req.params.begin(); while (paramIter != req.params.end()) { char* keyCStr = evhttp_encode_uri(paramIter->first.c_str()); - char* valueCStr = evhttp_encode_uri(paramIter->second.c_str()); + // this is simplified - Data might be more elaborate than a simple string atom + char* valueCStr = evhttp_encode_uri(paramIter->second.atom.c_str()); kvps << kvpSeperator << keyCStr << "=" << valueCStr; free(keyCStr); free(valueCStr); diff --git a/src/uscxml/plugins/ioprocessor/modality/MMIHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/modality/MMIHTTPIOProcessor.cpp index 67b518c..0009cc0 100644 --- a/src/uscxml/plugins/ioprocessor/modality/MMIHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/modality/MMIHTTPIOProcessor.cpp @@ -138,9 +138,10 @@ void MMIHTTPIOProcessor::send(const SendRequest& req) { // event namelist if (req.namelist.size() > 0) { - std::map::const_iterator namelistIter = req.namelist.begin(); + std::map::const_iterator namelistIter = req.namelist.begin(); while (namelistIter != req.namelist.end()) { - kvps << kvpSeperator << evhttp_encode_uri(namelistIter->first.c_str()) << "=" << evhttp_encode_uri(namelistIter->second.c_str()); + // this is simplified - Data might be more elaborate than a simple string atom + kvps << kvpSeperator << evhttp_encode_uri(namelistIter->first.c_str()) << "=" << evhttp_encode_uri(namelistIter->second.atom.c_str()); kvpSeperator = "&"; // targetURL.addOutHeader(namelistIter->first, namelistIter->second); namelistIter++; @@ -149,9 +150,10 @@ void MMIHTTPIOProcessor::send(const SendRequest& req) { // event params if (req.params.size() > 0) { - std::multimap::const_iterator paramIter = req.params.begin(); + std::multimap::const_iterator paramIter = req.params.begin(); while (paramIter != req.params.end()) { - kvps << kvpSeperator << evhttp_encode_uri(paramIter->first.c_str()) << "=" << evhttp_encode_uri(paramIter->second.c_str()); + // this is simplified - Data might be more elaborate than a simple string atom + kvps << kvpSeperator << evhttp_encode_uri(paramIter->first.c_str()) << "=" << evhttp_encode_uri(paramIter->second.atom.c_str()); kvpSeperator = "&"; // targetURL.addOutHeader(paramIter->first, paramIter->second); paramIter++; diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index e37d8cc..0521d74 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -194,14 +194,15 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD // } // seperate path into components - std::stringstream ss(request.data.compound["path"].atom); - std::string item; - while(std::getline(ss, item, '/')) { - if (item.length() == 0) - continue; - request.data.compound["pathComponent"].array.push_back(Data(item, Data::VERBATIM)); + { + std::stringstream ss(request.data.compound["path"].atom); + std::string item; + while(std::getline(ss, item, '/')) { + if (item.length() == 0) + continue; + request.data.compound["pathComponent"].array.push_back(Data(item, Data::VERBATIM)); + } } - // parse query string struct evkeyvalq params; struct evkeyval *param; @@ -233,9 +234,27 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD std::string contentType = request.data.compound["header"].compound["Content-Type"].atom; if (false) { } else if (boost::iequals(contentType, "application/x-www-form-urlencoded")) { - char* contentCStr = evhttp_decode_uri(request.data.compound["content"].atom.c_str()); - request.data.compound["content"].atom = contentCStr; - free(contentCStr); + // this is a form submit + std::stringstream ss(request.data.compound["content"].atom); + std::string item; + std::string key; + std::string value; + while(std::getline(ss, item, '=')) { + if (item.length() == 0) + continue; + if (key.length() == 0) { + key = item; + continue; + } + value = item; + char* keyCStr = evhttp_decode_uri(key.c_str()); + char* valueCStr = evhttp_decode_uri(value.c_str()); + request.data.compound["content"].compound[keyCStr] = Data(valueCStr, Data::VERBATIM); + free(keyCStr); + free(valueCStr); + key.clear(); + } + request.data.compound["content"].atom.clear(); } else if (boost::iequals(contentType, "application/json")) { request.data.compound["content"] = Data::fromJSON(request.data.compound["content"].atom); } diff --git a/src/uscxml/server/InterpreterServlet.cpp b/src/uscxml/server/InterpreterServlet.cpp index b59cc05..49f6677 100644 --- a/src/uscxml/server/InterpreterServlet.cpp +++ b/src/uscxml/server/InterpreterServlet.cpp @@ -35,7 +35,22 @@ bool InterpreterServlet::httpRecvRequest(const HTTPServer::Request& req) { event.name = "http." + event.data.compound["type"].atom; event.origin = toStr((uintptr_t)req.curlReq); - event.initContent(event.data.compound["content"].atom); + + if (event.data.compound["content"]) { + if (event.data.compound["content"].compound.size() > 0) { + std::map::iterator compoundIter = event.data.compound["content"].compound.begin(); + while(compoundIter != event.data.compound["content"].compound.end()) { +// std::cout << compoundIter->second.atom << std::endl; + Data json = Data::fromJSON(compoundIter->second.atom); + if (json) { +// std::cout << Data::toJSON(json) << std::endl; + compoundIter->second = json; + } + compoundIter++; + } + } + } + _interpreter->receive(event); return true; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 93c002e..5fcad93 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,6 +18,13 @@ if (SWI_FOUND) add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml) endif() +if (FFMPEG_FOUND) + add_executable(test-ffmpeg src/test-ffmpeg.cpp) + target_link_libraries(test-ffmpeg uscxml) + add_test(test-ffmpeg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ffmpeg) + set_target_properties(test-ffmpeg PROPERTIES FOLDER "Tests") +endif() + if (V8_FOUND) add_test(test-ecmascript ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-ecmascript.scxml) endif() diff --git a/test/src/test-ffmpeg.cpp b/test/src/test-ffmpeg.cpp new file mode 100644 index 0000000..c9dcfe1 --- /dev/null +++ b/test/src/test-ffmpeg.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file + * libavformat API example. + * + * Output a media file in any supported libavformat format. + * The default codecs are used. + * @example doc/examples/muxing.c + */ + +#include +#include + +extern "C" { +#include +#include +#include +#include + +#include +#include +#include +#include +#include +} +/* 5 seconds stream duration */ +#define STREAM_FRAME_RATE 25 /* 25 images/s */ +#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ +#define BMP_FORMAT AV_PIX_FMT_RGB24 + +static int sws_flags = SWS_BICUBIC; + +/* Add an output stream. */ +static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, + enum AVCodecID codec_id) +{ + AVCodecContext *c; + AVStream *st; + + /* find the encoder */ + *codec = avcodec_find_encoder(codec_id); + if (!(*codec)) { + fprintf(stderr, "Could not find encoder for '%s'\n", + avcodec_get_name(codec_id)); + exit(1); + } + + st = avformat_new_stream(oc, *codec); + if (!st) { + fprintf(stderr, "Could not allocate stream\n"); + exit(1); + } + st->id = oc->nb_streams-1; + c = st->codec; + + switch ((*codec)->type) { + case AVMEDIA_TYPE_AUDIO: + c->sample_fmt = AV_SAMPLE_FMT_FLTP; + c->bit_rate = 64000; + c->sample_rate = 44100; + c->channels = 2; + break; + + case AVMEDIA_TYPE_VIDEO: + c->codec_id = codec_id; + + c->bit_rate = 400000; + /* Resolution must be a multiple of two. */ + c->width = 352; + c->height = 288; + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + c->time_base.den = STREAM_FRAME_RATE; + c->time_base.num = 1; + c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = STREAM_PIX_FMT; + if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + c->max_b_frames = 2; + } + if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + c->mb_decision = 2; + } + break; + + default: + break; + } + + /* Some formats want stream headers to be separate. */ + if (oc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + + return st; +} + + +/**************************************************************/ +/* video output */ + +static AVFrame *frame; +static AVPicture src_picture, dst_picture; +static int frame_count; + +static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st) +{ + int ret; + AVCodecContext *c = st->codec; + + /* open the codec */ + ret = avcodec_open2(c, codec, NULL); + if (ret < 0) { + fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret)); + exit(1); + } + + /* allocate and init a re-usable frame */ + frame = avcodec_alloc_frame(); + if (!frame) { + fprintf(stderr, "Could not allocate video frame\n"); + exit(1); + } + + /* Allocate the encoded raw picture. */ + ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height); + if (ret < 0) { + fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret)); + exit(1); + } + + /* If the output format is not YUV420P, then a temporary YUV420P + * picture is needed too. It is then converted to the required + * output format. */ + if (c->pix_fmt != BMP_FORMAT) { + ret = avpicture_alloc(&src_picture, BMP_FORMAT, c->width, c->height); + if (ret < 0) { + fprintf(stderr, "Could not allocate temporary picture: %s\n", + av_err2str(ret)); + exit(1); + } + } + + /* copy data and linesize picture pointers to frame */ + *((AVPicture *)frame) = dst_picture; +} + + +/* Prepare a dummy image. */ +static void fill_rgba_image(AVPicture *pict, int frame_index, + int width, int height) +{ + int x, y, i; + + i = frame_index; + + std::stringstream ssFilename; + ssFilename << "/Users/sradomski/Desktop/ctrl/" << (i % 125) << ".bmp"; + + std::ifstream file(ssFilename.str().c_str()); + + file.seekg(0, std::ios::end); + size_t length = file.tellg(); + file.seekg(0, std::ios::beg); + + char* buffer = (char*)malloc(length); + file.read(buffer, length); + + uint32_t offset = 0; + offset += buffer[10] << 0; + offset += buffer[11] << 8; + offset += buffer[12] << 16; + offset += buffer[13] << 24; + offset--; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + pict->data[0][y * pict->linesize[0] + x * 3] = buffer[offset++]; + pict->data[0][y * pict->linesize[0] + x * 3 + 1] = buffer[offset++]; + pict->data[0][y * pict->linesize[0] + x * 3 + 2] = buffer[offset++]; + } + } + + free(buffer); +} + +static void write_video_frame(AVFormatContext *oc, AVStream *st) +{ + int ret; + static struct SwsContext *sws_ctx; + AVCodecContext *c = st->codec; + + if (c->pix_fmt != BMP_FORMAT) { + /* as we only generate a YUV420P picture, we must convert it + * to the codec pixel format if needed */ + if (!sws_ctx) { + sws_ctx = sws_getContext(c->width, c->height, BMP_FORMAT, + c->width, c->height, c->pix_fmt, + sws_flags, NULL, NULL, NULL); + if (!sws_ctx) { + fprintf(stderr, + "Could not initialize the conversion context\n"); + exit(1); + } + } + fill_rgba_image(&src_picture, frame_count, c->width, c->height); + sws_scale(sws_ctx, + (const uint8_t * const *)src_picture.data, src_picture.linesize, + 0, c->height, dst_picture.data, dst_picture.linesize); + } else { + fill_rgba_image(&dst_picture, frame_count, c->width, c->height); + } + + if (oc->oformat->flags & AVFMT_RAWPICTURE) { + /* Raw video case - directly store the picture in the packet */ + AVPacket pkt; + av_init_packet(&pkt); + + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index = st->index; + pkt.data = dst_picture.data[0]; + pkt.size = sizeof(AVPicture); + + ret = av_interleaved_write_frame(oc, &pkt); + } else { + AVPacket pkt = { 0 }; + int got_packet; + av_init_packet(&pkt); + + /* encode the image */ + ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); + if (ret < 0) { + fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); + exit(1); + } + /* If size is zero, it means the image was buffered. */ + + if (!ret && got_packet && pkt.size) { + pkt.stream_index = st->index; + + /* Write the compressed frame to the media file. */ + ret = av_interleaved_write_frame(oc, &pkt); + } else { + ret = 0; + } + } + if (ret != 0) { + fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret)); + exit(1); + } + frame_count++; +} + +static void close_video(AVFormatContext *oc, AVStream *st) +{ + avcodec_close(st->codec); + av_free(src_picture.data[0]); + av_free(dst_picture.data[0]); + av_free(frame); +} + +/**************************************************************/ +/* media file output */ + +int main(int argc, char **argv) +{ + const char *filename; + AVOutputFormat *fmt; + AVFormatContext *oc; + AVStream *video_st; + AVCodec *video_codec; + int ret; + + /* Initialize libavcodec, and register all codecs and formats. */ + av_register_all(); + + filename = "/Users/sradomski/Desktop/test.mpg"; + + /* allocate the output media context */ + avformat_alloc_output_context2(&oc, NULL, NULL, filename); + if (!oc) { + printf("Could not deduce output format from file extension: using MPEG.\n"); + avformat_alloc_output_context2(&oc, NULL, "mpeg", filename); + } + if (!oc) { + return 1; + } + fmt = oc->oformat; + + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + video_st = NULL; + + if (fmt->video_codec != AV_CODEC_ID_NONE) { + video_st = add_stream(oc, &video_codec, fmt->video_codec); + } + + /* Now that all the parameters are set, we can open the audio and + * video codecs and allocate the necessary encode buffers. */ + if (video_st) + open_video(oc, video_codec, video_st); + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); + if (ret < 0) { + fprintf(stderr, "Could not open '%s': %s\n", filename, + av_err2str(ret)); + return 1; + } + } + + /* Write the stream header, if any. */ + ret = avformat_write_header(oc, NULL); + if (ret < 0) { + fprintf(stderr, "Error occurred when opening output file: %s\n", + av_err2str(ret)); + return 1; + } + + if (frame) + frame->pts = 0; + for (int i = 0; i < 125; i++) { + write_video_frame(oc, video_st); + frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base); + } + + /* Write the trailer, if any. The trailer must be written before you + * close the CodecContexts open when you wrote the header; otherwise + * av_write_trailer() may try to use memory that was freed on + * av_codec_close(). */ + av_write_trailer(oc); + + /* Close each codec. */ + if (video_st) + close_video(oc, video_st); + + if (!(fmt->flags & AVFMT_NOFILE)) + /* Close the output file. */ + avio_close(oc->pb); + + /* free the stream */ + avformat_free_context(oc); + + return 0; +} diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp index e0f8343..ed892b8 100644 --- a/test/src/test-url.cpp +++ b/test/src/test-url.cpp @@ -47,7 +47,7 @@ int main(int argc, char** argv) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif - + std::string exeName = argv[0]; exeName = exeName.substr(exeName.find_last_of("\\/") + 1); -- cgit v0.12